【发布时间】:2013-08-21 07:01:15
【问题描述】:
我试图通过 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);
它有效。然而,虽然测试循环中的迭代次数 - nn - 增加了,但 simd-copy 的性能增益却减少了:
nn=10:SSE-gain=3,AVX-gain=6;
nn=100:SSE 增益=0.75,AVX 增益=1.5;
nn=1000:SSE 增益=0.55,AVX 增益=1.1;
谁能解释一下提到的性能下降影响的原因是什么?是否建议手动对复制操作进行矢量化?
【问题讨论】:
-
我相信我在某处读到过(Agner Fog ?),由于 Haswell 上的积极电源管理,当您开始使用之前空闲时,可能会有一个“加速”时间(数百个周期?)执行单元,例如 SSE/AVX。对于小的 nn,这可能会扭曲您的测量结果。您应该查看绝对时间(每个元素)以及比率来验证这一点。
-
@PaulR 但是这里 SSE/AVX 变得越来越慢,而不是越来越快......这是一个下降,而不是一个上升
-
@xanatos:是的,但也许
std::copy已经在使用 SSE/AVX,而且升级主要影响的是std::copy,而不是随后的手动编码 SIMD 副本。您可以通过更改我想的副本的顺序来测试这一点。 -
FWIW,我无法使用 Intel Core i7 2600K 在 VS2012 上重现此问题。使用
nn = 1000太小而无法测量。上升到nn = 1000000显示SSE gain: 1.02222和AVX gain: 1.70371- 如果编译器仅使用 SSE,我希望看到它本身。 -
您的代码包含一个错误:AVX 对齐副本需要 32 字节对齐,但您只请求 16 字节对齐。另外,我认为您的测试用例的大小存在严重缺陷。在 Windows 上,如果系统时钟实现 1ms 精度,那么您很幸运,但是您的测试用例的结果在我的系统(i7-2820QM)上运行在微秒范围内。如果我在这里添加几个零,结果非常接近(~5%)。不要忘记预热你的处理器...
标签: c++ performance sse simd avx