When using a 64-bit sized struct, the following code snippet
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 8)]
unsafe struct BUF
{
}
((BUF*)dst) = *((BUF*)src);
Produces
IL_0046: nop
IL_0047: ldloc.s dst
IL_0049: ldloc.2
IL_004a: ldobj MyClass/BUF
IL_004f: stobj MyClass/BUF
However, when just using a long, the following code produces
*((long*)dst) = *((long*)src);
Produces:
IL_0046: nop
IL_0047: ldloc.s dst
IL_0049: ldloc.2
IL_004a: ldind.i8
IL_004b: stind.i8
Does anyone know what difference ldobj/stobj and ldind.i8/stind.i8 makes, if any, for this example?
ldobj/stobj seems to give a 20% performance improvement, but I cannot figure out why that is happening. Aren't these two lines doing the exact same thing?
Thanks!
edit: [64-bit release mode] The bytecode looks the same when compiled in release mode. The performance measurement was done a while ago in release mode.
I've replicated the two distinct methods you're using, and see the same IL being generated, however the jitted code for the two is absolutely identical when run in Release mode:
Here's the test method I used:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Test
{
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 8)]
unsafe struct BUF
{
}
static class Program
{
static void Main()
{
BUF x, y, z;
unsafe
{
Do1(&x, &y);
Do2(&y, &z);
}
// Readline here to allow attaching debugger and dumping jitted code
Console.ReadLine();
}
// Disable inlining to permit easier identification of the code
[MethodImpl(MethodImplOptions.NoInlining)]
unsafe static void Do1(BUF* src, BUF* dst)
{
*((BUF*)dst) = *((BUF*)src);
}
// Disable inlining to permit easier identification of the code
[MethodImpl(MethodImplOptions.NoInlining)]
unsafe static void Do2(BUF* src, BUF* dst)
{
*((long*)dst) = *((long*)src);
}
}
}
The IL for the two methods match yours:
Do1
:
IL_0000: ldarg.1 IL_0001: ldarg.0 IL_0002: ldobj Test.BUF IL_0007: stobj Test.BUF IL_000c: ret
Do2
:
IL_0000: ldarg.1 IL_0001: ldarg.0 IL_0002: ldind.i8 IL_0003: stind.i8 IL_0004: ret
And dumping the jitted code:
Do1
:
Test.Program.Do1(Test.BUF*, Test.BUF*) Begin 000007ff00170190, size 7 000007ff`00170190 488b01 mov rax,qword ptr [rcx] 000007ff`00170193 488902 mov qword ptr [rdx],rax 000007ff`00170196 c3 ret
Do2
:
Test.Program.Do2(Test.BUF*, Test.BUF*) Begin 000007ff001701b0, size 7 000007ff`001701b0 488b01 mov rax,qword ptr [rcx] 000007ff`001701b3 488902 mov qword ptr [rdx],rax 000007ff`001701b6 c3 ret
They look pretty identical to me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With