He estado escribiendo un raytracer la semana pasada, y he llegado a un punto en el que está haciendo suficiente para que el multi-threading tenga sentido. He intentado usar OpenMP para paralelizarlo, pero ejecutarlo con más hilos es en realidad más lento que ejecutarlo con uno.Dividir un programa en 4 hilos es más lento que un solo hilo
Leyendo sobre otras preguntas similares, especialmente sobre OpenMP, una sugerencia fue que gcc optimiza mejor el código de serie. Sin embargo, ejecutar el siguiente código compilado con export OMP_NUM_THREADS=1
es dos veces más rápido que con export OMP_NUM_THREADS=4
. Es decir. Es el mismo código compilado en ambas ejecuciones.
Ejecutar el programa con time
:
> export OMP_NUM_THREADS=1; time ./raytracer
real 0m34.344s
user 0m34.310s
sys 0m0.008s
> export OMP_NUM_THREADS=4; time ./raytracer
real 0m53.189s
user 0m20.677s
sys 0m0.096s
tiempo del usuario es mucho más pequeño que real, lo cual es inusual cuando se utilizan múltiples cores- usuario debe ser mayor que como varios núcleos son reales corriendo al mismo tiempo.
Código que he parallelized usando OpenMP
void Raytracer::render(Camera& cam) {
// let the camera know to use this raytracer for probing the scene
cam.setSamplingFunc(getSamplingFunction());
int i, j;
#pragma omp parallel private(i, j)
{
// Construct a ray for each pixel.
#pragma omp for schedule(dynamic, 4)
for (i = 0; i < cam.height(); ++i) {
for (j = 0; j < cam.width(); ++j) {
cam.computePixel(i, j);
}
}
}
}
Al leer this question pensé que había encontrado mi respuesta. Habla sobre la implementación de gclib rand() sincronización de llamadas a sí mismo para preservar el estado para la generación de números aleatorios entre hilos. Estoy usando rand() bastante para el muestreo de monte carlo, así que pensé que ese era el problema. Me deshice de las llamadas a rand, reemplazándolas por un único valor, pero el uso de múltiples hilos es aún más lento. EDITAR: ¡oops resulta que no lo probé correctamente, fueron los valores aleatorios!
Ahora que están fuera del camino, discutiré un resumen de lo que se está haciendo en cada llamada al computePixel
, así que con suerte se puede encontrar una solución.
En mi raytracer, esencialmente tengo un árbol de escenas, con todos los objetos en él. Este árbol se recorre mucho durante computePixel
cuando se prueba la intersección de objetos, sin embargo, no se realizan escrituras en este árbol ni en ningún objeto. computePixel
esencialmente lee la escena un montón de veces, llamando a métodos en los objetos (todos ellos son métodos const), y al final escribe un único valor en su propia matriz de píxeles. Esta es la única parte que conozco en la que más de un subproceso intentará escribir en la misma variable miembro. No hay sincronización en ninguna parte, ya que no hay dos hilos que puedan escribir en la misma celda en la matriz de píxeles.
¿Alguien puede sugerir lugares donde podría haber algún tipo de disputa? Cosas que probar?
Gracias de antemano.
EDITAR: Disculpa, fue estúpido no proporcionar más información sobre mi sistema.
- compilador gcc 4.6 (con optimización -O2)
- Ubuntu Linux 11.10
- OpenMP 3
- Intel i3-2310M Quad núcleo 2.1Ghz (en mi portátil en el momento) Código
por píxel de cómputo:
class Camera {
// constructors destructors
private:
// this is the array that is being written to, but not read from.
Colour* _sensor; // allocated using new at construction.
}
void Camera::computePixel(int i, int j) const {
Colour col;
// simple code to construct appropriate ray for the pixel
Ray3D ray(/* params */);
col += _sceneSamplingFunc(ray); // calls a const method that traverses scene.
_sensor[i*_scrWidth+j] += col;
}
De las sugerencias, que podría ser el recorrido del árbol que provoca la ralentización. Algunos otros aspectos: hay una gran cantidad de recurrencia involucrada una vez que se llama a la función de muestreo (rebote recursivo de los rayos). ¿Podría esto causar estos problemas?
Pregunta tonta: está ejecutando un multiprocesador y está ejecutando una versión SMP del sistema operativo, ¿correcto? P: ¿Cuáles son las CPU? P: ¿Cuál es la versión de OS/OS? P: ¿Compilador (gcc?)/Versión del compilador? P: ¿Open MP versión? – paulsm4
Dado que está calculando valores de píxeles, ¿ha considerado la programación de GPGPU? –
Intel tiene algunas buenas herramientas para analizar el rendimiento del hilo, tal vez esas pueden darle pistas –