2012-03-13 4 views
15

グローバル/o+コンパイラスイッチが設定されていなくても、メソッドを常に最適化する必要があることをコンパイラに伝えるための属性はありますか?特定のメソッドを最適化するようにコンパイラを強制できますか?

私が尋ねる理由は、既存のメソッドのILコードに基づいてメソッドを動的に作成するというアイデアを思いついているからです。私がやりたい操作は、コードが最適化されるとかなり簡単ですが、コンパイラによって生成される余分な命令のために最適化されていないコードではかなり困難になります。


EDIT:私を気に非最適化についての詳細...

のは、以下の階乗関数の実装について考えてみましょう:

static long FactorialRec(int n, long acc) 
{ 
    if (n == 0) 
     return acc; 
    return FactorialRec(n - 1, acc * n); 
} 

(注:私は知っています階乗を計算する良い方法ですが、これは単なる例です)

最適化を有効にして生成したIL非常に簡単だ:

IL_0000: ldarg.0  
IL_0001: brtrue.s IL_0005 
IL_0003: ldarg.1  
IL_0004: ret   
IL_0005: ldarg.0  
IL_0006: ldc.i4.1  
IL_0007: sub   
IL_0008: ldarg.1  
IL_0009: ldarg.0  
IL_000A: conv.i8  
IL_000B: mul   
IL_000C: call  UserQuery.FactorialRec 
IL_0011: ret   

しかし、最適化されていないバージョンがかなり異なっている

IL_0000: nop   
IL_0001: ldarg.0  
IL_0002: ldc.i4.0  
IL_0003: ceq   
IL_0005: ldc.i4.0  
IL_0006: ceq   
IL_0008: stloc.1  
IL_0009: ldloc.1  
IL_000A: brtrue.s IL_0010 
IL_000C: ldarg.1  
IL_000D: stloc.0  
IL_000E: br.s  IL_001F 
IL_0010: ldarg.0  
IL_0011: ldc.i4.1  
IL_0012: sub   
IL_0013: ldarg.1  
IL_0014: ldarg.0  
IL_0015: conv.i8  
IL_0016: mul   
IL_0017: call  UserQuery.FactorialRec 
IL_001C: stloc.0  
IL_001D: br.s  IL_001F 
IL_001F: ldloc.0  
IL_0020: ret   

最後に、唯一の出口点を持つように設計されています。返される値は、ローカル変数に格納されます。

なぜこの問題が発生しますか?テールコールの最適化を含むメソッドを動的に生成したい。最適化されたメソッドは、ret以外の呼び出しの後に何もないので、再帰呼び出しの前に接頭辞tail.を追加することで簡単に変更できます。しかし、最適化されていないバージョンでは、再帰呼び出しの結果がローカル変数に格納され、次の命令にジャンプする無駄なブランチがあり、ローカル変数がロードされて返されます。だから、再帰呼び出しが実際に最後の命令であることを確認する簡単な方法はないので、テールコール最適化を適用できるかどうかは確かではありません。

+1

AFAIK、いいえ - できません –

+1

JITコンパイラは常にすべてのメソッドを最適化します。 – Steven

+0

@Steven、そうでないと伝えないと(例えば、 'MethodImplAttribute'の' NoOptimization'フラグを使って)。しかし、とにかく、私の質問は、私は生成されるILコードに興味があるので、コンパイラの最適化ではなく、JITの最適化についてです。 –

答えて

2

ダイナミックメソッドのテンプレートとして使用するメソッドが比較的単純で、他のメソッドに依存しない場合は、それを自分のアセンブリに入れて、そのアセンブリだけの最適化を有効にします。

MSILはスタックベースの言語なので、元々問題になっていました。また、仕様ではretステートメントでスタック状態を保証しているので、問題なくテール・プレフィックスを追加できることを100%保証することができます。しかし実際にJITが最終的にジッテされたコードを最適化するためにテール・プレフィックスを使用しているのを実際に見ていないので、実際にはどんな利点も追加することはほとんどありません。

+0

間違っているものを指摘するのに気をつけますか? –

+0

関連:http://stackoverflow.com/questions/491376 - 明らかに '.tail'はx64では最適化されていますが、x86では最適化されていません。 –

0

Microsoft.CSharp.CSharpCodeProviderを使用して元のメソッドコードを動的に生成できますか?

メソッドのコンパイルを制御する場合は、CompilerOptionsを使用してコンパイラを呼び出すときにオプションを設定できます。

+0

それは役に立たないでしょう。私はコードを動的に生成したくない(私が意味する元のコードではない) –

0

C#を使用している限り、テールコールの最適化を得ることは決してできません。

特に、call ... retであっても、JITterはテールコールを保証しません。だから、スタックオーバーフローを避けるために、テールコールの最適化に依存するIMO C#コードは、単純に壊れています。 C#では、テールコールの最適化は純粋にパフォーマンスの最適化です。

テールコールが確実に発行される言語を使用するか、テールコールを必要としないようにメソッドを書き換えます。

+0

私はC#がテールコールには適していないことを知っている、私はちょうど概念の証明をしていた...心配しないで、私はしないこれを生産コードで使用しようと思っています; –

関連する問題