繁体   English   中英

矩阵乘法代码在AVX2上运行速度较慢

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM