パフォーマンス - foreach って大丈夫?

最初に軽くid:ladybug:20050804#p1 の http://www.microsoft.com/japan/msdn/enterprise/pag/scalenet-intro.asp について触れておくと、
とりあえず、日本語になっている Part I & II を眺めてみると、この2章は .NET に固有の内容よりも、まずは考え方やらなんやらがメインといったところでしょうか。やっぱり、これ読んでないかも。
パフォーマンス問題に関しては、実務上でいろいろとやってる都合で経験的に直感している部分が大きいのでまじめに勉強する時間があれば勉強してみたいところですね。

たとえば

これは、C# でプログラムを組むようになって一ヶ月もたたないうちに思ったことなのですが、構造体(値型)のボックス化が自動であることを学んだ上で、それを保持する配列やコレクションの中身に対して処理を実行する場合、

// (1) by index
for (int i = 0; i < container_count; i++)
{
  // access by 'container[i]'
}
// (2) auto enumeration
foreach (VALUE value in container)
{
  // access by 'value'
}
// (3) manualy enumeration
using (IEnumerator e = container.GetEnumerator())
{
  while (e.MoveNext())
  {
    VALUE value = (VALUE) e.Current;

    // access by 'value'
  }
}

といった感じに記述が可能なわけですが、現状のコンパイラC#JIT)は、どれぐらいこれを最適化できるんだろうか?とか、ふと気にしませんか?
JIT コンパイラの上で、ループが減算になるか?とか、JIT 後のネイティブコードの格納アドレスの連続性を確保してジャンプ等の最適化もやってるか?とか、そんなレベルことを考える以前に、たとえば値型の配列型(Array)に対して (3) のコードを動かすと、ボックス化とアンボックス化が Current のアクセスのたびに発生してめちゃくちゃ遅いんじゃないだろうか、とか気になったりしませんか?
さらに foreach は IL のレベルには存在しない C# コンパイラが扱う構文だから、(2) をコンパイルしたら (3) とほぼ同じ IL に展開されて、(2) も (3) と同じ理由で遅くなるんじゃないだろうか!? とか怖くなってきませんか?*1
とりあえずこの内容に関しては、現状の Microsoft Visual C# .NET Compiler 7.10.6001.4 は (2) の例で container にあたる部分の型によって foreach の IL を (1) と (3) のどちらに展開するか決定しているようです。int[] を渡すと (1) になり、ArrayList を渡すと (3) になる、というようなかんじです。*2IList 型を (1) に展開できる仕組みもちょっと欲しいと思ったりします。*3
.NET 2.0 では IEnumerator`1 を利用することで (1) のタイプのコードを生成しなくともボックス化の影響を受けないようにすることも可能になりますが、現状の .NET 1.1 ではプロファイラや実測を見るかぎり ArrayList あたりは (3) よりも (1) が高速なわけで、0.1ms ぐらいのオーダーを削る時には気にしたい項目かもしれません。

*1:例の (3) はコンパイルできないコードですが、実際の foreach に近づけるために意図的にこう書いてあります。

*2:とくに値型に限った話ではなく、参照型の配列も (1) の形式に展開されます。

*3:アクセス順序とインデックスに関連性がなかったり、リンクリストのようにインデックス指定の参照の繰り返しが IEnumerator による参照より効率が悪い場合もあるでしょうから、自動化は非常に困難だろう