こんにちは、Unityエンジニアのオオバです。

C#を使う上でLINQは非常に便利な機能なのは言うまでもありません。

しかし、実際のプロダクトへ組み込む際の負荷は知っておくべきということで、改めて調べてみたという記事です。

今回の検証対象は使用頻度が高いフィルタ系関数です。

検証するポイントはUnityProfiler項目で言うと、Time ms(実行時間)GC Allocです。
実行時間は言葉の通りで、高ければ高いほどFPSが低下します。
GC Allocは1フレームにヒープメモリの確保メモリ容量で、数値が高いとGC発生回数が増えてしまい、プロダクトのパフォーマンスを下げる場合があります。

今回の検証では数値を可視化しやすいように配列要素数を10000個1フレーム内の実行回数を100回とします。

検証方法は以前ブログで紹介したやり方です。

確認場所(for, foreach文など)にProfiler.BeginSampleとProfiler.EndSampleで処理を挟んで数値を確認しています。

FirstOrDefaultの場合

LINQとfor文の負荷検証_0

Anyの場合

LINQとfor文の負荷検証_1

Whereの場合

LINQとfor文の負荷検証_2

for文の場合

LINQとfor文の負荷検証_3

これらの結果を以下の表にまとめます。

処理名処理時間GC Alloc
FirstOrDefault97.52ms13.3KB
Any97.52ms13.3KB
Where0.05ms17.2KB
配列のfor文5.17ms0KB
IList型のfor文19.52ms0KB
List型のfor文16.65ms0KB
配列foreach5.69ms0KB
List型のforeach42.15ms0KB
IList型宣言変数のforeach43.34ms3.9KB
IEnumerable型のforeach101.81ms10.9KB

まとめ

予想通りパフォーマンスだけで言うと、ベタにfor文で書いた方が早いですし、GC対策にもヒットしますが記述コード量の削減、可読性を考えるとLINQは捨て難いものです。

今回の検証コード自体が、可視化するために非現実で高い負荷をかけています。for文がLINQの20倍早いとはいえ、ループ回数や使用頻度が低ければ、それは全く問題にならないという事になります。

LINQとfor文の使い分けとして今回のような毎フレーム実行する部分でのLINQ使用は控えるべきだとオオバは考えます。あっという間にGC発生させることになるからです。

高頻度処理には素直にfor文が良いかと。

何事も使い所が重要で、どの処理にどれだけのリソースを使うかという情報は把握しておきたいものです。

オススメ記事