1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#のループ処理におけるインデックス取得方法によるパフォーマンス比較してみた

Last updated at Posted at 2025-04-27

はじめに

日々C#で開発をしている中で、
手動でインデックスをインクリメントするループ」と
LINQのSelectでインデックスを取得するループ
のどちらがより効率的か、ふと疑問に思う場面がありました。

今回、簡単な検証を通して

  • メモリ使用量
  • 実行時間
    にどれくらい差が出るのかを比較してみたので、その結果をまとめます。

実験内容

使用したコード

検証のために、以下のようなコードを作成しました。

オブジェクト作成とループ検証プログラム

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;

public class MyData {
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public MyData(int id, string name, int age)
    {
        Id = id;
        Name = name;
        Age = age;
    }
}

class Program
{
    public static string LOOPTYPE = "while"; // "for", "foreach", "foreachLinq", "while"
    public static int LOOPNUMBER = 1000000;

    static List<MyData> create(int numberOfObjects)
    {
        List<MyData> objects = new List<MyData>();
        for (int i = 0; i < numberOfObjects; i++)
        {
            var data = new MyData(i + 1, "pokepoke", 12);
            objects.Add(data);
        }
        Console.WriteLine($"作成したオブジェクト数: {objects.Count}");
        return objects;
    }
    
    static void Main()
    {
        var sw = new Stopwatch();
        List<MyData> sampleList = create(LOOPNUMBER);

        sw.Start();
        long initialMemory = GC.GetTotalMemory(false);
        Console.WriteLine($"初期メモリ: {initialMemory:N0} bytes");
        Console.WriteLine($"{LOOPTYPE}");
        
        for (int i = 0; i < 10; i++)
        {
            int[] id = new int[sampleList.Count];
            string[] name = new string[sampleList.Count];
            int index = 0;

            switch(LOOPTYPE){
                case "for":
                    for (int j = 0; j < sampleList.Count; j++){
                        id[j] = sampleList[j].Id;
                        name[j] = sampleList[j].Name;
                    }
                    break;
                case "foreach":
                    foreach(var value in sampleList){
                        id[index] = value.Id;
                        name[index] = value.Name;
                        index++;
                    }
                    break;
                case "foreachLinq":
                    foreach(var value in sampleList.Select((item,index) => new {item,index})){
                        id[value.index] = value.item.Id;
                        name[value.index] = value.item.Name;
                    }
                    break;
                case "while":
                    while (index < sampleList.Count){
                        id[index] = sampleList[index].Id;
                        name[index] = sampleList[index].Name;
                        index++;
                    }
                    break;
                default:
                    break;
            }

            long currentMemory = GC.GetTotalMemory(false);
            Console.WriteLine($"[{i}] メモリ: {currentMemory:N0} bytes 経過時間: {sw.ElapsedMilliseconds} ms");
            System.Threading.Thread.Sleep(500);
        }

        sw.Stop();
        Console.WriteLine($"総時間: {sw.ElapsedMilliseconds} ms");
    }
}

コード要約

このコードは、

  • for / foreach / foreach+LINQ / while の4つのループ方式で、
  • 100万件など大量のオブジェクトに対して、
  • メモリ使用量と処理時間を比較する

ための簡易パフォーマンス検証ツールです。

実験条件

オブジェクト数

  • 10,000件
  • 100,000件
  • 1,000,000件

計測対象

  • メモリ使用量(GC.GetTotalMemory(true)使用)
  • 実行時間(Stopwatch使用)

実行環境

  • ※ここに開発環境を書く予定(例:Windows 11 / .NET 8 / Releaseビルド etc.)

実験結果

オブジェクト数 方法 メモリ増加量 実行時間
10,000 手動インクリメント 少ない 速い
10,000 LINQ Select やや多い やや遅い
100,000 手動インクリメント 少ない 速い
100,000 LINQ Select 多め 遅い
1,000,000 手動インクリメント 少ない やや遅い(でもまだ速い)
1,000,000 LINQ Select さらに多い さらに遅い

ざっくりまとめると

  • オブジェクト数が多くなるほど差が開く
  • 10,000件程度ならほぼ誤差レベル

考察(現場目線で)

今回の結果を現場で活かすなら、以下のような判断ができそうです。

条件 推奨方法
少量データ(数千〜数万件) どちらでもOK。可読性重視でSelectもアリ
大量データ(数十万件以上) 手動インクリメントを使ったほうが無難

理由

  • LINQを使うと「一時オブジェクト」が内部で生成されるため、オーバーヘッドが無視できない
  • また、LINQの内部イテレータ(SelectIterator)によりメモリ圧迫・GCコストも増える
  • 少量なら開発スピード優先、大量ならパフォーマンス優先がベター

まとめ

  • LINQのインデックス取得は少量データなら気にせず使ってOK
  • 大量データ処理では、素直なforやインクリメント管理のほうが安全
  • 可読性 vs パフォーマンス、どちらを重視すべきか現場ごとに判断する

今後も「なんとなくLINQを使う」ではなく、場面に応じた選択を意識していきたいと思いました。
記事の内容で間違いとかあれば指摘していただきたいです
今後の勉強にやくだてさせていただきます

1
1
0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?