2011-06-05 23 views
6

¿Podría alguien dar algunas sugerencias sobre cómo puedo disminuir lo siguiente para el tiempo de ejecución del ciclo mediante multihilo? Supongamos que también tengo dos vectores llamados 'a' y 'b'.Suma paralela para vectores

for (int j = 0; j < 8000; j++){ 
    // Perform an operation and store in the vector 'a' 
    // Add 'a' to 'b' coefficient wise 
} 

Este bucle for se ejecuta muchas veces en mi programa. Las dos operaciones en el ciclo de arriba para arriba ya están optimizadas, pero solo se ejecutan en un núcleo. Sin embargo, tengo 16 núcleos disponibles y me gustaría utilizarlos.

He intentado modificar el ciclo de la siguiente manera. En lugar de tener el vector 'a', tengo 16 vectores, y supongo que el i-ésimo se llama un [i]. Mi bucle ahora se ve como

for (int j = 0; j < 500; j++){ 
    for (int i = 0; i < 16; i++){ 
     // Perform an operation and store in the vector 'a[i]' 
    } 
    for (int i = 0; i < 16; i++){ 
     // Add 'a[i]' to 'b' coefficient wise 
    } 

} 

utilizo el OpenMP en cada uno de los bucles en el interior mediante la adición de 'omp paralelo para #pragma' antes de cada uno de los bucles internos. Todos mis procesadores están en uso, pero mi tiempo de ejecución solo aumenta significativamente. ¿Alguien tiene alguna sugerencia sobre cómo puedo disminuir el tiempo de ejecución de este ciclo? Gracias de antemano.

+0

¿Ha perfilado su código para ver dónde están los cuellos de botella? – GWW

+0

puede ser porque quizás después de optimizar tu código no se puede dividir en fragmentos más pequeños, si tu original solo estaba haciendo algo como 'a [i] + = b [i]' entonces puedes agregar esa etiqueta pragma justo antes de eso para. aumentará tu rendimiento como quisieras. – Ali1S232

+0

Si el cuerpo de su bucle es realmente tan trivial, entonces probablemente esté limitado por el ancho de banda de su memoria, y más núcleos no ayudarán (porque el ancho de banda de la memoria ya está saturado). Vuelva a organizar a un nivel más alto para encontrar más trabajo que hacer dentro del bucle, o consiga una máquina con RAM más rápida. – Nemo

Respuesta

5

omp crea subprocesos para su programa dondequiera que inserte la etiqueta pragma, por lo que crea subprocesos para etiquetas internas pero el problema es que se crean 16 subprocesos, cada uno realiza 1 operación y luego todos se destruyen con su método. crear y destruir subprocesos lleva mucho tiempo, por lo que el método que usaste aumenta el tiempo general de tu proceso aunque usa los 16 núcleos. no tenía que crear fors internos simplemente coloque la etiqueta #pragma omp parallel for antes de que su bucle 8000 sea omp para separar los valores entre las huellas, por lo que lo que hizo para crear el segundo bucle es el trabajo de omp. de esa manera omp crear hilos sólo una vez y luego procesar 500 números useing que cada hilo y el fin de ellos después de que (usando 499 menos la creación del hilo y destrucción)

+0

Esta es la primera vez que uso OpenMp, pero hago 'omp_set_num_threads (16)' en mi método principal. ¿No actúa como un threadpool y no destruye ningún hilo? –

+0

hilos son como funciones, no puede agregar o eliminar ninguna operación en su lista. solo piense en la función de creación de subprocesos como algo que obtiene un puntero para funcionar como una entrada. 'omp_set_num_threads' por cierto es algo opcional, si no lo pones allí omp elige el número de subprocesos que debe crear para optimizar tu código, configuración que solo fuerza a omp a usar tu cantidad de uso especificada. – Ali1S232

+0

la [página wiki para omp] (http://en.wikipedia.org/wiki/OpenMP) contiene casi todos los datos necesarios para crear un programa simple de omp, solo léalo antes de cualquier depuración que vaya a hacer. – Ali1S232

0

Does anyone have any suggestions on how I can decrease the runtime of this loop?

for (int j = 0; j < 500; j++){ // outer loop 
    for (int i = 0; i < 16; i++){ // inner loop 

Siempre trate de hacer exterior iteraciones de bucle menores que bucle interno. Esto le ahorrará desde inicializaciones de bucle interno que muchas veces. En el código anterior, el bucle interno i = 0; se inicializa 500 veces. Ahora,

for (int i = 0; j < 16; i++){ // outer loop 
    for (int j = 0; j < 500; j++){ // inner loop 

Ahora, j = 0; bucle interno se inicializa sólo 16 veces! Pruebe modificando su código en consecuencia, si tiene algún impacto.

+3

En una CPU moderna, este consejo es exactamente al revés. Es más probable que una secuencia de bucles internos _pequeños_ opere sobre datos en la caché L1, que es 100-1000 veces más importante que la sobrecarga de inicializar un contador de bucles. – Nemo

+0

@Nemo, gracias, no lo sabía. Sin embargo, ¿aquí probablemente se refería a * loop más pequeño * que a * inner * loop correcto? Eso dependerá del tamaño de la caché también. Si el tamaño de la memoria caché es realmente capaz de acomodar * loop * más grande entonces, la lógica sobre aún se sostiene. – iammilind

+0

Sí. La clave es ser amigable con el caché, y un caché L1 típico es algo así como 32K en estos días. Así que quiere asegurarse de que sus circuitos internos toquen menos datos que ese ... – Nemo

3

En realidad, voy a poner estos comentarios en una respuesta.

Hilos de bifurcación para operaciones triviales simplemente agrega sobrecarga.

En primer lugar, asegúrese de que el compilador está utilizando instrucciones vectoriales para implementar el bucle. (Si no sabe cómo hacer esto, es posible que tenga código con instrucciones vectoriales usted mismo, tratar de buscar "instrinsics ESS" Pero para este tipo de simple adición de vectores, vectorización automática debería ser posible..)

Asumiendo que su compilador GCC es una bastante moderna, con lo invocan:

gcc -O3 -march=native ... 

Añadir -ftree-vectorizer-verbose=2 para averiguar si es o no de auto-vectorizado su bucle y por qué.

Si ya está usando instrucciones vectoriales, entonces es posible que esté saturando el ancho de banda de su memoria. Los núcleos de CPU modernos son bastante rápidos ... Si es así, necesitas reestructurar a un nivel más alto para obtener más operaciones dentro de cada iteración del ciclo, encontrando formas de realizar muchas operaciones en bloques que se ajustan dentro de la caché L1.

+0

Gracias por su comentario. Tengo SSE2 habilitado y estoy usando el compilador de microsoft con el IDE de Visual Studio 2010. Tomaré esto en consideración y veré si puedo reestructurar cualquier otra cosa. –

+0

Sugiero correr con -S para mirar el conjunto y asegurarse de que está usando las instrucciones de SSE2. – Nemo