c++ c performance openmp simd

c++ - Paralelo para vs omp simd: cuando usar cada uno?



performance openmp (3)

OpenMP 4.0 presenta una nueva construcción llamada "omp simd". ¿Cuál es el beneficio de usar esta construcción sobre el antiguo "paralelo para"? ¿Cuándo sería cada uno una mejor opción que el otro?

EDITAR: Aquí hay un paper interesante relacionado con la directiva SIMD.


El estándar vinculado es relativamente claro (p 13, líneas 19 + 20)

Cuando un hilo encuentra un constructo simd, las iteraciones del ciclo asociado con el constructo pueden ejecutarse mediante los carriles SIMD que están disponibles para el hilo.

SIMD es una cosa sub-hilo. Para hacerlo más concreto, en una CPU podría imaginarse el uso de directivas simd para solicitar específicamente la vectorización de fragmentos de iteraciones de ciclo que pertenecen individualmente al mismo hilo . Está exponiendo los múltiples niveles de paralelismo que existen dentro de un solo procesador multinúcleo, de una manera independiente de la plataforma. Vea, por ejemplo, la discusión (junto con las cosas del acelerador) en esta publicación de blog de Intel .

Entonces, básicamente, querrá usar omp parallel para distribuir el trabajo en diferentes subprocesos, que luego pueden migrar a múltiples núcleos; y querrá usar omp simd para hacer uso de tuberías de vector (por ejemplo) dentro de cada núcleo. Normalmente omp parallel iría en el "exterior" para tratar con una distribución paralela de trabajo más omp simd y omp simd circuitos estrechos para explotar el paralelismo de grano fino.


Los compiladores no están obligados a hacer que la optimización simd en una región paralela esté condicionada a la presencia de la cláusula simd. Los compiladores con los que estoy familiarizado siguen siendo compatibles con bucles anidados, paralelos, externos e internos, de la misma manera que antes.
En el pasado, las directivas OpenMP generalmente se tomaban para evitar optimizaciones de conmutación de bucle que implicaban el bucle paralelizado externo (bucles múltiples con cláusula de colapso). Esto parece haber cambiado en unos pocos compiladores. OpenMP 4 abre nuevas posibilidades, incluida la optimización de un bucle externo paralelo con un bucle interno no vectorializable, mediante una especie de extracción de banda, cuando se establece omp parallel do [for] simd. ifort a veces lo informa como vectorización de bucle externo cuando se realiza sin la cláusula simd. Luego se puede optimizar para un número menor de hilos que el omp parallel do simd, que parece necesitar más hilos que el ancho del vector simd para pagar. Tal distinción podría inferirse, ya que, sin la cláusula simd, se le pide implícitamente al compilador que optimice para un recuento de bucles como 100 o 300, mientras que la cláusula simd solicita la optimización simd incondicional. gcc 4.9 omp parallel para simd parecía bastante efectivo cuando tenía una plataforma de 24 núcleos.


Una respuesta simple:

OpenMP solo se usa para explotar múltiples hilos para múltiples núcleos. Esta nueva extensión simd permite utilizar explícitamente instrucciones SIMD en CPU modernas, como AVX / SSE de Intel y NEON de ARM.

(Tenga en cuenta que una instrucción SIMD se ejecuta en un único subproceso y en un solo núcleo, por diseño. Sin embargo, el significado de SIMD puede ser bastante amplio para GPGPU. Pero, no creo que deba considerar GPGPU para OpenMP 4.0. )

Entonces, una vez que conozca las instrucciones SIMD, puede usar esta nueva construcción.

En una CPU moderna, existen aproximadamente tres tipos de paralelismo: (1) paralelismo a nivel de instrucción (ILP), (2) paralelismo a nivel de subprocesos (TLP) y (3) instrucciones SIMD (podríamos decir que esto es a nivel de vector) más o menos).

ILP se realiza automáticamente por sus CPU o compiladores fuera de orden. Puede explotar TLP utilizando el parallel for de OpenMP y otras bibliotecas de subprocesos. Entonces, ¿qué hay de SIMD? Los intrínsecos fueron una forma de usarlos (así como la vectorización automática de los compiladores). La simd de OpenMP es una nueva forma de usar SIMD.

Tome un ejemplo muy simple:

for (int i = 0; i < N; ++i) A[i] = B[i] + C[i];

El código anterior calcula una suma de dos vectores N-dimensionales. Como puede ver fácilmente, no existe una dependencia de datos (transportada por bucle) en la matriz A[] . Este ciclo es embarazosamente paralelo .

Podría haber múltiples formas de paralelizar este ciclo. Por ejemplo, hasta OpenMP 4.0, esto se puede paralelizar utilizando solo parallel for constructo. Cada hilo realizará N/#thread iteraciones de N/#thread en múltiples núcleos.

Sin embargo, puede pensar que usar múltiples hilos para una adición tan simple sería una exageración. Es por eso que hay vectorización, que se implementa principalmente mediante instrucciones SIMD.

Usar un SIMD sería así:

for (int i = 0; i < N/8; ++i) VECTOR_ADD(A + i, B + i, C + i);

Este código asume que (1) la instrucción SIMD ( VECTOR_ADD ) es de 256 bits u 8 vías (8 * 32 bits); y (2) N es un múltiplo de 8.

Una instrucción SIMD de 8 vías significa que se pueden ejecutar 8 elementos en un vector en una sola instrucción de máquina. Tenga en cuenta que el último AVX de Intel proporciona instrucciones vectoriales de 8 direcciones (32 bits * 8 = 256 bits).

En SIMD, todavía usa un solo núcleo (de nuevo, esto es solo para CPU convencionales, no para GPU). Pero puedes usar un paralelismo oculto en el hardware. Las CPU modernas dedican recursos de hardware para las instrucciones SIMD, donde cada carril SIMD se puede ejecutar en paralelo.

Puede usar el paralelismo de nivel de subproceso al mismo tiempo. El ejemplo anterior se puede paralelizar más parallel for .

(Sin embargo, tengo dudas de cuántos bucles se pueden transformar realmente en bucles SIMDized. La especificación OpenMP 4.0 parece poco clara al respecto. Por lo tanto, el rendimiento real y las restricciones prácticas dependerían de las implementaciones reales de los compiladores).

Para resumir, simd construct le permite usar instrucciones SIMD, a su vez, se puede aprovechar más paralelismo junto con el paralelismo de nivel de subprocesos. Sin embargo, creo que las implementaciones reales serían importantes.