<div class="gmail_quote">On 7 August 2012 04:24, F i L <span dir="ltr"><<a href="mailto:witte2008@gmail.com" target="_blank">witte2008@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="im">Right now I'm working with DMD on Linux x86_64. LDC doesn't support SIMD right now, and I haven't built GDC yet, so I can't do performance comparisons between the two. I really need to get around to setting up GDC, because I've always planned on using that as a "release compiler" for my code.</div>

<br>
The problem is, as I mentioned above, that performance of SIMD completely get's shot when wrapping a float4 into a struct, rather than using float4 directly. There are some places where (like matrices), where they do make a big impact, but I'm trying to find the best solution for general code. For instance my current math library looks like:<br>

<br>
    struct Vector4 { float x, y, z, w; ... }<br>
    struct Matrix4 { Vector4 x, y, z, w; ... }<br>
<br>
but I was planning on changing over to (something like):<br>
<br>
    alias float4 Vector4;<br>
    alias float4[4] Matrix4;<br>
<br>
So I could use the types directly and reap the performance gains. I'm currently doing this to both my D code (still in early state), and our C# code for Mono. Both core.simd and Mono.Simd have "compiler magic" vector types, but Mono's version gives me access to component channels and simple constructors I can use, so for user code (and types like the Matrix above, with internal vectors) it's very convenient and natural. D's simply isn't, and I'm not sure there's any ways around it since again, at least with DMD, performance is shot when I put it in a struct.<br>
</blockquote><div><br></div><div>I'm not sure why the performance would suffer when placing it in a struct. I suspect it's because the struct causes the vectors to become unaligned, and that impacts performance a LOT. Walter has recently made some changes to expand the capability of align() to do most of the stuff you expect should be possible, including aligning structs, and propogating alignment from a struct member to its containing struct. This change might actually solve your problems...</div>
<div><br></div><div>Another suggestion I might make, is to write DMD intrinsics that mirror the GDC code in std.simd and use that, then I'll sort out any performance problems as soon as I have all the tools I need to finish the module :)</div>
<div>There's nothing inherent in the std.simd api that will produce slower than optimal code when everything is working properly.</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">Better to factor your code to eliminate any scalar work, and make sure<br>
'scalars' are broadcast across all 4 components and continue doing 4d<br>
operations.<br>
<br></div>
Instead of: @property pure nothrow float x(float4 v) { return v.ptr[0]; }<div class="im"><br>
Better to use: @property pure nothrow float4 x(float4 v) { return<br>
swizzle!"xxxx"(v); }<br>
</div></blockquote>
<br>
Thanks a lot for telling me this, I don't know much about SIMD stuff. You're actually the exact person I wanted to talk to, because you do know a lot about this and I've always respected your opinions.<br>
<br>
I'm not apposed to doing something like:<br>
<br>
    float4 addX(ref float4 v, float val)<br>
    {<br>
        float4 f;<br>
        f.x = val<br>
        v += f;<br>
    }<br></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
to do single component scalars, but it's very inconvenient for users to remember to use:<br>
<br>
    vec.addX(scalar);<br>
<br>
instead of:<br>
<br>
    vec.x += scalar;<br></blockquote><div><br></div><div>And this is precisely what I suggest you don't do. x64-SSE is the only architecture that can reasonably tolerate this (although it's still not the most efficient way). So if portability is important, you need to find another way.</div>
<div><br></div><div>A 'proper' way to do this is something like:</div><div><div>  float4 wideScalar = loadScalar(scalar); // this function loads a float into all 4 components. Note: this is a little slow, factor these float->vector loads outside the hot loops as is practical.</div>
<br class="Apple-interchange-newline"></div><div>  float4 vecX = getX(vec); // we can make shorthand for this, like 'vec.xxxx' for instance...</div><div>  vecX += wideScalar; // all 4 components maintain the same scalar value, this is so you can apply them back to non-scalar vectors later:</div>
<div><br></div><div>With this, there are 2 typical uses, one is to scale another vector by your scalar, for instance:</div><div>  someOtherVector *= vecX; // perform a scale of a full 4d vector by our 'wide' scalar</div>
<div><br></div><div>The other, less common operation, is that you may want to directly set the scalar to a component of another vector, setting Y to lock something to a height map for instance:</div><div>  someOtherVector = setY(someOtherVector, wideScalar); // note: it is still important that you have a 'wide' scalar in this case for portability, since different architectures have very different interleave operations.</div>
<div><br></div><div><br></div><div>Something like '.x' can never appear in efficient code.</div><div>Sadly, most modern SIMD hardware is simply not able to efficiently express what you as a programmer intuitively want as convenient operations.</div>
<div>Most SIMD hardware has absolutely no connection between the FPU and the SIMD unit, resulting in loads and stores to memory, and this in turn introduces another set of performance hazards.
</div><div>x64 is actually the only architecture that does allow interaction between the FPU and SIMD however, although it's still no less efficient to do it how I describe, and as a bonus, your code will be portable.</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">But that wouldn't be an issue if I could write custom operators for the components what basically did that. But I can't without wrapping float, which is why I am requesting these magic types get some basic features like that.<br>
</blockquote><div><br></div><div>See above.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
I'm wondering if I should be looking at just using inlined ASM and use the ASM SIMD instructions directly. I know basic ASM, but I don't know what the potential pitfalls of doing that, especially with portability. Is there a reason not to do this (short of complexity)? I'm also wondering why wrapping a core.simd type into a struct completely negates performance.. I'm guessing because when I return the struct type, the compiler has to think about it as a struct, instead of it's "magic" type and all struct types have a bit more overhead.<br>
</blockquote><div><br></div><div>Inline asm is usually less efficient for large blocks of code, it requires that you hand-tune the opcode sequencing, which is very hard to do, particularly for SSE.</div><div>Small inline asm blocks are also usually less efficient, since most compilers can't rearrange other code within the function around the asm block, this leads to poor opcode sequencing.</div>
<div>I recommend avoiding inline asm where performance is desired unless you're confident in writing the ENTIRE function/loop in asm, and hand tuning the opcode sequencing. But that's not portable...</div><div><br>
</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On a side note, DMD without SIMD is much faster than C# without SIMD, by a factor of 8x usually on simple vector types (micro-benchmarks), and that's not counting the runtimes startup times either. However, when I use Mono.Simd, both DMD (with core.simd) and C# are similar performance (see below). Math code with Mono C# (with SIMD) actually runs faster on Linux (even without the SGen GC or LLVM codegen) than it does on Window 8 with MS .NET, which I find to be pretty impressive and encouraging for our future games with Mono on Android (which has been out biggest performance PITA platform so far).<br>
</blockquote><div><br></div><div>Android? But you're benchmarking x64-SSE right? I don't think it's reasonable to expect that performance characteristics for one architectures SIMD hardware will be any indicator at all of how another architecture may perform.</div>
<div>Also, if you're doing any of the stuff I've been warning against above, NEON will suffer very hard, whereas x64-SSE will mostly shrug it off.</div><div><br></div><div>I'm very interested to hear your measurements when you try it out!</div>
<div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I've noticed some really odd things with core.simd as well, which is another reason I'm thing of trying inlined ASM. I'm not sure what's causing certain compiler optimizations. For instance, given the basic test program, when I do:<br>

<br>
    float rand = ...; // user input value<br>
<br>
    float4 a, b = [1, 4, -12, 5];<br>
<br>
    a.ptr[0] = rand;<br>
    a.ptr[1] = rand + 1;<br>
    a.ptr[2] = rand + 2;<br>
    a.ptr[3] = rand + 3;<br>
<br>
    ulong mil;<br>
    StopWatch sw;<br>
<br>
    foreach (t; 0 .. testCount)<br>
    {<br>
        sw.start();<br>
        foreach (i; 0 .. 1_000_000)<br>
        {<br>
            a += b;<br>
            b -= a;<br>
        }<br>
        sw.stop();<br>
        mil += sw.peek().msecs;<br>
        sw.reset();<br>
    }<br>
<br>
    writeln(a.array, ", ", b.array);<br>
    writeln(cast(double) mil / testCount);<br>
<br>
When I run this on my Phenom II X4 920, it completes in ~9ms. For comparison, C# Mono.Simd gets almost identical performance with identical code. However, if I add:<br>
<br>
    auto vec4(float x, float y, float z, float w)<br>
    {<br>
        float4 result;<br>
<br>
        result.ptr[0] = x;<br>
        result.ptr[1] = y;<br>
        result.ptr[2] = z;<br>
        result.ptr[3] = w;<br>
<br>
        return result;<br>
    }<br>
<br>
then replace the vector initialization lines:<br>
<br>
    float4 a, b = [ ... ];<br>
    a.ptr[0] = rand;<br>
    ...<br>
<br>
with ones using my factory function:<br>
<br>
    auto a = vec4(rand, rand+1, rand+2, rand+3);<br>
    auto b = vec4(1, 4, -12, 5);<br>
<br>
Then the program consistently completes in 2.15ms...<br>
<br>
wtf right? The printed vector output is identical, and there's no changes to the loop code (a += b, etc), I just change the construction code of the vectors and it runs 4.5x faster. Beats me, but I'll take it. Btw, for comparison, if I use a struct with an internal float4 it runs in ~19ms, and a struct with four floats runs in ~22ms. So you can see my concerns with using core.simd types directly, especially when my Intel Mac gets even better improvements with SIMD code.<br>

I haven't done extensive test on the Intel, but my original test (the one above, only in C# using Mono.Simd) the results for ~55ms using a struct with internal float4, and ~5ms for using float4 directly.<br></blockquote>
<div><br></div><div>wtf indeed! O_o</div><div><br></div><div>Can you paste the disassembly?<br></div><div>There should be no loads or stores in the loop, therefore it should be unaffected... but it obviously is, so the only thing I can imagine that could make a different in the inner loop like that is a change in alignment. Wrapping a vector in a struct will break the alignment, since, until recently, DMD didn't propagate aligned members outwards to the containing struct (which I think Walter fixed in 2.60??).</div>
<div><br></div><div>I can tell you this though, as soon as DMDs SIMD support is able to do the missing stuff I need to complete std.simd, I shall do that, along with intensive benchmarks where I'll be scrutinising the code-gen very closely.</div>
<div>I expect performance peculiarities like you are seeing will be found and fixed at that time...</div></div>