++C++; // 未確認飛行 C ブログ

http://ufcpp.net/

C# vNext (Roslyn)での async/await の仕様変更

leave a comment »

そういえば。昔、こんな話題が。

これの「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 に変わっているものの、コンパイル オプションとかは今まで通りみたい。

結果

ちゃんと変更されてた。

image

(フィールド名とかは読めるように書き換えています。コンパイラーの生成するコードはもっと難読化されたようなひどい変数名。)

現状の何が問題かというと、awaitをまたがないものでも無関係に全部、ローカル変数がフィールド化されてたこと。

一方で、新コンパイラーでは、ちゃんとawaitをまたぐかどうかを判定して、またいでいるものだけをフィールド化。

このサンプルで言うと、変数 a だけがawaitをまたいでいて、x と y はまたいでいない。現コンパイラーだと a, x, y 全部フィールドになっていて、新コンパイラーだと a だけがフィールドになる。

変更の影響

まあ、実のところ破壊的変更になるわけですが。とはいえ、めったなことではこの変更の影響を受けないはず。

この変更の影響を受けるとすると、GCのタイミングに依存するようなコードを書いた場合(デストラクターを持っていて、かつ、デストラクターの実行順序によって挙動が変わるとか)。そんなコード書くことは普通ないので問題は出ないでしょう。

(というか、元のコードでもコンパイラーの最適化の有無で参照が消えたりするはずで、元々GCのタイミングに依存するようなコード書いたらまともに動かないはずですけども。)

Written by ufcpp

2014年4月5日 @ 17:36

カテゴリー: C#

Tagged with ,

コメントを残す