C++ SSE比常规函数慢得多
我正在使用SSE进行Julia集可视化。 这是我的密码 类和运算符C++ SSE比常规函数慢得多,c++,visual-studio-2010,windows-forms-designer,sse,C++,Visual Studio 2010,Windows Forms Designer,Sse,我正在使用SSE进行Julia集可视化。 这是我的密码 类和运算符 class vec4 { public: inline vec4(void) {} inline vec4(__m128 val) :v(val) {} __m128 v; inline void operator=(float *a) {v=_mm_load_ps(a);} inline vec4(float *a) {(*this)=
class vec4 {
public:
inline vec4(void) {}
inline vec4(__m128 val) :v(val) {}
__m128 v;
inline void operator=(float *a) {v=_mm_load_ps(a);}
inline vec4(float *a) {(*this)=a;}
inline vec4(float a) {(*this)=a;}
inline void operator=(float a) {v=_mm_load1_ps(&a);}
};
inline vec4 operator+(const vec4 &a,const vec4 &b) { return _mm_add_ps(a.v,b.v); }
inline vec4 operator-(const vec4 &a,const vec4 &b) { return _mm_sub_ps(a.v,b.v); }
inline vec4 operator*(const vec4 &a,const vec4 &b) { return _mm_mul_ps(a.v,b.v); }
inline vec4 operator/(const vec4 &a,const vec4 &b) { return _mm_div_ps(a.v,b.v); }
inline vec4 operator++(const vec4 &a)
{
__declspec(align(16)) float b[4]={1.0f,1.0f,1.0f,1.0f};
vec4 B(b);
return _mm_add_ps(a.v,B.v);
}
功能本身:
vec4 TWO(2.0f);
vec4 FOUR(4.0f);
vec4 ZER(0.0f);
vec4 CR(cR);
vec4 CI(cI);
for (int i=0; i<320; i++) //H
{
float *pr = (float*) _aligned_malloc(4 * sizeof(float), 16); //dynamic
__declspec(align(16)) float pi=i*ratioY + startY;
for (int j=0; j<420; j+=4) //W
{
pr[0]=j*ratioX + startX;
for(int x=1;x<4;x++)
{
pr[x]=pr[x-1]+ratioX;
}
vec4 ZR(pr);
vec4 ZI(pi);
__declspec(align(16)) float color[4]={0.0f,0.0f,0.0f,0.0f};
vec4 COLOR(color);
vec4 COUNT(0.0f);
__m128 MASK=ZER.v;
int _count;
enum {max_count=100};
for (_count=0;_count<=max_count;_count++)
{
vec4 tZR=ZR*ZR-ZI*ZI+CR;
vec4 tZI=TWO*ZR*ZI+CI;
vec4 LEN=tZR*tZR+tZI*tZI;
__m128 MASKOLD=MASK;
MASK=_mm_cmplt_ps(LEN.v,FOUR.v);
ZR=_mm_or_ps(_mm_and_ps(MASK,tZR.v),_mm_andnot_ps(MASK,ZR.v));
ZI=_mm_or_ps(_mm_and_ps(MASK,tZI.v),_mm_andnot_ps(MASK,ZI.v));
__m128 CHECKNOTEQL=_mm_cmpneq_ps(MASK,MASKOLD);
COLOR=_mm_or_ps(_mm_and_ps(CHECKNOTEQL,COUNT.v),_mm_andnot_ps(CHECKNOTEQL,COLOR.v));
COUNT=COUNT++;
operations+=17;
if (_mm_movemask_ps((LEN-FOUR).v)==0) break;
}
_mm_store_ps(color,COLOR.v);
vec4-TWO(2.0f);
vec4-4(4.0f);
vec4-ZER(0.0f);
vec4cr(CR);
vec4ci(CI);
对于(int i=0;i您遇到的问题是SSE intrinics执行的内存操作比非SSE版本多得多。我使用vector类编写了以下内容:
int main (int argc, char *argv [])
{
vec4 a (static_cast <float> (argc));
cout << "argc = " << argc << endl;
a=++a;
cout << "a = (" << a.v.m128_f32 [0] << ", " << a.v.m128_f32 [1] << ", " << a.v.m128_f32 [2] << ", " << a.v.m128_f32 [3] << ", " << ")" << endl;
}
注意:地址是相对于ESP的,并且有一些推送,这解释了相同值的偏移量的奇怪变化
现在,将代码与此版本进行比较:
int main (int argc, char *argv [])
{
float a[4];
for (int i = 0 ; i < 4 ; ++i)
{
a [i] = static_cast <float> (argc + i);
}
cout << "argc = " << argc << endl;
for (int i = 0 ; i < 4 ; ++i)
{
a [i] += 1.0f;
}
cout << "a = (" << a [0] << ", " << a [1] << ", " << a [2] << ", " << a [3] << ", " << ")" << endl;
}
就内存访问而言,增量要求:
SSE FPU
4xfloat write 1xfloat read
1xsse read 1xfloat write
1xsse read+add 1xfloat read
1xsse write 1xfloat write
1xfloat read
1xfloat write
1xfloat read
1xfloat write
total
8 float reads 4 float reads
8 float writes 4 float writes
这表明SSE使用的内存带宽是FPU版本的两倍,内存带宽是一个主要瓶颈
如果你想最大化SSE,那么你需要在一个SSE汇编函数中编写整个AGlorith,这样你就可以尽可能地消除内存读/写。使用内部函数不是优化的理想解决方案。这里是另一个例子(Mandelbrot集)这与我的Julia集算法的实现方式几乎相同
基于。
同样的故事FPU>SSE我甚至跳过了一些不相关的操作。
有什么好办法吗?您可能需要再次检查您的内联运算符函数是否正在内联。我以前也做过类似的事情,结果是VS拒绝内联函数,即使在声明时使用inline
。因此我最终需要使用\u forceinline
。如果您发布一个完全独立的示例,展示了这种差异。您所展示的代码本身是不可编译的。我不确定SIMD是否适合此算法,因为您需要执行的迭代次数在每一点上都是不同的。GPU可能会更好,甚至可以通过cpu核心进行并行。我支持上面Mystical的评论。Pro查看两种实现(SSE和非SSE)的自包含示例需要真正确定问题所在。此外,您使用的优化级别是什么?在较低的优化级别上,编译器可能会做一些愚蠢的事情,尤其是在内部循环中生成的所有临时vec4
变量。忘了说:上面的汇编程序是由VS2005生成的。问题是算法对不同输入的行为不同,这与SSE的行为相反-SIMD=单指令多数据,算法对不同输入的行为必须相同。我能够“修复”但问题是,我不明白为什么会发生这种情况!我没有在vs中调试程序,而是从中实现了程序,然后运行exe。出乎意料的是,SSE的运行速度比NONSE快了3倍。有人知道编译器在处理SSE代码时优化了什么吗?
fild dword ptr [argc] // converting argc to floating point values
fstp dword ptr [esp+8]
fild dword ptr [esp+4] // the argc+i is done in the integer unit
fstp dword ptr [esp+0Ch]
fild dword ptr [esp+8]
fstp dword ptr [esp+18h]
fild dword ptr [esp+10h]
fstp dword ptr [esp+24h] // array a now initialised
fld dword ptr [esp+8] // load a[0]
fld1 // load 1 into FPU
fadd st(1),st // increment a[0]
fxch st(1)
fstp dword ptr [esp+14h] // save a[0]
fld dword ptr [esp+1Ch] // load a[1]
fadd st,st(1) // increment a[1]
fstp dword ptr [esp+24h] // save a[1]
fld dword ptr [esp+28h] // load a[2]
fadd st,st(1) // increment a[2]
fstp dword ptr [esp+28h] // save a[2]
fadd dword ptr [esp+2Ch] // increment a[3]
fstp dword ptr [esp+2Ch] // save a[3]
SSE FPU
4xfloat write 1xfloat read
1xsse read 1xfloat write
1xsse read+add 1xfloat read
1xsse write 1xfloat write
1xfloat read
1xfloat write
1xfloat read
1xfloat write
total
8 float reads 4 float reads
8 float writes 4 float writes