2011-01-29 8 views
15

Supongamos que tiene una matriz de valor de función enorme (40+ GB) (coma flotante), las filas son características diferentes y las columnas son las muestras/imágenes.cómo hacer un mapa de la memoria de una gran matriz?

La tabla está precalculada en columnas. Luego se accede completamente a filas y a múltiples subprocesos (cada subproceso carga una fila completa) varias veces.

¿Cuál sería la mejor forma de manejar esta matriz? Estoy especialmente ponderando más de 5 puntos:

  1. Dado que se ejecuta en una PC x64, ¿podría la memoria mapear toda la matriz de una vez, pero tendría sentido?
  2. ¿Qué pasa con los efectos del multihilo (también computación inicial multiproceso?)?
  3. Cómo maquetar la matriz: fila o columna principal?
  4. ¿Ayudaría marcar la matriz como de solo lectura después de que se haya terminado la precomputación?
  5. ¿Se podría usar algo como http://www.kernel.org/doc/man-pages/online/pages/man2/madvise.2.html para acelerarlo?
+0

Esta pregunta podría cerrarse por * demasiado interesante * para SO - pero espero que no. ¿Hay alguna restricción en el sistema operativo? (Adivinando Linux desde el enlace.) –

+0

No entiendo por qué podría cerrarse, ¿hay alguna regla que haya olvidado? Sí, el software está actualmente restringido a Linux. Pero las respuestas con respecto a Windows también son bienvenidas. – Trass3r

Respuesta

5

Mapeo de memoria todo el archivo podría hacer que el proceso sea mucho más fácil.

Desea diseñar sus datos para optimizarlos para el patrón de acceso más común. Parece que los datos se escribirán una vez (en columnas) y se leerán varias veces (en filas). Eso sugiere que los datos deben almacenarse en orden mayor de fila.

Marcar la matriz de solo lectura una vez que se realiza el cálculo previo probablemente no ayudará al rendimiento (hay algunas posibles optimizaciones de bajo nivel, pero no creo que nada las implemente), pero evitará errores de escribir accidentalmente en datos que no tiene la intención de. Podría también.

madvise podría terminar siendo útil, una vez que tenga su aplicación escrita y en funcionamiento.

Mi consejo general: escriba el programa de la manera más simple que pueda, secuencialmente al principio, y luego ponga temporizadores alrededor de todo y de las diversas operaciones principales. Asegúrese de que los tiempos de operación principales se suman al tiempo total, para que pueda estar seguro de que no le falta nada. Luego, dirija sus esfuerzos de mejora del rendimiento hacia los componentes que en realidad están tomando más tiempo.

Por la mención de JimR de páginas de 4MB en su comentario, puede terminar queriendo buscar en hugetlbfs o usar una versión de Kernel de Linux con soporte de página enorme transparente (fusionado para 2.6.38, probablemente podría ser parcheado en versiones anteriores). Esto probablemente le ahorrará una gran cantidad de errores de TLB, y convencerá al kernel de hacer el disco IO en trozos suficientemente grandes para amortizar cualquier sobrecarga de búsqueda.

+1

Si no accedes a la memoria correctamente, podrías terminar en un festival de thrash. Asegúrese de medir las fallas de página dentro/fuera si lo encuentra lento. zvrba cubre algunos de los problemas que verás en su respuesta, particularmente el # 3. Trabajé en algo similar a principios de los 90 (200 a 1G) y la paliza de las fallas dentro y fuera lo arruinó por completo. Esto fue en un momento en que 64MB de RAM se consideraban maximizados.Puede reducir la vibración (al reducir la sobrecarga) si puede cambiar el tamaño de la página de 4096 a, creo, 4MB. – JimR

+0

A> 40 Gb, creo que podemos suponer que es demasiado grande para la memoria principal. Entonces una implementación ingenua (como se sugiere aquí) conducirá a un "festival de thrash". –

+0

Es posible que esté en mal estado, pero tengo acceso a máquinas con más RAM que eso. De todos modos, a menos que la fase de cálculo sea realmente pesada, solo leer los datos secuencialmente llevará tanto tiempo como el resto del programa. La implementación sensata y "ingenua" leería los datos secuencialmente, y así obtendría esencialmente un rendimiento completo en ese límite. – Novelocrat

3
  1. Quizás, vea a continuación.
  2. El tamaño del conjunto de trabajo total de todos los subprocesos no debe exceder la RAM disponible, de lo contrario, el programa se ejecutará a velocidad de caracol debido al intercambio.
  3. El diseño debe coincidir con los patrones de acceso, siempre que se respete la condición 2.
  4. ¿Qué quiere decir con "marcar como de solo lectura"?
  5. Mídelo.

Re 3: Si tiene, por ejemplo,, 8 CPU pero no tienen suficiente RAM para cargar 8 filas, debe hacer que cada subproceso procese su fila secuencialmente en fragmentos manejables. En este caso, el diseño de bloques de una matriz tendría sentido. Si el hilo DEBE tener toda la fila en la memoria para procesarlo, me temo que no puede usar todas las CPU, ya que el proceso comenzará a agitarse, es decir, expulsar algún subconjunto de la matriz del carnero y volver a cargarla. otro subconjunto necesario. Esto es ligeramente menos malo que el intercambio completo ya que la matriz nunca se modifica, por lo que el contenido de las páginas no necesita escribirse en el archivo de intercambio antes de ser expulsado. Pero todavía lastima el rendimiento mal.

También, hacer E/S de acceso aleatorio desde múltiples hilos es una mala idea, que es lo que terminarás haciendo si usas mmap(). Tiene (presumiblemente) solo un disco, y las E/S paralelas lo harán más lento. Así que mmap() podría no tener sentido y usted podría lograr un mejor rendimiento de E/S leyendo los datos secuencialmente en RAM.

Tenga en cuenta que 40 GB son aproximadamente 10,5 millones de páginas de 4096 bytes. Al hacer mmap(), en el peor de los casos, ralentizará el cálculo por la cantidad de búsquedas de disco duro. Con 8ms por búsqueda (tomada de wikipedia), terminarás desperdiciando 83666 segundos, es decir, ¡casi un día entero!

+0

Bueno, una sola fila es del orden de unos pocos MB más tengo 12GB de RAM, así que ese no es el problema. – Trass3r

+0

Ok. Pero mmapping aún generará muchas E/S aleatorias. – zvrba

2

Si pudiera encajar todo en la memoria principal, entonces sí: la memoria lo mapea todo, y no importa si es la columna principal o la fila principal. Sin embargo, a más de 40 Gb, estoy seguro de que es demasiado grande para la memoria principal. En cuyo caso:

  1. No, ¡no lo asigne todo! Al menos, no espere que la memoria funcione como la memoria normal si lo mapea todo. Su programa tardará una eternidad si no se ocupa adecuadamente de los problemas de E/S.
  2. El problema del acceso de subprocesos múltiples se resuelve si lo almacena en filas principales (parece que no tiene escrituras de columnas con múltiples subprocesos).
  3. Debe disponerlo en filas, suponiendo que cada celda se escribe una vez y luego se lee muchas veces.
  4. Sí, creo que sería útil marcar la matriz como de solo lectura después de que se haya escrito, pero únicamente como una forma de evitar errores (escrituras accidentales). No afectará el rendimiento.
  5. No, ninguna cantidad de lectura inteligente del kernel va a resolver sus problemas de rendimiento. Necesitas resolverlo a nivel de algoritmo.

Creo que va a haber un problema de rendimiento con una implementación ingenua. O bien la computadora con Thrash al escribir (si la almacena fila principal) o se agrietará durante la consulta (si la almacena en la columna principal). Este último es probablemente peor, pero es un problema en ambos sentidos.

La solución correcta es utilizar una representación intermedia que no sea fila mayor ni columna mayor sino cuadrados grandes. Tome las primeras 50,000 columnas y guárdelas en un archivo mapeado en memoria (fase 1). No importa si es la columna principal o la fila principal, ya que será pura memoria residente. Luego, tome cada fila y escríbala en el último archivo mapeado en memoria de la fila final (fase 2). Luego repite el ciclo para las siguientes 50,000 columnas, y así sucesivamente.

Cuestiones relacionadas