Pregunta

Tengo algo de código en un bucle

for(int i = 0; i < n; i++)
{
  u[i] = c * u[i] + s * b[i];
}

Así, U y b son vectores de la misma longitud, y c y s son escalares. ¿Es este código a un candidato bueno para la vectorización para su uso con SSE con el fin de conseguir un aumento de velocidad?

Actualizar

he aprendido vectorización (resulta que no es tan difícil si se utiliza intrínsecos) e implementado en mi bucle de SSE. Sin embargo, cuando se establecía la marca SSE2 en el compilador de VC ++, consigo sobre el mismo rendimiento que con mi propio código de SSE. El compilador de Intel, por otro lado era mucho más rápido que mi código SSE o VC ++ compilador.

Este es el código que escribí para referencia

double *u = (double*) _aligned_malloc(n * sizeof(double), 16);
for(int i = 0; i < n; i++)
{
   u[i] = 0;
}

int j = 0;
__m128d *uSSE = (__m128d*) u;
__m128d cStore = _mm_set1_pd(c);
__m128d sStore = _mm_set1_pd(s);
for (j = 0; j <= i - 2; j+=2)
{
  __m128d uStore = _mm_set_pd(u[j+1], u[j]);

  __m128d cu = _mm_mul_pd(cStore, uStore);
  __m128d so = _mm_mul_pd(sStore, omegaStore);

  uSSE[j/2] = _mm_add_pd(cu, so);
}
for(; j <= i; ++j)
{
  u[j] = c * u[j] + s * omegaCache[j];
}
¿Fue útil?

Solución

Sí, este es un excelente candidato para la vectorización. Pero, antes de hacerlo, asegúrese de que ha perfila el código para estar seguro de que este es en realidad un valor de optimización. Dicho esto, la vectorización sería algo como esto:

int i;
for(i = 0; i < n - 3; i += 4)
{
  load elements u[i,i+1,i+2,i+3]
  load elements b[i,i+1,i+2,i+3]
  vector multiply u * c
  vector multiply s * b
  add partial results
  store back to u[i,i+1,i+2,i+3]
}

// Finish up the uneven edge cases (or skip if you know n is a multiple of 4)
for( ; i < n; i++)
  u[i] = c * u[i] + s * b[i];

Para aún más el rendimiento, se puede considerar la obtención previa de otros elementos de la matriz, y / o desenrollar el bucle y el uso de software pipelining para intercalar el cálculo en un bucle con el accesos a memoria a partir de una iteración diferente.

Otros consejos

Probablemente sí, pero hay que compilador de ayuda con algunos consejos. __restrict__ colocado sobre los punteros dice compilador que no hay ningún alias entre los dos punteros. si sabe alineación de sus vectores, comunicar que al compilador (Visual C ++ puede tener alguna facilidad).

No estoy familiarizado con Visual C ++ a mí mismo, pero he oído que no es bueno para la vectorización. Considere el uso de Intel compilador lugar. Intel permite bastante el control preciso sobre generada montaje: http://www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cppref_pragma_vector.htm

_mm_set_pd no está vectorizada. Si se toma literalmente, lee los dos dobles usando operaciones escalares, entonces combina las dos dobles escalares y los copia en el registro SSE. Uso _mm_load_pd lugar.

Sí, esto es un gran candidato para vectorizaton, suponiendo que no hay superposición de U y B matriz. Sin embargo, el código está limitado por el acceso a memoria (carga / almacenamiento). Vectorización ayuda a reducir los ciclos por ciclo, pero las instrucciones se detendrá debido a la caché-miss en U y B matriz. El / C Compilador Intel C ++ genera el siguiente código con banderas por defecto para procesador x5500 Xeon. El compilador desenrolla el bucle por 8 y emplea SIMD ADD (addpd) e instrucciones multiplicar (mulpd) utilizando xmm [0-16] registros SIMD. En cada ciclo, el procesador puede emitir instrucciones SIMD 2 rendimiento de 4 vías escalar ILP, suponiendo que tiene los datos dispuestos en los registros.

Aquí U, B, C y S son de doble precisión (8 bytes).

    ..B1.14:                        # Preds ..B1.12 ..B1.10
    movaps    %xmm1, %xmm3                                  #5.1
    unpcklpd  %xmm3, %xmm3                                  #5.1
    movaps    %xmm0, %xmm2                                  #6.12
    unpcklpd  %xmm2, %xmm2                                  #6.12
      # LOE rax rcx rbx rbp rsi rdi r8 r12 r13 r14 r15 xmm0 xmm1 xmm2 xmm3
    ..B1.15:     # Preds ..B1.15 ..B1.14
    movsd     (%rsi,%rcx,8), %xmm4                          #6.21
    movhpd    8(%rsi,%rcx,8), %xmm4                         #6.21
    mulpd     %xmm2, %xmm4                                  #6.21
    movaps    (%rdi,%rcx,8), %xmm5                          #6.12
    mulpd     %xmm3, %xmm5                                  #6.12
    addpd     %xmm4, %xmm5                                  #6.21
    movaps    16(%rdi,%rcx,8), %xmm7                        #6.12
    movaps    32(%rdi,%rcx,8), %xmm9                        #6.12
    movaps    48(%rdi,%rcx,8), %xmm11                       #6.12
    movaps    %xmm5, (%rdi,%rcx,8)                          #6.3
    mulpd     %xmm3, %xmm7                                  #6.12
    mulpd     %xmm3, %xmm9                                  #6.12
    mulpd     %xmm3, %xmm11                                 #6.12
    movsd     16(%rsi,%rcx,8), %xmm6                        #6.21
    movhpd    24(%rsi,%rcx,8), %xmm6                        #6.21
    mulpd     %xmm2, %xmm6                                  #6.21
    addpd     %xmm6, %xmm7                                  #6.21
    movaps    %xmm7, 16(%rdi,%rcx,8)                        #6.3
    movsd     32(%rsi,%rcx,8), %xmm8                        #6.21
    movhpd    40(%rsi,%rcx,8), %xmm8                        #6.21
    mulpd     %xmm2, %xmm8                                  #6.21
    addpd     %xmm8, %xmm9                                  #6.21
    movaps    %xmm9, 32(%rdi,%rcx,8)                        #6.3
    movsd     48(%rsi,%rcx,8), %xmm10                       #6.21
    movhpd    56(%rsi,%rcx,8), %xmm10                       #6.21
    mulpd     %xmm2, %xmm10                                 #6.21
    addpd     %xmm10, %xmm11                                #6.21
    movaps    %xmm11, 48(%rdi,%rcx,8)                       #6.3
    addq      $8, %rcx                                      #5.1
    cmpq      %r8, %rcx                                     #5.1
    jl        ..B1.15       # Prob 99%                      #5.1

depende de cómo ha colocado u y B en la memoria. si ambos bloques de memoria están lejos el uno del otro, no sería SSE a aumentar mucho en este escenario.

se sugiere que la matriz U y B son AOE (matriz de la estructura) en lugar de SOA (estructura de la matriz), porque se puede cargar tanto de ellos en el registro de instrucción individual.

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