I am programming a perfect program to parallelize with multimedia extensions. The program consists of transforming an image, so i go over a matrix and i modify each pixel inside it. For go over faster, i use multimedia extensions:
At first i used SSE3 extensions and achieved a 2.5 speedup. Next, i programmed it extending the sse algorithm for using AVX extensions (Double size vector), but i do not get gains respect to SSE3. More or less the time to execute the program with SSE is the same as AVX. Here is the summary of the code for SSE and AVX, respectively:
for(i=0; i<lim; i+=12) { //tam vale n*n o n*n-12 dependiendo de si n*n es multiplo de 12. 12 ya que 3componentes*4pixeles(4 tamvector)
vectorR = _mm_set_ps(matrix[i+9], matrix[i+6], matrix[i+3], matrix[i]);
vectorG = _mm_set_ps(matrix[i+10], matrix[i+7], matrix[i+4], matrix[i+1]);
vectorB = _mm_set_ps(matrix[i+11], matrix[i+8], matrix[i+5], matrix[i+2]);
calcular_coeficientes_sse3(Ycoef0, Ycoef1, Ycoef2, Ucoef0, Vcoef0, vectorR, vectorG, vectorB,
values0, values255, values128, &vectorY, &vectorU, &vectorV);
_mm_store_ps(&y_aux[0], vectorY);
_mm_store_ps(&u_aux[0], vectorU);
_mm_store_ps(&v_aux[0], vectorV);
//Colocamos los datos en la matriz
//PIXEL 1
matrix[i] = y_aux[0];
matrix[i+1]=u_aux[0];
matrix[i+2]=v_aux[0];
//PIXEL 2
matrix[i+3]=y_aux[1];
matrix[i+4]=u_aux[1];
matrix[i+5]=v_aux[1];
//PIXEL 3
matrix[i+6] = y_aux[2];;
matrix[i+7]=u_aux[2];
matrix[i+8]=v_aux[2];
//PIXEL 4
matrix[i+9]=y_aux[3];
matrix[i+10]=u_aux[3];
matrix[i+11]=v_aux[3];
}
for(i=0; i<lim; i+=24) { //Vamos de 8 en 8 pixeles
vectorR = _mm256_set_ps(matrix[i+21], matrix[i+18], matrix[i+15] ,matrix[i+12],matrix[i+9], matrix[i+6], matrix[i+3], matrix[i]);
vectorG = _mm256_set_ps(matrix[i+22], matrix[i+19], matrix[i+16], matrix[i+13], matrix[i+10], matrix[i+7], matrix[i+4], matrix[i+1]);
vectorB = _mm256_set_ps(matrix[i+23], matrix[i+20], matrix[i+17], matrix[i+14], matrix[i+11], matrix[i+8], matrix[i+5], matrix[i+2]);
calcular_coeficientes_avx(Ycoef0, Ycoef1, Ycoef2, Ucoef0, Vcoef0, vectorR, vectorG, vectorB,
values0, values255, values128, &vectorY, &vectorU, &vectorV);
_mm256_store_ps(&y_aux[0], vectorY);
_mm256_store_ps(&u_aux[0], vectorU);
_mm256_store_ps(&v_aux[0], vectorV);
//Colocamos los datos en la matriz
//PIXEL 1
matrix[i] = y_aux[0];
matrix[i+1]=u_aux[0];
matrix[i+2]=v_aux[0];
//PIXEL 2
matrix[i+3]=y_aux[1];
matrix[i+4]=u_aux[1];
matrix[i+5]=v_aux[1];
//PIXEL 3
matrix[i+6] = y_aux[2];;
matrix[i+7]=u_aux[2];
matrix[i+8]=v_aux[2];
//PIXEL 4
matrix[i+9]=y_aux[3];
matrix[i+10]=u_aux[3];
matrix[i+11]=v_aux[3];
//PIXEL 5
matrix[i+12]=y_aux[4];
matrix[i+13]=u_aux[4];
matrix[i+14]=v_aux[4];
//PIXEL 6
matrix[i+15]=y_aux[5];
matrix[i+16]=u_aux[5];
matrix[i+17]=v_aux[5];
//PIXEL 7
matrix[i+18]=y_aux[6];
matrix[i+19]=u_aux[6];
matrix[i+20]=v_aux[6];
//PIXEL 8
matrix[i+21]=y_aux[7];
matrix[i+22]=u_aux[7];
matrix[i+23]=v_aux[7];
}
void calcular_coeficientes_sse3(__m128 Ycoef0, __m128 Ycoef1, __m128 Ycoef2, __m128 Ucoef0, __m128 Vcoef0, __m128 vectorR,
__m128 vectorG, __m128 vectorB, __m128 values0, __m128 values255, __m128 values128,
__m128 *vectorY, __m128 *vectorU, __m128 *vectorV) {
//CALCULO DE Y3, Y2, Y1, Y0 (Cuatro píxeles consecutivos)
//PRIMERA VUELta
__m128 valores1 = _mm_mul_ps(Ycoef0, vectorR); // valores1 = (0.299*R[3], 0.299*R[2], 0.299*R[1], 0.299*R[0])
__m128 valores2 = _mm_mul_ps(Ycoef1, vectorG); // valores2 = (0.587*G[3], 0.587*G[2], 0.587*G[1], 0.587*G[0])
__m128 valores3 = _mm_mul_ps(Ycoef2, vectorB); // valores3 = (0.114*B[3], 0.114*B[2], 0.114*B[1], 0.114*B[0]);
valores1 = _mm_add_ps(valores1, valores2); // valores1 = (0.299*R[3] + 0.587*G[3], 0.299*R[2] + 0.587*G[2], 0.299*G[1]+ ..., ...)
*vectorY = _mm_add_ps(valores1, valores3); // vectorY = (Y[3], Y[2], Y[1], Y[0])
*vectorY = _mm_floor_ps(*vectorY);
//Calculo de U3, U2, U1, U0
//B-Y
valores1 = _mm_sub_ps(vectorB, *vectorY); // valores1 = (B[3]-Y[3], B[2]-Y[2], B[1]-Y[1], B[0]-Y[0])
valores1 = _mm_mul_ps(Ucoef0, valores1); // valores1 = (0.492*(B[3]-Y[3]), 0.492*(B[2]-Y[2]), 0.492*(B[1]-Y[1]), 0.492*(...))
*vectorU = _mm_add_ps(valores1, values128); // vectorU = (U[3], U[2], U[1], U[0])
//CALCULO DE V3, V2, V1, V0
// R-Y
valores1 = _mm_sub_ps(vectorR, *vectorY); // valores1 = (R[3]-Y[3], R[2]-Y[2], R[1]-Y[1], R[0]-Y[0])
valores1 = _mm_mul_ps(Vcoef0, valores1); // valores1 = (0.877*(R[3]-Y[3]), 0.877*(R[2]-Y[2]), 0.877*(R[1]-Y[1]), 0.877*(...))
valores1 = _mm_add_ps(valores1, values128); // valores1 = (0.877*(R[3]-Y[3]) + 128, 0.877*(R[2]-Y[2]) + 128, ..., ...)
//valores1 pueden necesitar saturacion.
//SATURACIONES a 0
//Para evitar hacer comparaciones cogemos el mayor entre 0 y el valor V[i]:
// Si V[i] > 0 se queda con V[i] pues es mayor que 0.
// Si V[i] < 0 se queda con 0 pues es mayor que un número negativo.
valores1 = _mm_max_ps(valores1, values0); // valores1 = (max(0.877*(R[3]-Y[3]) + 128,0), ..., ..., ...)
// SATURACIONES a 255
//Para evitar hacer comparacion cogemos el menor entre 255 y el valor V[i]
// Si V[i] < 255 entonces se queda con el menor, V[i]
// Si V[i] > 255 entonces se queda con el menor, 255.
*vectorV = _mm_min_ps(valores1, values255); //vectorV = (V[3], V[2], V[1], V[0])
//NOTA: Al estar las operaciones implementadas en hardware se hacen las operaciones max y min en un 1 ciclo.
//por lo que solo en dos ciclos comprobamos la saturacion de 4 valores V.
return; //El procedimiento termina devolviendo vectorY, vectorU y vectorV
}
void calcular_coeficientes_avx(__m256 Ycoef0, __m256 Ycoef1, __m256 Ycoef2, __m256 Ucoef0, __m256 Vcoef0, __m256 vectorR,
__m256 vectorG, __m256 vectorB, __m256 values0, __m256 values255, __m256 values128,
__m256 *vectorY, __m256 *vectorU, __m256 *vectorV) {
//CALCULO DE Y7, Y6, Y5, Y4, Y3, Y2, Y1, Y0 (Cuatro píxeles consecutivos)
__m256 valores1 = _mm256_mul_ps(Ycoef0, vectorR);
__m256 valores2 = _mm256_mul_ps(Ycoef1, vectorG);
__m256 valores3 = _mm256_mul_ps(Ycoef2, vectorB);
valores1 = _mm256_add_ps(valores1, valores2);
*vectorY = _mm256_add_ps(valores1, valores3);
*vectorY = _mm256_floor_ps(*vectorY);
//Calculo de U7, U6, U5, U4, U3, U2, U1, U0
valores1 = _mm256_sub_ps(vectorB, *vectorY);
valores1 = _mm256_mul_ps(Ucoef0, valores1);
*vectorU = _mm256_add_ps(valores1, values128);
//CALCULO DE V7, V6, V5, V4, V3, V2, V1, V0
// R-Y
valores1 = _mm256_sub_ps(vectorR, *vectorY);
valores1 = _mm256_mul_ps(Vcoef0, valores1);
valores1 = _mm256_add_ps(valores1, values128);
//valores1 pueden necesitar saturacion.
//SATURACIONES a 0
//Para evitar hacer comparaciones cogemos el mayor entre 0 y el valor V[i]:
valores1 = _mm256_max_ps(valores1, values0);
// SATURACIONES a 255
//Para evitar hacer comparacion cogemos el menor entre 255 y el valor V[i]
// Si V[i] < 255 entonces se queda con el menor, V[i]
// Si V[i] > 255 entonces se queda con el menor, 255.
*vectorV = _mm256_min_ps(valores1, values255); //vectorV = (V[3], V[2], V[1], V[0])
//NOTA: Al estar las operaciones implementadas en hardware se hacen las operaciones max y min en un 1 ciclo.
//por lo que solo en dos ciclos comprobamos la saturacion de 4 valores V.
return; //El procedimiento termina devolviendo vectorY, vectorU y vectorV
}
As you can see both sse and avx are the same but the last is extended and use longer size vector. Why is the same execution time?
NOTE: I have tried in two differents computers with AVX support (obviusly) and i have the same problem.
Thank you very much.