SSE-copy,AVX-copy和std :: copy性能

gor*_*ill 19 c++ performance sse simd avx

我试图通过SSE和AVX提高复制操作的性能:

    #include <immintrin.h>

    const int sz = 1024;
    float *mas = (float *)_mm_malloc(sz*sizeof(float), 16);
    float *tar = (float *)_mm_malloc(sz*sizeof(float), 16);
    float a=0;
    std::generate(mas, mas+sz, [&](){return ++a;});

    const int nn = 1000;//Number of iteration in tester loops    
    std::chrono::time_point<std::chrono::system_clock> start1, end1, start2, end2, start3, end3; 

    //std::copy testing
    start1 = std::chrono::system_clock::now();
    for(int i=0; i<nn; ++i)
        std::copy(mas, mas+sz, tar);
    end1 = std::chrono::system_clock::now();
    float elapsed1 = std::chrono::duration_cast<std::chrono::microseconds>(end1-start1).count();

    //SSE-copy testing
    start2 = std::chrono::system_clock::now();
    for(int i=0; i<nn; ++i)
    {
        auto _mas = mas;
        auto _tar = tar;
        for(; _mas!=mas+sz; _mas+=4, _tar+=4)
        {
           __m128 buffer = _mm_load_ps(_mas);
           _mm_store_ps(_tar, buffer);
        }
    }
    end2 = std::chrono::system_clock::now();
    float elapsed2 = std::chrono::duration_cast<std::chrono::microseconds>(end2-start2).count();

    //AVX-copy testing
    start3 = std::chrono::system_clock::now();
    for(int i=0; i<nn; ++i)
    {
        auto _mas = mas;
        auto _tar = tar;
        for(; _mas!=mas+sz; _mas+=8, _tar+=8)
        {
           __m256 buffer = _mm256_load_ps(_mas);
           _mm256_store_ps(_tar, buffer);
        }
    }
    end3 = std::chrono::system_clock::now();
    float elapsed3 = std::chrono::duration_cast<std::chrono::microseconds>(end3-start3).count();

    std::cout<<"serial - "<<elapsed1<<", SSE - "<<elapsed2<<", AVX - "<<elapsed3<<"\nSSE gain: "<<elapsed1/elapsed2<<"\nAVX gain: "<<elapsed1/elapsed3;

    _mm_free(mas);
    _mm_free(tar);
Run Code Online (Sandbox Code Playgroud)

有用.然而,虽然测试器循环中的迭代次数--nn - 增加,但是simd-copy的性能增益降低了:

nn = 10:SSE增益= 3,AVX增益= 6;

nn = 100:SSE增益= 0.75,AVX增益= 1.5;

nn = 1000:SSE增益= 0.55,AVX增益= 1.1;

任何人都可以解释提到性能降低效果的原因是什么,是否可以手动矢量化复制操作?

Ste*_*fan 23

问题在于,您的测试在硬件中迁移一些使基准测试变得困难的因素方面做得很差.为了测试这个,我做了我自己的测试用例.像这样的东西:

for blah blah:
    sleep(500ms)
    std::copy
    sse
    axv
Run Code Online (Sandbox Code Playgroud)

输出:

SSE: 1.11753x faster than std::copy
AVX: 1.81342x faster than std::copy
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下,AVX比一堆快std::copy.当我将测试用例改为..时会发生什么

for blah blah:
    sleep(500ms)
    sse
    axv
    std::copy
Run Code Online (Sandbox Code Playgroud)

请注意,除了测试的顺序之外,绝对没有任何改变.

SSE: 0.797673x faster than std::copy
AVX: 0.809399x faster than std::copy
Run Code Online (Sandbox Code Playgroud)

哇!怎么可能?CPU需要一段时间才能提升到全速,因此稍后运行的测试具有优势.这个问题现在有3个答案,包括一个"接受"的答案.但只有赞成票数量最少的人才能走上正轨.

这是基准测试很难的原因之一,除非他们已经包含了他们设置的详细信息,否则你永远不应该相信任何人的微基准测试.不只是代码可能出错.省电功能和奇怪的驱动程序可能会完全破坏您的基准.有一次,我通过切换不到1%的笔记本电脑提供的BIOS中的开关来测量性能差异7.

  • 这个答案提出了一些非常重要的观点,没有这些观点,整个讨论将毫无用处。但恐怕也不完全正确。它指出“CPU 需要一段时间才能达到全速”,但是,这里的问题似乎更可能与缓存有关。一个好的测试必须(至少)在循环中运行多次以缓解这种情况,永远不要只运行一次。 (3认同)

PhD*_*EcE 6

这是一个非常有趣的问题,但我相信到目前为止没有答案是正确的,因为问题本身是如此误导.

标题应更改为"如何达到理论内存I/O带宽?"

无论使用什么指令集,CPU都比RAM快得多,因此纯块存储器复制是100%I/O限制.这就解释了为什么SSE和AVX性能之间几乎没有差别.

对于L1D高速缓存中的小型缓冲区,AVX可以比像Haswell这样的CPU上的SSE复制速度明显快,其中256b加载/存储确实使用256b数据路径到L1D缓存而不是分成两个128b操作.

具有讽刺意味的是,古老的X86指令rep stosq在内存复制方面比SSE和AVX表现更好!

这篇文章解释了如何很好地充分记忆带宽,并且它还有很多参考资料可供进一步探索.

另请参阅SO上的memcpy的增强REP MOVSB,其中@ BeeOnRope的答案讨论了NT存储(以及完成的非RFO存储rep stosb/stosq)与常规存储的关系,以及单核内存带宽通常如何受最大并发/延迟的限制,而不是内存控制器本身.