【问题标题】:AVX, SSE sums are slower than gcc autovectorizationAVX,SSE 总和比 gcc 自动矢量化慢
【发布时间】:2017-06-10 22:19:04
【问题描述】:

我遇到了一个奇怪的现象,无法真正解释。我正在尝试编写一些数字代码,从而对一些实现进行基准测试。我只是想用 SSEAVX 以及 gcc 自动矢量化来对一些矢量添加进行基准测试。为了测试这一点,我使用并修改了下面的代码。

代码:

#include <iostream>
#include <immintrin.h>

#include "../../time/timer.hpp"


void ser(double* a, double* b, double* res, int size){
 for(int i(0); i < size; i++ )
 {
    res[i] = a[i] + b[i];
 }
}

void sse(double* a, double* b, double* res, int size){
 for (int i(0); i < (size & ~0x1); i += 2 )
 {
    const __m128d kA2   = _mm_load_pd( &a[i] );
    const __m128d kB2   = _mm_load_pd( &b[i] );
    const __m128d kRes = _mm_add_pd( kA2, kB2 );
    _mm_store_pd( &res[i], kRes );
 }
}

void avx(double* a, double* b, double* res, int size){
for (int i(0); i < (size & ~0x3); i += 4 )
 {
    const __m256d kA4   = _mm256_load_pd( &a[i] );
    const __m256d kB4   = _mm256_load_pd( &b[i] );
    const __m256d kRes = _mm256_add_pd( kA4, kB4 );
    _mm256_store_pd( &res[i], kRes );
 }
}


#define N 1e7*64

int main(int argc, char const *argv[])
{ 


 double* a = (double*)_mm_malloc(N*sizeof(double), 64);
 double* b = (double*)_mm_malloc(N*sizeof(double), 64);
 double* res = (double*)_mm_malloc(N*sizeof(double), 64);

 Timer tm;

 tm.start();
 avx(a,b,res,N);
 tm.stop();
 std::cout<<"AVX\t"<<tm.elapsed()<<" ms\t"
          <<1e-6*N/tm.elapsed() <<" GFLOP/s"<<std::endl;

 tm.start();
 sse(a,b,res,N);
 tm.stop();
 std::cout<<"SSE\t"<<tm.elapsed()<<" ms\t"
          <<1e-6*N/tm.elapsed() <<" GFLOP/s"<<std::endl;

 tm.start();
 ser(a,b,res,N);
 tm.stop();
 std::cout<<"SER\t"<<tm.elapsed()<<" ms\t"
          <<1e-6*N/tm.elapsed() <<" GFLOP/s"<<std::endl;
 return 0;
}

对于时间和计算的 GFLOP/S,我得到:

./test3
AVX 1892 ms 0.338266 GFLOP/s
SSE 408  ms 1.56863 GFLOP/s
SER 396  ms 1.61616 GFLOP/s

与我的 i5 6600K 大约 170 GFLOP/s 的 peak performance 相比,这显然非常慢。

我在这里遗漏了什么重要的东西吗?我知道在 CPU 上添加向量并不是最好的主意,但这些结果真的很糟糕。感谢您提供任何线索。

【问题讨论】:

  • 您犯了一个非常常见(但不明显)的基准测试错误。您忘记从malloc() 初始化内存。所以 AVX 测试(首先运行)实际上是页面错误。
  • 一旦你解决了这个问题,你就会遇到这个问题:stackoverflow.com/questions/18159455/…
  • @Mysticial 对!我现在使用热内存进行基准测试,并在每个矢量化上获得大致相同的性能!谢谢。
  • 当我进行基准测试时,我通常会在没有计时的情况下进行冷运行。例如。在您的代码中,在计时之前先进行一次迭代avx(a,b,res,1)。实际上,根据您在做什么,我认为报告冷热时间可能很有用。如果一个函数只被调用一次,那么只报告热时间可能会产生误导。

标签: c++ gcc vectorization sse avx


【解决方案1】:

您的应用程序可能受内存限制而不是 CPU 限制。换句话说,内存带宽是瓶颈,所以向量化在这里没有多大帮助。

【讨论】:

    【解决方案2】:

    它很可能与分支预测有关(阅读Why is it faster to process a sorted array than an unsorted array? 以获得更详细的解释)。在我的 i5-4200U 上,当按 AVX、SSE、SER 的顺序处理 100000000 双倍时,我得到了这个。

    AVX 807 ms 0.123916 GFLOP/s SSE 215 ms 0.465116 GFLOP/s SER 287 ms 0.348432 GFLOP/s

    但如果我将其更改为 SER、AVX、SSE,我会得到这个

    SER 753 ms 0.132802 GFLOP/s AVX 225 ms 0.444444 GFLOP/s SSE 196 ms 0.510204 GFLOP/s

    我不知道为什么它离你的 CPU 的峰值性能这么远。

    【讨论】:

    • 这与分支预测无关。这是关于页面提交的。第一次测试比较慢,因为内存还没有被提交,所以它出现了页面错误。
    • 对不起,我的错。谢谢。
    • 当编译器编写者可能比普通人做得更好时,为什么要努力显式使用 AVX?当然,如果编译器不能自动向量化,那么可能需要这样做,但有一种假设是,自己编写指令会得到比编译器更好的代码,这显然不是确定性的。
    • @Holmz 你是完全正确的。但这更多是关于向量化和的基本问题及其向量化的可能性(以及为什么不)。
    • 基本上最初的问题是关于基准测试的,编译器生成的代码比你更快。这对我们其他人来说也很常见,除非我们花时间编写类似于汇编的 SSE/AVX。使用更高级别的语言总是更快,除非我们进行基准测试,或者追求最快,或者有编译器无法做到的情况......那么我们别无选择,只能自己做。跨度>
    猜你喜欢
    • 1970-01-01
    • 2021-03-25
    • 2017-08-31
    • 2012-08-08
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 2015-10-11
    • 2011-10-18
    相关资源
    最近更新 更多