Pregunta

Me gustaría hacer una pregunta y luego continuar con mi propia respuesta, pero también ver qué respuestas tienen otras personas.

Tenemos dos archivos grandes que nos gustaría leer de dos hilos separados al mismo tiempo.Un hilo leerá secuencialmente el archivo A mientras que el otro hilo leerá secuencialmente el archivo B.No hay bloqueo ni comunicación entre los subprocesos, ambos leen secuencialmente lo más rápido que pueden y ambos descartan inmediatamente los datos que leen.

Nuestra experiencia con esta configuración en Windows es muy pobre.El rendimiento combinado de los dos subprocesos es del orden de 2-3 MiB/seg.La unidad parece pasar la mayor parte del tiempo buscando hacia adelante y hacia atrás entre los dos archivos, presumiblemente leyendo muy poco después de cada búsqueda.

Si deshabilitamos uno de los subprocesos y observamos temporalmente el rendimiento de un solo subproceso, obtenemos un ancho de banda mucho mejor (~45 MiB/seg para esta máquina).Claramente, el mal rendimiento de dos subprocesos es un artefacto del programador de disco del sistema operativo.

¿Hay algo que podamos hacer para mejorar el rendimiento de lectura de subprocesos simultáneos? Quizás usando diferentes API o modificando los parámetros del programador del disco del sistema operativo de alguna manera.

Algunos detalles:

Los archivos son del orden de 2 GiB cada uno en una máquina con 2 GiB de RAM.A los efectos de esta pregunta, consideramos que no están almacenados en caché y que están perfectamente desfragmentados.Utilizamos herramientas de desfragmentación y reiniciamos para garantizar que este sea el caso.

No utilizamos API especiales para leer estos archivos.El comportamiento se puede repetir en varias API estándar como CreateFile de Win32, fopen de C, std::ifstream de C++, FileInputStream de Java, etc.

Cada hilo gira en un bucle realizando llamadas a la función de lectura.Hemos variado la cantidad de bytes solicitados de la API en cada iteración desde valores entre 1 KB hasta 128 MiB.Variar esto no ha tenido ningún efecto, por lo que claramente la cantidad que el sistema operativo lee físicamente después de cada búsqueda de disco no está dictada por este número.Esto es exactamente lo que debería esperarse.

La dramática diferencia entre el rendimiento de uno y dos subprocesos se puede repetir en Windows 2000, Windows XP (32 bits y 64 bits), Windows Server 2003 y también con y sin hardware RAID5.

¿Fue útil?

Solución

El problema parece estar en la política de programación de E/S de Windows.Según lo que encontré aquí Hay muchas maneras para que un O.S.para programar solicitudes de disco.Mientras que Linux y otros pueden elegir entre diferentes políticas, antes de Vista Windows estaba bloqueado en una sola política:una cola FIFO, donde todas las solicitudes se dividieron en bloques de 64 KB.Creo que esta política es la causa del problema que estás experimentando:el programador combinará solicitudes de los dos subprocesos, provocando una búsqueda continua entre diferentes áreas del disco.
Ahora bien, la buena noticia es que según aquí y aquí, Vista introdujo un programador de disco más inteligente, donde puede establecer la prioridad de sus solicitudes y también asignar un ancho mínimo para su proceso.
La mala noticia es que no encontré forma de cambiar la política de disco o el tamaño de los buffers en versiones anteriores de Windows.Además, incluso si aumentar la prioridad de E/S del disco de su proceso aumentará el rendimiento frente a los otros procesos, aún tendrá el problema de que sus subprocesos compitan entre sí.
Lo que puedo sugerir es modificar su software introduciendo una política de acceso al disco hecha por usted mismo.
Por ejemplo, podrías usar una política como esta en tu hilo B (similar para el hilo A):

if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again  

Podría usar semáforos para verificar el estado o podría usar contadores de rendimiento para obtener el estado de la cola de disco real.Los valores de X y/o Y también se pueden ajustar automáticamente comprobando las tasas de transferencia reales y modificándolas lentamente, maximizando así el rendimiento cuando la aplicación se ejecuta en diferentes máquinas y/o sistemas operativos.Es posible que descubra que los niveles de caché, memoria o RAID los afectan de una forma u otra, pero con el ajuste automático siempre obtendrá el mejor rendimiento en cada escenario.

Otros consejos

Me gustaría agregar algunas notas adicionales en mi respuesta.Todos los demás sistemas operativos que no son de Microsoft que hemos probado no sufren este problema.Linux, FreeBSD y Mac OS X (este último en hardware diferente) se degradan mucho más suavemente en términos de ancho de banda agregado cuando se pasa de un hilo a dos.Linux, por ejemplo, se degradó de ~45 MiB/seg a ~42 MiB/seg.Estos otros sistemas operativos deben leer fragmentos más grandes del archivo entre cada búsqueda y, por lo tanto, no perder casi todo el tiempo esperando en el disco para buscar.

Nuestra solución para Windows es pasar el FILE_FLAG_NO_BUFFERING bandera a CreateFile y utilice lecturas grandes (~16MiB) en cada llamada para ReadFile.Esto no es óptimo por varias razones:

  • Los archivos no se almacenan en caché cuando se leen de esta manera, por lo que no existen ninguna de las ventajas que normalmente ofrece el almacenamiento en caché.
  • Las restricciones al trabajar con este indicador son mucho más complicadas que las de la lectura normal (alineación de los buffers de lectura con los límites de la página, etc.).

(Como observación final.¿Explica esto por qué el intercambio en Windows es tan infernal?Es decir, Windows es incapaz de realizar IO en varios archivos al mismo tiempo con cierta eficiencia, por lo que, al intercambiar, todas las demás operaciones de IO se ven obligadas a ser desproporcionadamente lentas).


Edite para agregar más detalles sobre Will Dean:

Por supuesto, en estas diferentes configuraciones de hardware, las cifras brutas cambiaron (a veces sustancialmente).Sin embargo, el problema es la constante degradación del rendimiento que sólo sufre Windows al pasar de un subproceso a dos.Aquí hay un resumen de las máquinas probadas:

  • Varias estaciones de trabajo Dell (Intel Xeon) de distintas edades que ejecutan Windows 2000, Windows XP (32 bits) y Windows XP (64 bits) con una sola unidad.
  • Un servidor Dell 1U (Intel Xeon) que ejecuta Windows Server 2003 (64 bits) con RAID 1+0.
  • Una estación de trabajo HP (AMD Opteron) con Windows XP (64 bits) y Windows Server 2003, y hardware RAID 5.
  • Mi PC doméstica sin marca (AMD Athlon64) con Windows XP (32 bits), FreeBSD (64 bits) y Linux (64 bits) con una sola unidad.
  • La MacBook de mi casa (Intel Core1) con Mac OS X, unidad SATA única.
  • Mi hogar Koolu PC con Linux.Tiene mucha poca potencia en comparación con los otros sistemas, pero demostré que incluso esta máquina puede superar a un servidor Windows con RAID5 cuando realiza lecturas de disco de subprocesos múltiples.

El uso de CPU en todos estos sistemas fue muy bajo durante las pruebas y el antivirus estaba desactivado.

Olvidé mencionarlo antes, pero también probamos el Win32 normal. CreateFile API con el FILE_FLAG_SEQUENTIAL_SCAN conjunto de banderas.Esta bandera no solucionó el problema.

Parece un poco extraño que no se vea ninguna diferencia entre una amplia gama de versiones de Windows y nada entre una sola unidad y el hardware raid-5.

Es sólo una "intuición", pero eso me hace dudar de que se trate realmente de un simple problema de búsqueda.Aparte de OS X y Raid5, ¿se probó todo esto en la misma máquina? ¿Has probado en otra máquina?¿El uso de su CPU es básicamente cero durante esta prueba?

¿Cuál es la aplicación más corta que puedes escribir y que demuestra este problema?- Me interesaría probarlo aquí.

Crearía algún tipo de bloqueo seguro para subprocesos en la memoria.Cada hilo podría esperar en el candado hasta que estuviera libre.Cuando el bloqueo se libere, tome el bloqueo y lea el archivo durante un período de tiempo definido o una cantidad definida de datos, luego libere el bloqueo para cualquier otro subproceso en espera.

Lo usas Puertos de finalización de IO bajo Windows?Windows a través de C++ tiene un capítulo detallado sobre este tema y, por suerte, también está disponible en MSDN.

Paul: vio la actualización.Muy interesante.

Sería interesante probarlo en Vista o Win2008, ya que la gente parece estar reportando mejoras considerables de E/S en algunas circunstancias.

Mi única sugerencia sobre una API diferente sería intentar mapear la memoria de los archivos. ¿Lo has probado?Desafortunadamente, con 2 GB por archivo, no podrás mapear varios archivos completos en una máquina de 32 bits, lo que significa que esto no es tan trivial como podría ser.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top