¿Qué sucede detrás de las cortinas durante la E / S del disco?

Cuando busco alguna posición en un archivo y escribo una pequeña cantidad de datos (20 bytes), ¿qué sucede tras bambalinas?

Mi punto de vista

Que yo sepa, la unidad de datos más pequeña que se puede escribir o leer desde un disco es un sector (tradicionalmente, 512 bytes, pero ese estándar ahora está cambiando). Eso significa que para escribir 20 bytes, necesito leer todo un sector, modificar parte de él en la memoria y grabarlo en el disco.

Esto es lo que espero que suceda en E / S sin búfer. También espero que la E / S en búfer haga aproximadamente lo mismo, pero sé inteligente acerca de su caché. Por lo tanto, habría pensado que si destruyo la localidad por la ventana haciendo búsquedas y escrituras aleatorias, tanto la E / S con búfer como la que no tiene búfer deberían tener un rendimiento similar ... tal vez con un búffer un poco mejor.

Entonces, una vez más, sé que es una locura que la E / S en búfer solo almacene en búfer un sector, por lo que también podría esperar que se desempeñe terriblemente.

Mi aplicación

Estoy almacenando valores recopilados por un controlador de dispositivo SCADA que recibe telemetría remota por más de cien mil puntos. Hay datos adicionales en el archivo, de modo que cada registro es de 40 bytes, pero solo se deben escribir 20 bytes durante una actualización.

Pre-implementación de referencia

Para comprobar que no necesito soñar con una solución brillantemente diseñada en exceso, he realizado una prueba utilizando unos pocos millones de registros aleatorios escritos en un archivo que podría contener un total de 200,000 registros. Cada prueba siembra el generador de números aleatorios con el mismo valor para ser justo. Primero borro el archivo y lo coloco a la longitud total (aproximadamente 7.6 megas), luego recorro unos millones de veces, pasando un desplazamiento aleatorio del archivo y algunos datos a una de las dos funciones de prueba:

void WriteOldSchool( void *context, long offset, Data *data )
{
    int fd = (int)context;
    lseek( fd, offset, SEEK_SET );
    write( fd, (void*)data, sizeof(Data) );
}

void WriteStandard( void *context, long offset, Data *data )
{
    FILE *fp = (FILE*)context;
    fseek( fp, offset, SEEK_SET );
    fwrite( (void*)data, sizeof(Data), 1, fp );
    fflush(fp);
}

Tal vez no hay sorpresas?

losOldSchool Método salió en la parte superior - por mucho. Fue más de 6 veces más rápido (1.48 millones versus 232000 registros por segundo). Para asegurarme de que no había encontrado el almacenamiento en caché de hardware, amplié el tamaño de mi base de datos a 20 millones de registros (tamaño de archivo de 763 megas) y obtuve los mismos resultados.

Antes de señalar la llamada obvia afflush, déjame decir que eliminarlo no tuvo efecto. Me imagino que esto se debe a que el caché debe estar comprometido cuando busco lo suficientemente lejos, que es lo que hago la mayor parte del tiempo.

Entonces, ¿qué está pasando?

Me parece que la E / S en búfer debe estar leyendo (y posiblemente escribiendo todo) una gran parte del archivo cada vez que intento escribir. Debido a que casi nunca aprovecho su caché, esto es extremadamente inútil.

Además (y no conozco los detalles del almacenamiento en caché de hardware en el disco), si la E / S en búfer está intentando escribir un grupo de sectores cuando cambio solo uno, se reduciría la eficacia de la caché de hardware.

¿Hay expertos en discos por ahí que puedan comentar y explicar esto mejor que mis hallazgos experimentales? =)

Respuestas a la pregunta(2)

Su respuesta a la pregunta