Skip to main content
GameDev.net gamedev.net
🔒 Locked

inline ASM question

Started by CastorX Jul 3, 2009 at 4:53 PM 9 replies 1.4k views
Original Post
CastorX
CastorX
Hy! I've written this member function for SSE-accelereted matrix-matrix mutiplication: 1, UINL void Matrix4A::Mul_SSE_v2(const Matrix4A& m1, const Matrix4A& m2) 2, { 3, if(this == &m1) 4, { 5, Matrix4A tmp(m1); 6, __asm 7, { 8, lea eax, dword ptr [tmp] 9, mov ecx, dword ptr [m2] 10, } 11, } else 12, { 13, __asm 14, { 15, mov eax, dword ptr [m1] 16, mov ecx, dword ptr [m2] 17, } . } . __asm . { . //---------PART-0--------// . movaps xmm0, [eax + m00]; . ... . ... . ... . } .} This functions multiplies two matrices m1 and m2 and stores the result in *this. The problem becomes real when "m1 is (*this)". Since m1 and the result is the same matrix, I need to create a temporary matrix to store the original matrix m1 (before I overwrite it). My question is: Is it a bad thing to declare a variable tmp (line 5) inside the if(...) segment, save it's address and reuse it later OUTSIDE the if() segment? Or is it the better way to copy-paste my Multiplyes ASM code after booth versions of the loader code (line 3-17)? The code works fine; and runs about 3.1x times faster than normal C++ code without SSE.
trbot
trbot
I don't think you should keep using that address. Once the if block is left, that space is freed to be overwritten by who knows what (any newly spawned interrupting process). I think a run through valgrind would expose this...
Matt_D
Matt_D
lets look at this a little differently
why the heck would anyone do

MatrixA->Mul(MatrixA,MatrixB);

why not provide a simple function which allows you to multiply the current matrix instance by another matrix.

MatrixA->Mul(MatrixB)

this way, its clear that the result is the instance.

if your clever, you could implement both functions using a simple inline function which doesnt belong to the class instance, and multiplies two matricies together and returns the value. Also, you dont actually need inline ASM to do simple things like multiplying matricies together, your compiler should have a "generate SSE instructions" which will more than likely do the same thing for you.


your never as good as they say you were, never as bad as they say you was.
phresnel
phresnel
You shouldn't use inline assembler when there are intrinsics.

Further, in Microsoft's Compiler, every block of inline assembly is a blackbox to the compiler, effectively meaning that you lose a lot of optimisations around and inside the block of inline assembly, because there's no way in MSVC to tell the compiler about clobbered registers and dependencies on certain variables. AFAIR, you even lose the ability to let the compiler inline your function.

Furthermore, Microsoft's Compiler does not have an inline assembler on 64bit targets.

Sidenote: Using inline assembler in GCC is generally a braindead approach, too, because the compiler has probably more experience in optimisation than most human lifes. But if you are a assembler wizard, you can use it, as GCC intermixes your assembler with the rest of the code. There, even the order of assembly instructions may change drastically, and some of your assembly might even be folded away thanks to CSE, inlining, unrolling, etc.. All that thanks to constrains.
Matt_D
Matt_D
Quote:
Original post by phresnel
You shouldn't use inline assembler when there are intrinsics.

Further, in Microsoft's Compiler, every block of inline assembly is a blackbox to the compiler, effectively meaning that you lose a lot of optimisations around and inside the block of inline assembly, because there's no way in MSVC to tell the compiler about clobbered registers and dependencies on certain variables. AFAIR, you even lose the ability to let the compiler inline your function.

Furthermore, Microsoft's Compiler does not have an inline assembler on 64bit targets.

Sidenote: Using inline assembler in GCC is generally a braindead approach, too, because the compiler has probably more experience in optimisation than most human lifes. But if you are a assembler wizard, you can use it, as GCC intermixes your assembler with the rest of the code. There, even the order of assembly instructions may change drastically, and some of your assembly might even be folded away thanks to CSE, inlining, unrolling, etc.. All that thanks to constrains.


i thought the compiler still cached its state to stack before executing your ASM even with GCC.
either way, i totally agree with the intrinsics approach.

and lets not forget that, 99 times out of 100, the compiler will optimise things far better than you ever will*.

*unless your name is michael abrash.

your never as good as they say you were, never as bad as they say you was.
CastorX
CastorX
I have boot (m0.Mul(m1,m2) and m0.Mul(m1)) versions of the function. By the way, m0.Mul(m0,m1) means m0 = m0 * m1. This is just a speed test. I want to find the fastest mutrix-matrix multiplication function.

The inline version runs 3.2 times faster than the original compiler optimized and inline C++ version. When I remove the inline ("UINL") keyword the speedup reduces to 2.8x. This is slower, I think.

I know that the speed and compiler optimalization also depends on the sorrunding code.

[Edited by - CastorX on July 4, 2009 3:04:13 AM]
Matt_D
Matt_D
Quote:
Original post by CastorX
I have boot (m0.Mul(m1,m2) and m0.Mul(m1)) versions of the function. By the way, m0.Mul(m0,m1) means m0 = m0 * m1. This is just a speed test. I want to find the fastest mutrix-matrix multiplication function.

The inline version runs 3.2 times faster than the original compiler optimized and inline C++ version. When I remove the inline ("UINL") keyword the speedup reduces to 2.8x. This is slower, I think.

I know that the speed and compiler optimalization also depends on the sorrunding code.


you did turn on the "enable SSE instructions" in the compiler flags right? (
and of course, rule 1 in profiling is, is this function actually a bottleneck?

anyway, i suggest reading http://blogs.msdn.com/xiangfan/archive/2009/04/28/optimize-your-code-matrix-multiplication.aspx

and use the damn intrinsics :)
your never as good as they say you were, never as bad as they say you was.
samoth
samoth
Quote:
Original post by phresnel
Further, in Microsoft's Compiler, every block of inline assembly is a blackbox to the compiler, effectively meaning that you lose a lot of optimisations around and inside the block of inline assembly
Quoted for truth, not only for Microsoft.

asm("") is the canonical way under gcc to prevent a function being eliminated, for example by CSE. The compiler can still inline functions containing inline assembly though, and will perform instruction scheduling, some register optimisations, and some opcode replacements unless you tell it not to do it (__asm__ __volatile__).
Using intrinsic functions, it will perform full optimisations on the whole code.
__asm__ __volatile__("") is a full barrier too, i.e. the compiler cannot even move instructions across the statement within the same function.
CastorX
CastorX
Thanks for the comments! xiangfan's website looks very useful!
outRider
outRider
If everyone followed the conventional 'wisdom' around here no one would ever learn assembly, they'd be scared shitless of this terrible bogeyman that the Gamedev sages give dire warnings against every other day! Anyway...

CastorX, practically speaking it will usually work, as you found out, because even though the variable goes out of scope no sane compiler actually removes anything from the stack until the end of the function under normal circumstances.

However, don't do it. If you're compiler ever decided to reuse stack slots for variables whose live ranges didn't overlap you'd probably run into nasty bugs, although this kind of optimization might be restricted by the ABIs of some platforms.

Declare tmp outside the block and assign m1 to it inside the block as well as storing its address in eax to make sure it's in scope later.
CastorX
CastorX
I've already made the safe version. Thanks! It is inlined, asm, safe and fast.
Matt_D
Matt_D
Quote:
Original post by outRider
If everyone followed the conventional 'wisdom' around here no one would ever learn assembly, they'd be scared shitless of this terrible bogeyman that the Gamedev sages give dire warnings against every other day! Anyway...


hah, learning how to read assembly is far more important than learning how to write it ;)
i just love those release only crashes :)
your never as good as they say you were, never as bad as they say you was.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.