局部字符串变量的实例化是否会影响性能

本文关键字:影响 性能 是否 实例化 字符串 变量 局部 | 更新日期: 2023-09-27 18:05:27

我有两种情况:

    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影响性能?但它似乎和我需要知道的有点不同。谢谢。

局部字符串变量的实例化是否会影响性能

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

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

即使使用非优化JIT (所做的实际上生成的代码在每次调用时都做了更多的工作),我也会惊讶地看到这在任何实际应用程序中都有显著的影响。一如既往的

:

  • 在开始之前设定性能目标,并根据进行测量。
  • 找出哪些决策在以后的性能方面很难修复,并担心这些决策,而不是像这样的决策,这些决策可以在以后更改而不会影响其他地方。
  • 编写最简单,最易读的代码,将首先工作
  • 如果不能很好地执行,请调查是否做出损害可读性的更改足以帮助实现性能。

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

    在WinDbg中打开可执行文件。根据您的EXE使用匹配的32或64位版本。
  1. 类型sxe ld clrjit在clrjit.dll加载时中断。输入g直到中断。
  2. 加载SOS与.loadby sos clr。注意,对于早期的CLR版本,您需要使用mscorwks而不是CLR。
  3. 使用!name2ee * <full class name>查找方法表地址
  4. 输入!dumpmt -md <address of MethoTable>转储方法详细信息。注意,此时CreateCopyOfString和DoNotCreateCopyOfString还没有被合并。
  5. 类型!bpmd <full class name>.CreateCopyOfString!bpmd <full class name>.DoNotCreateCopyOfString在调用方法时断开。输入g继续。也可以使用!bpmd -md <address of MethodDesc>设置断点。
  6. 当遇到断点时,输入!u <address of MethodDesc>来转储该方法的机器码。

注意,当我尝试这样做时,只有一个方法被jit,可能是因为运行时确定这两个方法是相同的,而jit另一个方法是不必要的。因此,我适当地注释掉了调用,并重复以获得机器码。

实际的寄存器和地址会有所不同,但这两种方法都会得到以下机器码:

sub     rsp,28h
mov     rcx,121E3258h
mov     rcx,qword ptr [rcx]
call    000007fe`9852c038
nop
add     rsp,28h
ret

因此,您可以得出结论,由于执行的是相同的机器码,因此两种方法的性能将是相同的。