[英]Matrix multiplication code running slower with AVX2
我正在学习使用AVX编程。 因此,我编写了一个简单的程序来乘以大小为4的矩阵。虽然没有进行编译器优化,但AVX版本比非AVX版本略快,而经过O3优化,非AVX版本的速度几乎是AVX的两倍。版。 关于如何改善AVX版本性能的任何提示? 以下是完整的代码。
#include <immintrin.h>
#include <stdio.h>
#include <stdlib.h>
#define MAT_SIZE 4
#define USE_AVX
double A[MAT_SIZE][MAT_SIZE];
double B[MAT_SIZE][MAT_SIZE];
double C[MAT_SIZE][MAT_SIZE];
union {
double m[4][4];
__m256d row[4];
} matB;
void init_matrices()
{
for(int i = 0; i < MAT_SIZE; i++)
for(int j = 0; j < MAT_SIZE; j++)
{
A[i][j] = (float)(i+j);
B[i][j] = (float)(i+j+1);
matB.m[i][j] = B[i][j];
}
}
void print_result()
{
for(int i = 0; i < MAT_SIZE; i++)
{
for(int j = 0; j < MAT_SIZE; j++)
{
printf("%.1f\t", C[i][j]);
}
printf("\n");
}
}
void withoutAVX()
{
for(int row = 0; row < MAT_SIZE; row++)
for(int col = 0; col < MAT_SIZE; col++)
{
float sum = 0;
for(int e = 0; e < MAT_SIZE; e++)
sum += A[row][e] * B[e][col];
C[row][col] = sum;
}
}
void withAVX()
{
for(int row = 0; row < 4; row++)
{
//calculate_resultant_row(row);
const double* rowA = (const double*)&A[row];
__m256d* pr = (__m256d*)(&C[row]);
*pr = _mm256_mul_pd(_mm256_broadcast_sd(&rowA[0]), matB.row[0]);
for(int i = 1; i < 4; i++)
*pr = _mm256_add_pd(*pr, _mm256_mul_pd(_mm256_broadcast_sd(&rowA[i]),
matB.row[i]));
}
}
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
int main()
{
init_matrices();
// start timer
unsigned long long cycles = rdtsc();
#ifdef USE_AVX
withAVX();
#else
withoutAVX();
#endif
// stop timer
cycles = rdtsc() - cycles;
printf("\nTotal time elapsed : %ld\n\n", cycles);
print_result();
return 0;
}
很难确切地知道不知道您使用的是哪个编译器和系统。 您需要检查所生成代码的汇编以确保。 以下仅是一些可能的原因。
编译器可能会产生额外的负载/存储。 这将花费。
来自A的最里面的循环广播元素。因此,您有额外的负担。 最佳代码仅需要8次加载,A和B分别需要4次加载,并在C中再存储4次。但是,由于您使用broadcastsd,因此您的代码将导致至少16次额外加载。 这些将使您花费与计算本身一样多的费用,甚至可能更多。
编辑(评论太长)
在某些情况下,编译器将无法执行智能优化,或者有时它永远“太聪明”。 最近,我什至需要使用汇编程序来避免编译器优化,这实际上会导致错误的代码! 就是说,如果您需要的是性能,并且您实际上并不关心如何达到目标。 我建议您先寻找好的图书馆。 例如,线性代数的本征将完全适合您在此示例中的需求。 如果您确实想学习SIMD编程,建议您从更简单的情况开始,例如添加两个向量。 您很可能会发现,与前几次尝试相比,编译器将能够生成更好的矢量化二进制文件。 但是它们更加简单明了,因此您将更轻松地看到需要改进的地方。 在尝试生成与编译器所生成的代码一样好或更好的代码的过程中,您将学习编写最佳代码所需的各种知识。 最终,您将能够为编译器无法优化的代码提供最佳实现。 您需要记住的一件事是,级别越低,编译器可以为您做的越少。 您将对生成的二进制文件有更多的控制权,但是使它们最佳化也是您的责任。 这些建议非常模糊。 抱歉不能提供更多帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.