LoginSignup
9
8

More than 5 years have passed since last update.

引数で既存メソッドを渡した場合とlambdaで渡した場合でGCAllocに差がでた

Last updated at Posted at 2017-10-10

問題

Unityで、次の2つのコードでGCAllocが発生するものしないものとで差がでたので調査する。コードはプロファイラーで見やすいように毎フレーム処理が走るようにしている。

1 毎フレームGCAllocが発生しないコード

    void Update()
    {
        Method1(() => {});
    }

    void Method1(Action action)
    {
    }

2 毎フレームGCAllocが発生するコード

    void Update()
    {
        Method1(Method2);
    }

    void Method1(Action action)
    {
    }

    void Method2()
    {
    }

プロファイリングの結果

1 の結果

profiler1.png

2 の結果
profiler2.png

1の結果では GC Alloc が0だが、2の結果は104B GC Alloc が発生しているのがわかる。

ILを比較する

それぞれのコードでできるILを比較する。必要なところだけ抜粋

1 のコード

  .class public auto ansi beforefieldinit GCAllocTest
        extends [UnityEngine]UnityEngine.MonoBehaviour
  {
    .field  private static  class [mscorlib]System.Action '<>f__am$cache0'

    ...

    // method line 2
    .method private hidebysig 
           instance default void Update ()  cil managed 
    {
        // Method begins at RVA 0x2058
        // Code size 37 (0x25)
        .maxstack 8
        IL_0000:  nop 
        IL_0001:  ldarg.0 
        IL_0002:  ldsfld class [mscorlib]System.Action GCAllocTest::'<>f__am$cache0'
        IL_0007:  brtrue.s IL_001a

        IL_0009:  ldnull 
        IL_000a:  ldftn void()
        IL_0010:  newobj instance void class [mscorlib]System.Action::'.ctor'(object, native int)
        IL_0015:  stsfld class [mscorlib]System.Action GCAllocTest::'<>f__am$cache0'
        IL_001a:  ldsfld class [mscorlib]System.Action GCAllocTest::'<>f__am$cache0'
        IL_001f:  call instance void class GCAllocTest::Method1(class [mscorlib]System.Action)
        IL_0024:  ret 
    } // end of method GCAllocTest::Update

2 のコード

    // method line 2
    .method private hidebysig 
           instance default void Update ()  cil managed 
    {
        // Method begins at RVA 0x2058
        // Code size 20 (0x14)
        .maxstack 8
        IL_0000:  nop 
        IL_0001:  ldarg.0 
        IL_0002:  ldarg.0 
        IL_0003:  ldftn instance void()
        IL_0009:  newobj instance void class [mscorlib]System.Action::'.ctor'(object, native int)
        IL_000e:  call instance void class GCAllocTest::Method1(class [mscorlib]System.Action)
        IL_0013:  ret 
    } // end of method GCAllocTest::Update

2のコードから先に見ていく。2のコードの方は処理の中で毎回Actionをnewしている。そのため
GC Alloc を発生させている。
1のコードの方は lambda で定義されたコードが静的フィールドで保存されており、二回目以降はその保存されたコードが使われるようになっていた。

ということは最初の一回目は同じく1のコードも同じ量だけ GC Alloc が発生してたりするのかな。なにわともあれ原因がわかったのでよしとする。

環境

Unity5.6.3p3
macOS Sierra 10.12.6

9
8
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8