Should SSE data types be passed around or created for each operation?
c++, sse
Solution
You should avoid the second version like the plague because if all of your small/primitive operations each have load/store instructions the overall expression using those operations is going to be dwarfed with the overhead of load/store instructions and completely outweigh the actual work to be done.
All of your vector operations/functions should be written in a way that assumes and enforces only parameters that are already loaded into sse registers and only deals with those. load/store operations should be explicitly written outside of the context of those functions controlled in a such away that you only need to do it once per iteration of a loop or very infrequently.
Also what Mystical was trying to point out is when you access individual elements of SSE intrinsic type those cause load/store instructions to be generated so again you should avoid accessing/modifying individual elements. Pay attention to the assembly generated.
For vec2/3 I would just make them strong type aliases for vec4 and zero-out the other components when first created. SSE also have variants of most operations that work on just the first component so that is another thing worth keeping in mind.
To get the most throughput out of SSE you need to be dealing with SoA, hybrid SoA-AoS or do on the fly swizzling/shuffling into SoA form.
check out this video.
Problem
I'm currently trying my hand at making my own C++ vector math library and I'm interested in optimizing it with SSE. For my vec2 and vec3 data types I can't store the __m128 type directly since they have to be their expected sizes, but what about vec4? Suppose my vec4 type looks something like this (ignoring 16-byte alignment requirement for simplicity of discussion): ``` union vec4 { struct {float x, y, z, w;}; __m128 sse; } vec4 operator+(const vec4& left, const vec4& right) { vec4 result; result.sse = _mm_add_ps(left.sse, right.sse); return result; } ``` Is that the suggested way to do it or is there some big reason not to I can't think of? I.e., should I do this instead: ``` struct vec4 { float x, y, z, w; }; vec4 operator+(const vec4& left, const vec4& right) { __m128 leftSSE = _mm_load_ps(reinterpret_cast<const float*>(&left)); __m128 rightSSE = _mm_load_ps(reinterpret_cast<const float*>(&right)); __m128 resultSSE = _mm_add_ps(leftSSE, rightSSE); vec4 result; _mm_store_ps(reinterpret_cast<float*>(&result), resultSSE); return result; } ``` And while we're at it, what about my theoretical vec2 and vec3 types? Would it be faster to convert them to vec4 first and then use SIMD instructions or just handle their scalar elements individually?