Linqは内部でyieldを使っていますがyieldはコンテキストスイッチを行いますので、そのパフォーマンスはどうなのか調べてみました。
yieldのパフォーマンス
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 
 | using System.Collections;using System.Collections.Generic;
 using System.Diagnostics;
 using System.Windows.Forms;
 
 namespace Project1
 {
 public partial class Form1 : Form
 {
 public Form1()
 {
 InitializeComponent();
 }
 
 
 public TimeSpan MeasureTime(Action action)
 {
 var sw = new Stopwatch();
 try {
 sw.Start();
 action();
 } finally {
 sw.Stop();
 }
 return sw.Elapsed;
 }
 
 
 public IEnumerable<int> GetIntegerEnumerator()
 {
 for (var i = 0; i < 10000000; i++) {
 yield return i;
 }
 }
 
 
 void test1()
 {
 foreach (var i in GetIntegerEnumerator()) {
 var x = i;
 }
 }
 
 
 class Test2 : IEnumerator<int>
 {
 int _current = 0;
 
 public int Current
 {
 get { return _current; }
 }
 
 public void Dispose()
 {
 
 }
 
 object System.Collections.IEnumerator.Current
 {
 get { return _current; }
 }
 
 public bool MoveNext()
 {
 _current++;
 return  _current<10000000;
 }
 
 public void Reset()
 {
 _current = 0;
 }
 }
 
 
 void test2()
 {
 foreach (var i in new Test2().ToEnumerable()) {
 var x = i;
 }
 }
 
 private void button1_Click(object sender, EventArgs e)
 {
 for (var i=0; i<5; i++) {
 Console.WriteLine(MeasureTime(test1));
 }
 }
 
 private void button2_Click(object sender, EventArgs e)
 {
 for (var i = 0; i < 5; i++) {
 Console.WriteLine(MeasureTime(test2));
 }
 }
 }
 
 
 public static class EnumeratorExtensions
 {
 private class EnumerableConverter<T> : IEnumerable<T>
 {
 IEnumerator<T> _enumerator;
 
 public EnumerableConverter(IEnumerator<T> enumerator)
 {
 _enumerator = enumerator;
 }
 
 public IEnumerator<T> GetEnumerator()
 {
 return _enumerator;
 }
 
 IEnumerator IEnumerable.GetEnumerator()
 {
 return _enumerator;
 }
 }
 
 
 public static IEnumerable<T> ToEnumerable<T>(this IEnumerator<T> enumerator)
 {
 return new EnumerableConverter<T>(enumerator);
 }
 }
 
 }
 
 | 
 
リストではGetIntegerEnumeratorで整数を一千万回発生させ受け側test1ではforeachでごく軽い処理を行っています。
その結果は約0.7秒でした。
- 00:00:00.6699127
- 00:00:00.7322575
- 00:00:00.6883833
- 00:00:00.6887978
- 00:00:00.7158395
同等の処理をクラスで実現すると、0.5秒でした。
- 00:00:00.5362418
- 00:00:00.5300212
- 00:00:00.5566201
- 00:00:00.5479106
- 00:00:00.5479031
マシンスペックはAMD Athlon(tm) X2 Dual Core Processor BE-2350 2.11GHz、2.00 GB RAM、OSはWin
  XP SP3 32Bitです。Visual Studio 2008でデバッグビルドしました。
yieldはクラスに比べると若干遅いのですが、ソースファイルやHTMLファイルのフィルタリング処理を行う程度であれば極端にパフォーマンスを落とすこともなさそうです。
バッファリングによってコンテキストスイッチの回数を減らすテストプログラムを書こうとしたのですが、複雑でパフォーマンスが上がりそうもなかったので中止しました。
その後、Reflectorで生成されたコードを分析すると、yield returnはクラスによって状態を保持するコードに置き換えられることがわかりました。私はWinAPIのファイバーの様な仕組みを想像していましたが違いました。傾向としてyield returnの記述が多ければ生成されたコードのswitchの選択肢が増えますのでパフォーマンスは悪くなります。