C# vNext (Roslyn)での async/await の仕様変更
そういえば。昔、こんな話題が。
これの「Lift Your Way out of Garbage Collection」の節のあたりに書いてあること。大まかにいうと、
- 非同期メソッド内のローカル変数は全部フィールドに変更される
- それが原因で、オブジェクトの寿命が延びてGC性能を多少落とす
- 将来的に、このC#/VBコンパイラーの挙動は変更するかも
という内容。
今がその「将来」か?ということで、Roslynの新しいC#コンパイラーの生成結果をのぞいてみた。
元コード
以下のようなコードを、C# 5.0のコンパイラーと、Roslynの新コンパイラーでビルドして、その結果をIL Spyでのぞいてみる。
static async Task X()
{
var a = ReadInt();
var x = ReadInt();
Console.WriteLine(x);
await Task.Delay(1);
++a;
var y = ReadInt();
Console.WriteLine(y);
Console.WriteLine(a);
}
static int ReadInt()
{
return int.Parse(Console.ReadLine());
}
ちなみに、RoslynをインストールしちゃうとVisual Studioが新しいコンパイラーしか使わなくなるんで、コマンドラインで直にコンパイル。
コンパイラーは以下の場所を探せば見つかるはず。
- 現状のコンパイラー: Windowsフォルダーの下、Microsoft.NET\Framework64\v4.0.30319 (バージョン違いの場合、64 が付かなかったり v の後ろの数字が違う)にある csc.exe
- Roslynのコンパイラー: Visual Studio拡張のインストールフォルダー内を rcsc.exe で検索
- VS拡張は ユーザー名\AppData\Local\Microsoft\VisualStudio\12.0\Roslyn\Extensions に入ってる
実行ファイル名は rcsc.exe に変わっているものの、コンパイル オプションとかは今まで通りみたい。
結果
ちゃんと変更されてた。
(フィールド名とかは読めるように書き換えています。コンパイラーの生成するコードはもっと難読化されたようなひどい変数名。)
現状の何が問題かというと、awaitをまたがないものでも無関係に全部、ローカル変数がフィールド化されてたこと。
一方で、新コンパイラーでは、ちゃんとawaitをまたぐかどうかを判定して、またいでいるものだけをフィールド化。
このサンプルで言うと、変数 a だけがawaitをまたいでいて、x と y はまたいでいない。現コンパイラーだと a, x, y 全部フィールドになっていて、新コンパイラーだと a だけがフィールドになる。
変更の影響
まあ、実のところ破壊的変更になるわけですが。とはいえ、めったなことではこの変更の影響を受けないはず。
この変更の影響を受けるとすると、GCのタイミングに依存するようなコードを書いた場合(デストラクターを持っていて、かつ、デストラクターの実行順序によって挙動が変わるとか)。そんなコード書くことは普通ないので問題は出ないでしょう。
(というか、元のコードでもコンパイラーの最適化の有無で参照が消えたりするはずで、元々GCのタイミングに依存するようなコード書いたらまともに動かないはずですけども。)
コメントを残す