Performance 内存带宽受限时SSE和AVX的性能

Performance 内存带宽受限时SSE和AVX的性能,performance,caching,sse,avx,Performance,Caching,Sse,Avx,在下面的代码中,我更改了“dataLen”并获得了不同的效率 dataLen=400 SSE时间:758000 us平均时间:483000 us SSE>AVX dataLen=2400 SSE时间:4212000 us AVX时间:2636000 us SSE>AVX dataLen=2864 SSE时间:6115000 us AVX时间:6146000 us SSE~=AVX dataLen=3200 SSE时间:8049000 us平均时间:9297000 us SSE

在下面的代码中,我更改了“dataLen”并获得了不同的效率

dataLen=400 SSE时间:758000 us平均时间:483000 us SSE>AVX

dataLen=2400 SSE时间:4212000 us AVX时间:2636000 us SSE>AVX

dataLen=2864 SSE时间:6115000 us AVX时间:6146000 us SSE~=AVX

dataLen=3200 SSE时间:8049000 us平均时间:9297000 us SSE dataLen=4000 SSE时间:10170000us平均时间:1169000US平均时间 SSE和AVX代码都可以简化为:buf3[i]+=buf1[1]*buf2[i]

#include "testfun.h"
#include <iostream>
#include <chrono>
#include <malloc.h>
#include "immintrin.h"
using namespace std::chrono;

void testfun()
{
int dataLen = 4000; 
int N = 10000000;
float *buf1 = reinterpret_cast<float*>(_aligned_malloc(sizeof(float)*dataLen, 32));
float *buf2 = reinterpret_cast<float*>(_aligned_malloc(sizeof(float)*dataLen, 32));
float *buf3 = reinterpret_cast<float*>(_aligned_malloc(sizeof(float)*dataLen, 32));
for(int i=0; i<dataLen; i++)
{
    buf1[i] = 1;
    buf2[i] = 1;
    buf3[i] = 0;
}
//=========================SSE CODE=====================================
system_clock::time_point SSEStart = system_clock::now();
__m128 p1, p2, p3;

for(int j=0; j<N; j++)
for(int i=0; i<dataLen; i=i+4)
{
    p1 = _mm_load_ps(&buf1[i]);
    p2 = _mm_load_ps(&buf2[i]);
    p3 = _mm_load_ps(&buf3[i]);
    p3 = _mm_add_ps(_mm_mul_ps(p1, p2), p3);
    _mm_store_ps(&buf3[i], p3);
}

microseconds SSEtimeUsed = duration_cast<milliseconds>(system_clock::now() - SSEStart);
std::cout << "SSE time used: " << SSEtimeUsed.count() << " us, " <<std::endl;

//=========================AVX CODE=====================================
for(int i=0; i<dataLen; i++) buf3[i] = 0;

system_clock::time_point AVXstart = system_clock::now();
__m256  pp1, pp2, pp3; 

for(int j=0; j<N; j++)
for(int i=0; i<dataLen; i=i+8)
{       
    pp1 = _mm256_load_ps(&buf1[i]);
    pp2 = _mm256_load_ps(&buf2[i]);
    pp3 = _mm256_load_ps(&buf3[i]);
    pp3 = _mm256_add_ps(_mm256_mul_ps(pp1, pp2), pp3);
    _mm256_store_ps(&buf3[i], pp3);

}

microseconds AVXtimeUsed = duration_cast<milliseconds>(system_clock::now() - AVXstart);
std::cout << "AVX time used: " << AVXtimeUsed.count() << " us, " <<std::endl;

_aligned_free(buf1);
_aligned_free(buf2);
}
#包括“testfun.h”
#包括
#包括
#包括
#包括“immintrin.h”
使用名称空间std::chrono;
void testfun()
{
int dataLen=4000;
int N=10000000;
float*buf1=重新解释铸件(_-aligned_-malloc(sizeof(float)*dataLen,32));
float*buf2=重新解释铸件(_-aligned_-malloc(sizeof(float)*dataLen,32));
float*buf3=重新解释铸件(_-aligned_-malloc(sizeof(float)*dataLen,32));

对于(int i=0;i这是一个有趣的观察结果。我能够重现您的结果。我通过展开循环(参见下面的代码)设法大大提高了SSE代码的速度现在,对于SSE
dataLen=2864
显然更快,对于较小的值,它几乎与AVX一样快。对于较大的值,它更快。这是由于SSE代码中的带循环依赖性(即,展开循环会增加指令级并行性(ILP))。我没有尝试进一步展开。展开AVX代码没有帮助

不过,我对你的问题没有一个明确的答案。我的直觉是,这与ILP以及诸如Sandy Bridge之类的AVX处理器只能加载两个128位字(SSE宽度)有关同时而不是两个256位字。因此,在SSE代码中,它可以同时执行一个SSE加法、一个SSE乘法、两个SSE加载和一个SSE存储。对于AVX,它可以执行一个AVX加载(通过端口2和3上的两个128位加载)、一个AVX乘法、一个AVX加法和一个128位存储(AVX宽度的一半)同时。换句话说,虽然使用AVX时,乘法和加法的工作量是SSE的两倍,但加载和存储仍然是128位宽。这可能会导致使用AVX时的ILP低于SSE时的ILP,有时代码由加载和存储控制

有关端口和ILP的更多信息,请参见此

\uuum128 p1、p2、p3、p1\uv2、p2\uv2、p3\uv2;

对于(int j=0;j我认为这是Sandy Bdrige体系结构缓存系统的缺陷。我可以在Ivy Brdige CPU上重现相同的结果,但在Haswell CPU上无法重现,但Haswell在访问L3时也有相同的问题。我认为这是AVX的大缺陷。Intel应该在下一步或下一个体系结构上修复此问题

N = 1000000
datalen = 2000
SSE time used: 280000 us,
AVX time used: 156000 us,

N = 1000000
datalen = 4000 <- it's still fast on Haswell using L2
SSE time used: 811000 us,
AVX time used: 702000 us,

N = 1000000
datalen = 6000
SSE time used: 1216000 us,
AVX time used: 1076000 us,

N = 1000000
datalen = 8000
SSE time used: 1622000 us,
AVX time used: 1466000 us,

N = 100000  <- reduced
datalen = 20000 <- fit in L2 : 256K / 23 = 21845.3
SSE time used: 405000 us,
AVX time used: 374000 us,

N = 100000  
datalen = 40000 <- need L3
SSE time used: 1185000 us,
AVX time used: 1263000 us,

N = 100000  
datalen = 80000
SSE time used: 2340000 us,
AVX time used: 2527000 us,
N=1000000
数据长度=2000
使用时间:280000美国,
使用的平均时间:156000 us,
N=1000000

datalen=4000尝试展开SSE循环一次。相关性将变得更大。而且,
datalen=4000
适合L1,因此L3不应该是问题。一次计算使用3个浮点数据,因此L1的32768字节可以容纳2730个组件。你是对的。因此,从L1到L2时似乎会出现差异。你的内存是64 byte对齐,对吗?这很有趣。@Zboson:
dataLen=4000
=48 KB的数据,比32 KB的L1数据缓存大这个程序由内存带宽定义,而且在这种情况下,AVX将比SSE慢。AVX的256位负载比SSE的128位负载慢。我们可以称之为一个错误r中央处理器!
N = 1000000
datalen = 2000
SSE time used: 280000 us,
AVX time used: 156000 us,

N = 1000000
datalen = 4000 <- it's still fast on Haswell using L2
SSE time used: 811000 us,
AVX time used: 702000 us,

N = 1000000
datalen = 6000
SSE time used: 1216000 us,
AVX time used: 1076000 us,

N = 1000000
datalen = 8000
SSE time used: 1622000 us,
AVX time used: 1466000 us,

N = 100000  <- reduced
datalen = 20000 <- fit in L2 : 256K / 23 = 21845.3
SSE time used: 405000 us,
AVX time used: 374000 us,

N = 100000  
datalen = 40000 <- need L3
SSE time used: 1185000 us,
AVX time used: 1263000 us,

N = 100000  
datalen = 80000
SSE time used: 2340000 us,
AVX time used: 2527000 us,