C# LINQ パフォーマンスについて
C# LINQ パフォーマンスについて
TL;DR — 要点まとめ
- LINQ の Count() は for/foreach に比べて大きなオーバーヘッドがある - 毎フレーム呼び出しでは使用禁止
- フィルタリング (Where) と変換 (Select) は for/foreach とほぼ同等の性能
- LINQ は中間バッファ (配列) を生成し GC 圧力を上げる - 頻繁な呼び出しは GC スパイクの原因
- プロトタイプ・非クリティカルなコードでは LINQ、毎フレームや性能重視箇所では for/foreach
目次
LINQ の利点
- LINQ (Language Integrated Query) は、簡潔で扱いやすいコードを書くための C# 機能セットです。
クエリ構文を使うことで、最小限のコードでデータソースに対する
フィルタリング、ソート、グルーピングを実行できます。
LINQ 使用時の注意点
- LINQ の多くの演算子は中間バッファ (配列のようなもの) を生成し、それがすべてガベージになります。👉 GC 発生
- 1〜2 個の LINQ 演算子で済む場面でも、可読性目的で細かく分割しすぎると性能低下の原因になります。
- Average、Count、OrderBy、Reverse などは元シーケンスを反復するため、性能面で不利です。
そのため、LINQ を使う前に以下のルールを考える必要があります。
使用時ルール
- すばやい実装が必要か (プロトタイプ開発、TDD)? - LINQ を使う
- 性能に敏感でないコードか? - LINQ を使う
- LINQ の利便性や可読性が本当に必要か? - LINQ を使う
- 毎フレーム呼ばれる、または性能クリティカルな箇所か? - LINQ は使わない
LINQ パフォーマンスベンチマーク
- ベンチマークライブラリ: BenchMark
- テストコード: TestCode
- 元記事: LinqPerformance
ベンチマーク - 反復処理
年齢 18 歳未満の Customer 数を返す
For / Foreach
// 性能: 高速 (GC なし)
int count = 0;
foreach (var c in customers)
{
if (c.Age < 18)
count++;
}
return count;LINQ Count
// 性能: 低速 (GC 発生)
// Count() は内部で
// シーケンス全体を走査する
return customers
.Count(c => c.Age < 18);for/foreach と比べると、LINQ には若干のオーバーヘッド (機能実行に伴う追加の時間・メモリ・リソース) があります。
フィルタ後のカウントは比較的 for に近いですが、単純に要素数を返す Count は性能低下が大きいです。
ベンチマーク - フィルタリング
年齢 18 歳以上の顧客配列サブセットを返す
for/foreach 実装と非常に近い性能です。for/foreach より約 4ms 遅い結果でした。
実際に RaycastAll でフィルタ後に tag チェックへ使ったコード
フィルタリングのようなケースでは、通常の for/foreach よりも簡潔で可読性の高いコードにできます。
ベンチマーク - 変換
年齢を年ではなく月で表現した顧客リストを返す
LINQ はどれだけ GC を生成するか?
テスト条件は下記リンク参照
結果
Microsoft Docs の Unity 向けパフォーマンス推奨
LINQ vs For - パフォーマンス比較チャート
相対実行時間比較 (低いほど高速、for = 1.0 基準)
結論
- LINQ の
Countはかなり遅い。 - フィルタリング (
Where) と変換 (Select) は性能面でforとほぼ同等。 - 1〜2 行の非常に簡潔なコードが書ける。
- デバッグはやや難しい。
- GC 発生があるため、乱用すると厳しくなる。
- 毎フレーム呼び出しや性能影響が大きい箇所では使わないこと。
この記事は著者の CC BY 4.0 ライセンスの下で提供されています。













