Followers 0

# How to profile SIMD

## 13 posts in this topic

Actually the topic name should be "How to profile code".

Well I am writing SIMD math library. I got 2 implementations SSE and scalar.

I'm not shure how measure the code speed. Currently Im not using optimization, and no debug symbols are generated for profiling.

I'm creating a loop that repeats the operation...

The compiler is cl

I'm expecting SSE dot product to be slower than scalar version?

But the cross product is also slower!?!@

SGE_FORCE_INLINE SGVector vec3_cross(const SGVector& a, const SGVector& b)
{
#if defined(SGE_MATH_USE_SSE)
__m128 T = _mm_shuffle_ps(a.m_M128, a.m_M128, SGE_SIMD_SHUFFLE(1, 2, 0, 3)); //(Y Z X 0)
__m128 V = _mm_shuffle_ps(b.m_M128, b.m_M128, SGE_SIMD_SHUFFLE(1, 2, 0, 3)); //(Y Z X 0)

//i(ay*bz - by*az)  + j(bx*az - ax*bz)  + k(ax*by - bx*ay)
T = _mm_mul_ps(T, b.m_M128);//bx * ay, by * az, bz * ax
V = _mm_mul_ps(V, a.m_M128);//ax * by, ay * bz, az * bx
V = _mm_sub_ps(V, T);

V = _mm_shuffle_ps(V, V, SGE_SIMD_SHUFFLE(1, 2, 0, 3));
return SGVector(V);
#else
const float x = (a.y*b.z) - (b.y*a.z);
const float y = (b.x*a.z) - (a.x*b.z);
const float z = (a.x*b.y) - (b.x,a.y);

return SGVector(x, y, z, 0.f);
#endif
}

where SGVector is struct with union{ struct {float x,y,z;}; float arr[4]; __m128 m_M128}. (maybe that is the problem?!)

EDIT : maybe __forceinline is involed too!? I will remove it.

Edited by imoogiBG
0

##### Share on other sites

Currently Im not using optimization

Ah? If you want to measure performance you need to compile using optimizations. at least /O2, otherwise every instruction will go through memory.

The best tool for profiling is Intel's VTune, they have 30 days free trial. Visual studio comes with a profiler you can use as well. If you want to profile yourself, you can use the timestamp counter (rdtcs instruction, visual studio has an intinsic).

__forceinline is very good for performance, I use it almost everywhere in performance critical code.

The problem is probably the shuffles. Not sure what CPU you are using, but shuffle performance is limited. You have 3 shuffles and 3 math instructions, which is not a good ratio. The SSE math instructions have data dependency on the shuffles, which will stall the CPU pipeline.

Also, your inputs and outputs are structs. Even though you pass them by reference, both shuffles and return values will got through memory (probably, need to look at the generated assembly).

To maximize SSE performance you need to design your whole code around it. Have long SSE math sequences, and reduce the amount of memory acceses and shuffles.

BTW, you can tell CL to use scalar SSE for floating-point instructions by using /arch:SSE. Check this.

Edited by satanir
0

##### Share on other sites

Pass vectors by value, not reference

Hi Rob,

Why do you recommend passing vectors by value rather than reference?

I've made it a habit to use const ref parameters wherever possible, and wherever ref makes sense
compared to the size of the type (I wouldn't pass a char by reference)

0

##### Share on other sites

If you'd ever care to go into depth about optimization work, consider writing an article for the site wink.png

Maybe I will

0

##### Share on other sites

Guys i was looking at the ASM and this i what i've got

//pure _128
__m128 a, b , c;
c = _mm_set_ps1(f);
movss       xmm1,dword ptr [esp+0Ch]
b = _mm_set_ps1(ff);
movss       xmm0,dword ptr [esp+10h]
shufps      xmm0,xmm0,0
shufps      xmm1,xmm1,0

/////////////////////////////////////////////////////
//__m128 as a member of a struct
SGVector v, v2, d;
d.m_M128 = _mm_set_ps1(f);
movss       xmm1,dword ptr [esp+0Ch]
v2.m_M128 = _mm_set_ps1(ff);
movss       xmm0,dword ptr [esp+10h]
shufps      xmm0,xmm0,0
shufps      xmm1,xmm1,0

?/////////////////////////////////////////////////////
//_m128 as a member of a struct. Calling a custom function
SGVector v, v2, d;
d.m_M128 = _mm_set_ps1(f);
movss       xmm1,dword ptr [esp+0Ch]
v2.m_M128 = _mm_set_ps1(ff);
movss       xmm0,dword ptr [esp+10h]
shufps      xmm0,xmm0,0
shufps      xmm1,xmm1,0

////////////////////////////////////////////
//retval is SGVector

SGVector v, v2, d;
d.m_M128 = _mm_set_ps1(f);
movss       xmm1,dword ptr [esp+0Ch]
v2.m_M128 = _mm_set_ps1(ff);
movss       xmm0,dword ptr [esp+10h]
shufps      xmm0,xmm0,0
shufps      xmm1,xmm1,0
SGE_FORCE_INLINE void vec3_add2(const SGVector& a, const SGVector& b, SGVector& c)
{
#if defined(SGE_MATH_USE_SSE)
#endif
}
Refs, retvals do not change anything. For add ofc. Tomorrow i will try the cross product.

CL x86 O2

PS:
shufps      xmm0,xmm0,0

shufps      xmm1,xmm1,0

Why shufps is needed?

PS 2:

SuperVGA, on 24 Oct 2013 - 2:11 PM, said:

Pass vectors by value, not reference

Hi Rob,

Why do you recommend passing vectors by value rather than reference?

I've made it a habit to use const ref parameters wherever possible, and wherever ref makes sense
compared to the size of the type (I wouldn't pass a char by reference)

if the function is inlined then the refs do not change anything.

Edited by imoogiBG
0

##### Share on other sites

Pass vectors by value, not reference

Hi Rob,

Why do you recommend passing vectors by value rather than reference?
I've made it a habit to use const ref parameters wherever possible, and wherever ref makes sense
compared to the size of the type (I wouldn't pass a char by reference)

As of C++11, the new rule of thumb is to pass vectors and other objects like it by value when the operation performed on them requires a copy, otherwise pass by const ref. The rule for passing by reference is still the same. The reason is that this can enable move semantics when the arguments passed are rvalues. With virtual functions, the rule becomes when the operation logically needs a copy (even though every overload may not).

EDIT: that's std::vector. SIMD vectors that your architecture natively supports are basically primitive types since they can fit into a single register by definition. Edited by King Mir
2

##### Share on other sites

shufps xmm0,xmm0,0

shufps xmm1,xmm1,0

Why shufps is needed?

That's what _mm_set_ps1() - broadcast a single float value into all of the __m128 components. Has to be movss and shufps.

Refs, retvals do not change anything

This because of the inlining. You usually don't need to use reference for __m128. I use references for __m128 only when passing more than 3 vectors to a function - no calling convention support that, and due to stack alignemnt you can't pass a vector on stack.

0

## Create an account

Register a new account

Followers 0

• 11
• 19
• 14
• 23
• 11