3

我有两种情况:

    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.0ldloc.0。这是否意味着第一种情况会比第二种情况下的表现要弱,后者将字符串直接传递给方法而不是首先将其存储在局部变量中?

我看到了问题是否初始化具有 null 的局部变量会影响性能?但这似乎与我在这里需要知道的有点不同。谢谢。

4

2 回答 2

11

一方面,您正在查看未优化的 IL - 因此所有“nop”。您可能会发现它在构建发布版本时会生成不同的代码。

即使使用未优化的版本,如果您在优化的 JIT 下运行,我希望它最终会得到相同的 JIT 代码。

即使有一个非优化的 JIT确实生成了每次调用它时做更多工作的代码,我也会惊讶地看到这对任何实际应用程序都有重大影响。

如从前:

  • 在开始之前设定绩效目标,并根据它们进行衡量
  • 找出哪些决策在以后的性能方面将难以修复,并且比这样的决策更担心这些决策,这些决策可以在以后更改而不会影响其他地方。
  • 编写最简单、最易读的代码。
  • 如果这还不够好,请调查是否做出损害可读性的更改有助于性能足以保证痛苦。
于 2013-03-22T20:58:56.897 回答
1

不,它不会影响性能。您可以通过验证为两者生成的机器代码是否相同来确认这一点。请注意,在优化的 JIT 中,ProcessString 可能是内联的。为避免这种情况,您可以添加[MethodImpl(MethodImplOptions.NoInlining)]. 编译优化(发布)构建。

  1. 在 WinDbg 中打开可执行文件。根据您的 EXE 使用匹配的 32 位或 64 位版本。
  2. sxe ld clrjit加载 clrjit.dll 时键入以中断。键入g以继续直到休息。
  3. 加载 SOS 与.loadby sos clr. 请注意,对于早期的 CLR 版本,您需要使用 mscorwks 而不是 clr。
  4. 用 查找方法表的地址!name2ee * <full class name>
  5. 键入!dumpmt -md <address of MethoTable>以转储方法详细信息。注意此时 CreateCopyOfString 和 DoNotCreateCopyOfString 尚未 JITed。
  6. 调用方法时键入!bpmd <full class name>.CreateCopyOfString!bpmd <full class name>.DoNotCreateCopyOfString中断。键入g以继续。也可以!bpmd -md <address of MethodDesc>用来设置断点。
  7. 命中断点时,键入!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

因此,您可能会得出结论,由于正在执行相同的机器代码,因此任何一种方法的性能都是相同的。

于 2013-03-23T02:05:11.447 回答