   SIMD code generation with VC++
  I've been reading a bit about how the Intel C++ compiler optimizes code with SSE/SSE2, and I thought I'd give it a shot in VC++, with P4 optimizations turned on.So I wrote:float a[4], b[4], c[4];for(int j = 0; j < 4; ++j)   c[j] = a[j] + b[j];Happily, it seems like VC++ was able to do the right thing:movss  xmm0,dword ptr [esp+0Ch] addss  xmm0,dword ptr [esp+0Ch] movss  dword ptr [esp+0Ch],xmm0 movss  xmm0,dword ptr [esp+10h] addss  xmm0,dword ptr [esp+10h] movss  dword ptr [esp+10h],xmm0 movss  xmm0,dword ptr [esp+14h] addss  xmm0,dword ptr [esp+14h] movss  dword ptr [esp+14h],xmm0 movss  xmm0,dword ptr [esp+18h] addss  xmm0,dword ptr [esp+18h] movss  dword ptr [esp+18h],xmm0I've also read about intrinsics a bit, not sure if they are specific to Intel C++ compiler or not... But in any case, this seems pretty awesome. Instead of spending lots of time writing processor-specific code which is time consuming and difficult to maintain, just organize the data structures and code properly and let the compiler do the rest. At most, maybe spend 2 minutes examining the generated assembly code just to make sure it's doing the right thing.However, obviously if you're making a game, you can't just compile with /arch:SSE2, or /G7 flags enabled since you need to support a range of machines. So, I was wondering, how would you take advantage of these optimizations for a commercial game? One idea I can think of is making several versions of DLLs, each compiled for a specific target, and swap in whichever one you need to use based on cpuid. That sounds like a lot of hassle though!Thanks in advance!   
Quote:Original post by KuroHappily, it seems like VC++ was able to do the right thing:No, it's producing horrible code!It's only processing a single element at a time instead of four floats in parallel.Was that the whole example? Did the compiler have any reason to suspect aliasing?Adding some alignment would probably fix it though.edit: There's something seriously weird with that assembly dump. It seems to be adding a single vector to itself.   ;  Well, you have to go pretty far back to lose SSE support (float operations are SSE only, so unless you start messing with doubles, you shouldn't even need SSE2).Athlon XP added SSE support. The Athlon's before XP didn't. So assuming SSE might be relatively safe (Depending on your game, of course. For something like Doom 3, I'd say it's safe. For a simple 2d turnbased game, less so)I wouldn't count on SSE2 though.However, keep in mind that the code snippet you just posted is pretty trivial. It doesn't take much before the compiler gets confused and gives up on SIMD.instructions.And of course, none of this really answers your question. [wink]Edit:Oops, just looked at the code.It's not doing SIMD. It just uses SSE registers for normal scalar floating point math. (All the ___SS instructions are for scalar. The SIMD versions are ___PS (Packed Single precision)Edit2: I wouldn't call it horrible code though. It's a hell of a lot more efficient than using x87 fp math... :)   ;  Oh lord! You're right, lol, it's not even doing it in a SIMD way. Yeah like you said though Spoonbender, it's still faster than x87...Anyway thanks, I'll go check that code over again and see if I can't trick VC++ into being smarter :)   ;Quote:Original post by SpoonbenderEdit2: I wouldn't call it horrible code though. It's a hell of a lot more efficient than using x87 fp math... :)It's a slight bit faster on the P4, but definitely not worth losing Athlon (pre-XP) compatibility for, assuming that FPU is in 32-bit mode.It might have performed better if the compiler had been intelligent enough interleave the instructions.   ;  It's done a better job than raw x87 code would've, but it's managed to use 3 or 4 times as many cycles as is necessary to perform this op.Such is the state of compiler autovectorization. Hopefully it'll get better as time goes on. (I wonder how the X360 guys are coping, and how well VC generates code for VMX128?)   ;Quote:Original post by doynaxQuote:Original post by SpoonbenderEdit2: I wouldn't call it horrible code though. It's a hell of a lot more efficient than using x87 fp math... :)It's a slight bit faster on the P4, but definitely not worth losing Athlon (pre-XP) compatibility for, assuming that FPU is in 32-bit mode.Depends on your target, of course. If you're making something like, say, MS Word, you definitely wouldn't want to lose that compatibility. But for a game that might get released 2 years from now? I don't see the problem. [wink]And it is usually quite a lot faster. x87 is horribly inefficient. Not because the actual instructions are slower, but because you get so many excess memory accesses. The number of push/pop ops is staggering...;Quote:Original post by SpoonbenderAnd it is usually quite a lot faster. x87 is horribly inefficient. Not because the actual instructions are slower, but because you get so many excess memory accesses. The number of push/pop ops is staggering...Faster? When an intelligent human being is involved in the code generation process, yes. But not when someone's just changed the compiler settings and is hoping for a free performance boost, at least not by much.In this case the x87 would've generated near-identical code.The real question is how the compiler could be smart enough optimize the three arrays into a single one (a += a) but not eliminate the operation completely.;  Ahh I can't seem to figure it out :) I did check the alignment, and the arrays were aligned to 8 bytes, so I added __declspec(align(16)) to them, but it's still generating the same code. I'm using VC++ .NET 2003, so maybe 2005 will be a little smarter. *shrug* Anyway thanks for the replies, at least I learned some things!   ;  Try disabling aliasing around the code block:#pragma optimize("a", off)And place the loop in an external function with aligned parameters.You could also try unrolling the loop manually and begin by caching the two input vectors in local variables.   