3

私には2つの状況があります:

    static void CreateCopyOfString()
    {
        string s = "Hello";
        ProcessString(s);
    }

    static void DoNotCreateCopyOfString()
    {
        ProcessString("Hello");
    }

これら 2 つの状況の 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ます。これは、文字列が最初にローカル変数に格納されるのではなく、直接メソッドに渡される 2 番目のケースよりも、最初のケースのパフォーマンスが低いことを意味しますか?

質問を見ましたnull を使用したローカル変数の初期化はパフォーマンスに影響しますか? しかし、ここで知る必要があることとは少し違うようです。ありがとう。

4

2 に答える 2

11

1 つの理由として、最適化されていない IL を見ているため、すべての「nop」があります。リリース バージョンをビルドすると、異なるコードが生成されることがあります。

最適化されていないバージョンでも、最適化 JIT で実行している場合は、同じ JITted コードになると思います。

これが呼び出されるたびにより多くの作業を行うコードを実際に生成した非最適化 JIT を使用しても、これが実際のアプリケーションに重大な影響を与えることを確認すると、私はよろめきます

いつものように:

  • 開始する前にパフォーマンス目標を設定し、それに対して測定します。
  • パフォーマンスの観点から、後で修正するのが難しい決定を見つけ出し、他の場所に影響を与えずに後で変更できるこのような決定よりもはるかに心配してください。
  • 最初に機能する、最も単純で最も読みやすいコードを記述します。
  • それでも十分なパフォーマンスが得られない場合は、読みやすさを損なうような変更を加えることで、パフォーマンスが改善され、その苦痛を正当化できるかどうかを調査します。
于 2013-03-22T20:58:56.897 に答える
1

いいえ、パフォーマンスには影響しません。これは、両方に対して生成されたマシン コードが同じであることを確認することで確認できます。最適化された JIT では、ProcessString がインライン化される場合があることに注意してください。これを避けるために、 を追加することができ[MethodImpl(MethodImplOptions.NoInlining)]ます。最適化された (リリース) ビルドをコンパイルします。

  1. WinDbg で実行可能ファイルを開きます。EXE に応じて、一致する 32 または 64 ビット バージョンを使用します。
  2. sxe ld clrjitclrjit.dll がロードされたときにブレークするタイプ。休憩gまで続けるには入力してください。
  3. で SOS をロードし.loadby sos clrます。以前の CLR バージョンでは、clr の代わりに mscorwks を使用する必要があることに注意してください。
  4. でメソッド テーブルのアドレスを検索します!name2ee * <full class name>
  5. タイプ!dumpmt -md <address of MethoTable>してメソッドの詳細をダンプします。この時点で、CreateCopyOfString と DoNotCreateCopyOfString はまだ JIT されていないことに注意してください。
  6. メソッドが呼び出されたときにブレークするには!bpmd <full class name>.CreateCopyOfStringandを入力します。!bpmd <full class name>.DoNotCreateCopyOfString入力gして続行します。!bpmd -md <address of MethodDesc>ブレークポイントの設定にも使用できます。
  7. ブレークポイントに到達したら、タイプ!u <address of MethodDesc>してメソッドのマシン コードをダンプします。

これを試したとき、メソッドの 1 つだけが JIT されたことに注意してください。これはおそらく、ランタイムが 2 つのメソッドが同一であり、もう一方の JIT が不要であると判断したためです。そのため、必要に応じて呼び出しをコメントアウトし、マシンコードを取得するために繰り返しました。

実際のレジスタとアドレスは異なりますが、どちらの方法でも次のマシン コードが生成されます。

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 に答える