Original Post
I've been trying to optimize my code by using SSE intrinsic intructions in C++ in order to generate four 3D Perlin noise values at a time. Creating the SSE version was relatively easy, but unfortunately performance is extremely disappointing: the SSE code is 5 to 7 times slower than the standard one. Okay, it does calculate 4 values at a time, but since it's 5-7 times slower, overall there are no performance benefits (quite the contrary), while I was expecting it to be 2 to 3 times faster. The following code uses a few helper functions which I placed in a separate file for reference: http://fl-tw.com/opengl/sselib.h To track the problem, I simplified the code as much as I could to replicate it on as little code as possible. First of all, the C/C++ version:
float test(const SVec3D& xyz)
{
const int xtr = MFloatToInt(xyz.x - 0.5f);
const int ytr = MFloatToInt(xyz.y - 0.5f);
const int ztr = MFloatToInt(xyz.z - 0.5f);
const float x = xyz.x - xtr;
const float y = xyz.y - ytr;
const float z = xyz.z - ztr;
return x + y + z;
}
Now the SSE version:
SVec4D testSSE(const SVec3D *xyz)
{
C_SIMD_ALIGN16 float valX[4] = {xyz[0].x, xyz[1].x, xyz[2].x, xyz[3].x};
C_SIMD_ALIGN16 float valY[4] = {xyz[0].y, xyz[1].y, xyz[2].y, xyz[3].y};
C_SIMD_ALIGN16 float valZ[4] = {xyz[0].z, xyz[1].z, xyz[2].z, xyz[3].z};
C_SIMD_ALIGN16 SIMDVec4F xyzX = SIMD_Vec4F_Load(valX);
C_SIMD_ALIGN16 SIMDVec4F xtrf = SIMD_Vec4F_Sub(xyzX, SIMD_Vec4F_SetScalar(0.5f));
C_SIMD_ALIGN16 SIMDVec4I xtr = SIMD_Vec4F_To_Vec4I_Round(xtrf);
xtrf = SIMD_Vec4I_To_Vec4F(xtr);
C_SIMD_ALIGN16 SIMDVec4F x = SIMD_Vec4F_Sub(xyzX, xtrf);
xyzX = SIMD_Vec4F_Load(valY);
xtrf = SIMD_Vec4F_Sub(xyzX, SIMD_Vec4F_SetScalar(0.5f));
xtr = SIMD_Vec4F_To_Vec4I_Round(xtrf);
xtrf = SIMD_Vec4I_To_Vec4F(xtr);
x = SIMD_Vec4F_Add(x, SIMD_Vec4F_Sub(xyzX, xtrf));
xyzX = SIMD_Vec4F_Load(valZ);
xtrf = SIMD_Vec4F_Sub(xyzX, SIMD_Vec4F_SetScalar(0.5f));
xtr = SIMD_Vec4F_To_Vec4I_Round(xtrf);
xtrf = SIMD_Vec4I_To_Vec4F(xtr);
x = SIMD_Vec4F_Add(x, SIMD_Vec4F_Sub(xyzX, xtrf));
C_SIMD_ALIGN16 SVec4D res;
C_SIMD_Vec4F_Store(&res.x, x);
return res;
}
I tried a lot of ideas: - making sure all the data is 16-bits aligned, no change - reordering the instructions, groupping them by x/y/z, no change - using explicit temporaries, reusing them for constants, no change Everything I try is, at best, 4 times slower than the standard version, meaning (after taking into account the fact that it calculates 4 values at the same time) that results cancel out, and there's just no performance gain. If anybody has good knowledge about SSE performance, advices/tips/tricks are welcome.. Y.