我有两种情况:
static void CreateCopyOfString()
{
string s = "Hello";
ProcessString(s);
}
和
static void DoNotCreateCopyOfString()
{
ProcessString("Hello");
}
这两种情况的IL看起来像这样:
.method private hidebysig static void CreateCopyOfString() cil managed
{
// Code size 15 (0xf)
.maxstack 1
.locals init ([0] string s)
IL_0000: nop
IL_0001: ldstr "Hello"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: call void ConsoleApplication1.Program::ProcessString(string)
IL_000d: nop
IL_000e: ret
} // end of method Program::CreateCopyOfString
和
.method private hidebysig static void DoNotCreateCopyOfString() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello"
IL_0006: call void ConsoleApplication1.Program::ProcessString(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::DoNotCreateCopyOfString
在第一种情况下,会额外调用string init
,stloc.0
和ldloc.0
。
这是否意味着第一种情况比第二种情况更弱,其中字符串直接传递给方法而不是首先将其存储在局部变量中?
我看到了问题Does initialization of local variable with null impacts performance?,但它似乎与我在这里需要知道的有点不同。感谢。
答案 0 :(得分:11)
你一直在看未经优化的IL,因为所有的" nop" s。您可能在构建发布版本时发现它生成了不同的代码。
即使使用未经优化的版本,如果您在优化的JIT下运行,我也希望它最终使用相同的JITted代码。
即使有一个非优化的JIT 实际生成的代码在每次调用时都做了更多工作,我也会错开,看看这对任何实际应用都有重大影响
一如既往:
答案 1 :(得分:1)
不,它不会影响性能。
您可以通过验证为两者生成的机器代码是否相同来确认这一点。
请注意,在优化的JIT中,ProcessString可能是内联的。为避免这种情况,您可以添加[MethodImpl(MethodImplOptions.NoInlining)]
。
编译优化(发布)版本。
sxe ld clrjit
以在加载clrjit.dll时中断。输入g
继续,直到休息。.loadby sos clr
加载SOS。请注意,对于早期的CLR版本,您需要使用mscorwks而不是clr。!name2ee * <full class name>
查找方法表的地址。!dumpmt -md <address of MethoTable>
以转储方法详细信息。请注意,此时CreateCopyOfString和DoNotCreateCopyOfString尚未进行JIT。!bpmd <full class name>.CreateCopyOfString
和!bpmd <full class name>.DoNotCreateCopyOfString
以在调用方法时中断。输入g
继续。也可以使用!bpmd -md <address of MethodDesc>
来设置断点。!u <address of MethodDesc>
以转储该方法的机器代码。请注意,当我尝试这个时,只有一个方法是JITed,大概是因为运行时确定两个方法是相同的并且JITing另一个是不必要的。因此,我在适当的时候注释掉了这个电话,并重复了一下以获取机器代码。
实际的寄存器和地址会有所不同,但这两种方法都产生了以下机器代码:
sub rsp,28h
mov rcx,121E3258h
mov rcx,qword ptr [rcx]
call 000007fe`9852c038
nop
add rsp,28h
ret
因此,您可以得出结论,由于正在执行相同的机器代码,因此任一方法的性能都是相同的。