Why is Math.DivRem so inefficient?

Grrr. The only reason for this function to exist is to take advantage of the CPU instruction for this, and they didn't even do it!


Wow, that really looks stupid, doesn't it?

The problem is that -- according to the Microsoft Press book ".NET IL Assembler" by Lidin -- the IL rem and div atithmetic instructions are exactly that: compute remainder and compute divisor.

All arithmetical operations except the negation operation take two operands from the stack and put the result on the stack.

Apparently, the way the IL assembler language is designed, it's not possible to have an IL instruction that produces two outputs and pushes them onto the eval stack. Given that limitation, you can't have a division instruction in IL assembler that computes both the way the x86 DIV or IDIV instructions do.

IL was designed for security, verifiability, and stability, NOT for performance. Anyone who has a compute-intensive application and is concerned primarily with performance will be using native code and not .NET.

I recently attended Supercomputing '08, and in one of the technical sessions, an evangelist for Microsoft Compute Server gave the rough rule of thumb that .NET was usually half the speed of native code -- which is exactly the case here!.


If I had to take a wild guess, I'd say that whoever implemented Math.DivRem had no idea that x86 processors are capable of doing it in a single instruction, so they wrote it as two operations. That's not necessarily a bad thing if the optimizer works correctly, though it is yet another indicator that low-level knowledge is sadly lacking in most programmers nowadays. I would expect the optimizer to collapse modulus and then divide operations into one instruction, and the people who write optimizers should know these sorts of low-level things...


While .NET Framework 4.6.2 still uses the suboptimal modulo and divide, .NET Core (CoreCLR) currently replaces the divide with a subtract:

    public static int DivRem(int a, int b, out int result)
    {
        // TODO https://github.com/dotnet/runtime/issues/5213:
        // Restore to using % and / when the JIT is able to eliminate one of the idivs.
        // In the meantime, a * and - is measurably faster than an extra /.

        int div = a / b;
        result = a - (div * b);
        return div;
    }

And there's an open issue to either improve DivRem specifically (via intrinsic), or detect and optimise the general case in RyuJIT.