連載:[完全版]究極のC#プログラミング

Chapter3 新しい繰り返しのスタイル ― yield return文とForEachメソッド

川俣 晶
2009/08/31

3.12 性能比較

 さて、ここで気になるのは反復子とForEachメソッドのどちらを使うべきかである。ソースコードの簡潔さは大差ないとすれば、残ったポイントは性能といえる。

 そこで、反復子とForEachメソッドの速度比較を行ってみた。リスト3.14はそのために作成したテストプログラムである。

using System;
using System.Collections.Generic;

class Program
{
  private static IEnumerable<int> getCounter(int from, int to)
  {
    for (int i = from; i <= to; i++) yield return i;
  }

  private static void forEach(int from, int to, Action<int> action)
  {
    for (int i = from; i <= to; i++) action(i);
  }

  static void Main(string[] args)
  {
    const int TestCount = 1000000000;
    DateTime start1 = DateTime.Now;
    int sum1 = 0;

    foreach (int i in getCounter(1, TestCount))
    {
      sum1 += i;
    }
    Console.WriteLine(DateTime.Now - start1);

    DateTime start2 = DateTime.Now;
    int sum2 = 0;

    forEach(1, TestCount, delegate(int i)
    {
      sum2 += i;
    });

    Console.WriteLine(DateTime.Now - start2);
    Console.WriteLine(sum1 == sum2);
  }
}
リスト3.14 反復子とForEachメソッドの速度比較

00:00:20.5880586
00:00:07.5447544
True
リスト3.14の実行結果(Visual Studio 2008のリリースビルド)

 実行結果は、1行目が反復子による実行時間、2行目はForEachメソッドによる実行時間、3行目は2つの処理結果の累積値が同じ(つまり、同じ回数を繰り返した証拠)ということを示すTrueが出力されている。

 これを見ると一目瞭然だが、ForEachメソッドを使ったほうが圧倒的に速い。デバッグビルドにすると差は縮まるが、やはりForEachメソッドのほうが速い。

 なぜForEachメソッドのほうが速いのかは、内部構造を考えれば容易にわかるだろう。ForEachメソッドは、繰り返し1回ごとにメソッドを1回だけ呼ぶ。しかし、反復子はMoveNextメソッドを呼んでからCurrentプロパティで値を取得する必要があり、2回の呼び出しが発生する。さらに、周辺にかなり込み入ったコードが生成されていることから考えて、大きな性能差が出るのはやむをえないところだろう。

 さて、ここでの結論は列挙よりもForEachメソッドを使うほうが優れている……ということでよいのだろうか? 自作のオブジェクトに列挙インターフェースを付けることは意味がなく、ForEachメソッドさえ付けておけばよいのだろうか?

 そうではない。実は、C# 3.0のキーとなる重要技術の1つであるLINQ(第15章〜第18章参照)は「列挙できるものはクエリできる」という機能を発揮する。単に高速に列挙するだけならForEachメソッドでもよいのだが、LINQのクエリ式の中でオブジェクトを活用したいと思うなら、列挙インターフェースを追加することは重要な意味を持つ。そして、それをyield return文で簡潔に記述するノウハウにも重要な意味がある。

【Exercise】練習問題

 foreach文とForEachメソッドについて誤った説明を選べ。

  1. 1 実行速度に差がある
  2. foreachは文であるがForEachはメソッドなので、使いたい対象を列挙するForEachメソッドを作成しないと使えない
  3. foreach文そのものは言語の一部なので自作することは難しいが、ForEachメソッドは簡単に自作できる
  4. yield return文で列挙できる
  5. 名前が違う

 ◎解答:「 4 」(この行をマウスで選択してください)End of Article


 INDEX
  [完全版]究極のC#プログラミング
  Chapter3 新しい繰り返しのスタイル ― yield return文とForEachメソッド
    1.3.1 「繰り返し」という古くて新しい問題
    2.3.2 数を数えるというサンプル
    3.3.3 C# 1.xによるRangeクラスの実装
    4.3.4 C# 3.0によるRangeクラスの実装
    5.3.5 yield break文による中断
    6.3.6 yieldは予約語ではない
    7.3.7 1つのクラスに複数の列挙機能を付ける
    8.3.8 自動的に作られるオブジェクトと二重利用
    9.3.9 catchできない制約
    10.3.10 制約の真相―見た目と違う真実の姿
    11.3.11 ForEachメソッドを使う別解
  12.3.12 性能比較
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間