2012-06-08 31 views
14

Esta es la primera vez que trabajo con intrínsecamente SSE. Estoy tratando de convertir un simple fragmento de código en una versión más rápida usando Intel SSE intrínseco (hasta SSE4.2). Parece que me encuentro con una serie de errores.Optimización del código utilizando los intrínsecos de Intel SSE para la vectorización

La versión escalar del código es: (multiplicación matriz simple)

 void mm(int n, double *A, double *B, double *C) 
    { 
     int i,j,k; 
     double tmp; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k++) 
          tmp += A[n*i+k] * 
            B[n*k+j]; 
        C[n*i+j] = tmp; 

       } 
      } 

Ésta es mi versión: He incluido # include

 void mm_sse(int n, double *A, double *B, double *C) 
     { 
     int i,j,k; 
     double tmp; 
     __m128d a_i, b_i, c_i; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k+=4) 
          a_i = __mm_load_ps(&A[n*i+k]); 
          b_i = __mm_load_ps(&B[n*k+j]); 
          c_i = __mm_load_ps(&C[n*i+j]); 

          __m128d tmp1 = __mm_mul_ps(a_i,b_i); 
          __m128d tmp2 = __mm_hadd_ps(tmp1,tmp1); 
          __m128d tmp3 = __mm_add_ps(tmp2,tmp3); 
          __mm_store_ps(&C[n*i+j], tmp3); 

      } 
     } 

dónde voy mal con esto? Estoy recibiendo varios errores como este:

mm_vec.c (84): error: un valor de tipo "int" no se puede asignar a una entidad de tipo "__m128d" a_i = __mm_load_ps (& A [n * i + k]);

Esta es la forma en que estoy recopilando: ICC -O2 mm_vec.c -o vec

por favor alguien puede ayudar a convertir este código con precisión. ¡Gracias!

ACTUALIZACIÓN:

De acuerdo a sus sugerencias, que han realizado los siguientes cambios:

 void mm_sse(int n, float *A, float *B, float *C) 
     { 
     int i,j,k; 
     float tmp; 
     __m128 a_i, b_i, c_i; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k+=4) 
          a_i = _mm_load_ps(&A[n*i+k]); 
          b_i = _mm_load_ps(&B[n*k+j]); 
          c_i = _mm_load_ps(&C[n*i+j]); 

          __m128 tmp1 = _mm_mul_ps(a_i,b_i); 
          __m128 tmp2 = _mm_hadd_ps(tmp1,tmp1); 
          __m128 tmp3 = _mm_add_ps(tmp2,tmp3); 
          _mm_store_ps(&C[n*i+j], tmp3); 


      } 
     } 

Pero ahora parecen estar recibiendo un fallo de segmentación. Sé esto quizás porque no estoy accediendo correctamente a los subíndices de la matriz para la matriz A, B, C. Soy muy nuevo en esto y no estoy seguro de cómo proceder con esto.

Por favor, ayúdenme a determinar el enfoque correcto para manejar este código.

Respuesta

9

El error que se está viendo es porque tiene demasiados guiones en los nombres de función, por ejemplo:

__mm_mul_ps 

debería ser:

_mm_mul_ps // Just one underscore up front 

por lo que el compilador de C está asumiendo regresan int ya que no ha visto una declaración.

Además de esto, hay más problemas: parece que está mezclando llamadas a variantes de flotación dobles e individuales de la misma instrucción.

Por ejemplo, usted tiene:

__m128d a_i, b_i, c_i; 

pero se llama:

__mm_load_ps(&A[n*i+k]); 

que devuelve no es un __m128 un __m128d - que quería llamar:

_mm_load_pd 

lugar. Del mismo modo para las otras instrucciones si quieres que trabajen en pares de dobles.


Si usted está viendo violaciones de segmento inexplicables y en el código SSE yo estaría inclinado a suponer que usted tiene problemas de alineación de memoria - punteros pasados ​​a las características intrínsecas de la ESS (en su mayoría) necesitan ser 16 byte alineado. Puede check this with a simple assert en su código o verificarlo en un depurador (espera que el último dígito del puntero sea 0 si está alineado correctamente).

Si no está alineado, debe asegurarse de que sea así. Para que las cosas no distinguidos por new/malloc() se puede hacer esto con una extensión compilador (por ejemplo with gcc):

float a[16] __attribute__ ((aligned (16))); 

Siempre y cuando su versión de gcc tiene un alineamiento máximo lo suficientemente grande como para soportar esto y algunas otras advertencias sobre alineación de las pilas . Para el almacenamiento asignado dinámicamente, querrá usar una extensión específica de la plataforma, p. posix_memalign para asignar el almacenamiento adecuado:

float *a=NULL; 
posix_memalign(&a, __alignof__(__m128), sizeof(float)*16); 

(creo que podría ser más agradable maneras, portátiles de hacerlo con C++ 11 pero no estoy 100% seguro de que aún).

Hay algunas instrucciones que le permiten hacer cargas y tiendas desalineadas, pero son terriblemente lentas en comparación con las cargas alineadas y vale la pena evitarlas si es posible.

+0

Estoy trabajando con icc not gcc. ¿Cree que manejarlo así: a_i = _mm_load_ps (& A [n * i + k]); ¿es el enfoque correcto? Los ejemplos que veo publicados en otra parte (incluso en la documentación Intel Intrinsic) tienen ejemplos muy básicos. las matrices A, B, C han sido todas asignadas con malloc. – PGOnTheGo

+1

@Hello_PG La carga en sí no está directamente mal. No es necesario cargar c_i sin embargo. En su mayor parte ICC tiene las mismas extensiones que gcc; creo que ese es el caso para la alineación, aunque estoy más familiarizado con GCC que con ICC personalmente, así que lo califiqué con eso y me vinculé con los documentos que sabía cómo encontrar. malloc no garantiza una alineación adecuada en todas las plataformas, por lo que probablemente se necesite posix_memalign. ¿Falló la afirmación que sugerí? – Flexo

+0

Cuando intento asignar memoria para A como esta: A = (float *) _ aligned_malloc (dimension * dimension * sizeof (float), 16); Obtengo un error de compilación: referencia indefinida a 'aligned_malloc 'con icc. así es como estoy compilando: icc -O2 mm_vec.c -o vec2 – PGOnTheGo

3

Debe asegurarse de que sus cargas y tiendas estén siempre accediendo a direcciones alineadas de 16 bytes. Alternativamente, si no puede garantizar esto por alguna razón, entonces use _mm_loadu_ps/_mm_storeu_ps en lugar de _mm_load_ps/_mm_store_ps - esto será menos eficiente pero no se bloqueará en direcciones desalineadas.

Cuestiones relacionadas