LoginSignup
7
5

More than 5 years have passed since last update.

リストをソートするときにクロージャの代わりに IComparer を渡して GC Alloc を抑える

Last updated at Posted at 2017-12-03

問題

  • リストをソートするときに、外部の変数を参照してソートする必要がある
  • List.Sort に比較関数としてクロージャを渡すと GC Alloc が発生するのをどうにかしたい(ソートの頻度がそこそこあるため)

はじめに、リストをソートするときに GC Alloc が発生していることを、Unity のプロファイラで確認します。

var list = new List<int> { 1, 2, 5, 6, 10, 11, 12 };

_button1.onClick.AddListener(() =>
{
    Profiler.BeginSample("GCAllocCheck: sort(closure)");
    int p = 6;
    // list を p との距離が近い順にソートしたい
    list.Sort((a, b) => Math.Abs(a - p).CompareTo(Math.Abs(b - p)));
    Profiler.EndSample();

    Observable.NextFrame(FrameCountType.EndOfFrame).Subscribe(_ => EditorApplication.isPaused = true);
});

実行すると 104 B のゴミが発生していることが確認できます。

スクリーンショット_2017-12-10_15_48_59.png

対応

IComparer インターフェースを実装したインスタンスを予め作成しておいて、そのインスタンスを使い回すようにします。

class ListComparer : IComparer<int>
{
    // p を元にしてソートしたい
    public int P { get; set; }

    public ListComparer()
    {
        P = 0;
    }

    public int Compare(int a, int b)
    {
        return Math.Abs(a - P).CompareTo(Math.Abs(b - P));
    }
}

上のような ListComarer を作っておき、これを List.Sort に渡します。

var list = new List<int> { 1, 2, 5, 6, 10, 11, 12 };
var comparer = new ListComparer();

_button1.onClick.AddListener(() =>
{
    Profiler.BeginSample("GCAllocCheck: sort(closure)");
    int p = 6;
    // list を p との距離が近い順にソートしたい
    list.Sort((a, b) => Math.Abs(a - p).CompareTo(Math.Abs(b - p)));
    Profiler.EndSample();

    Profiler.BeginSample("GCAllocCheck: sort(comparer)");       
    comparer.P = p; // p をソートするタイミングで更新
    list.Sort(comparer); // comparer を使い回す          
    Profiler.EndSample();            

    Observable.NextFrame(FrameCountType.EndOfFrame).Subscribe(_ => EditorApplication.isPaused = true);
});

実行結果です。

スクリーンショット_2017-12-10_15_51_02.png

GC Alloc が 0 B と表示されており、ゴミが発生していないことが確認できます。

捕捉

ちなみに、list.Sort((a, b) => a - b) のように比較関数が外部環境を参照しない場合、比較関数は内部でキャッシュされて使い回されます(参考: Unityでのボクシングの殺し方、或いはラムダ式における見えないnewの見極め方)。

参考

7
5
4

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
7
5