0

まったく何もしないクラスにインターフェイスを実装すると、呼び出し元のコードが遅くなりますか? 例 2 (NoLogger) は、使用されているコードの速度に何らかの影響を与えますか?

コード例:

interface ILogger{
    void Write(string text);
}

class TextLogger : ILogger {
    public void Write(string text){
        using (var sw = new StreamWriter(@"C:\log.txt"))
        {
            sw.WriteLine(text);
        }
    }
}

class NoLogger : ILogger{
    public void Write(string text){
        //Do absolutely nothing
    }
}

実装 1、TextLogger

void Main(){
        ILogger tl = new TextLogger();
        for (int i = 0; i < 100; i++)
        {
            tl.Write(i.ToString());
        }
 }

実装 2、NoLogger

void Main(){
        ILogger tl = new NoLogger();
        for (int i = 0; i < 100; i++)
        {
            tl.Write(i.ToString());
        }
 }

もちろん、例 1 (テキストロガー) は、実際に何かを行うため、実装されているコードの実行を遅くします。

しかし、例 2 はどうでしょうか。コンパイラは、クラスがインスタンス化され、メソッドが呼び出されたとしても、パスを下って何かを実行し、コンパイル時にそれを無視するコードがまったくないことを理解するのに十分なほどインテリジェントですか?

4

3 に答える 3

2

.net JIT コンパイラが動的プロファイリングを行って、同じ仮想関数を何度も呼び出して使用しているかどうかを判断し、非仮想化してインライン化することを説明している興味深いブログ エントリがここにあります。この目的のために、アプリケーションの実行中にコードにパッチが適用されます。

したがって、ノーオペレーションと評価されることはありませんが、JIT コンパイラーは、メソッドの呼び出しに関連するオーバーヘッドのほとんどを排除します。

于 2013-11-01T12:51:15.737 に答える
2

コンパイラは、クラスがインスタンス化され、メソッドが呼び出されたとしても、パスを下って何かを実行し、コンパイル時にそれを無視するコードがまったくないことを理解するのに十分なほどインテリジェントですか?

コンパイラは、合法的に呼び出しを完全に排除することはできません。少なくとも、コンパイラは、呼び出されるメソッドに渡すすべての式を評価する必要があります。具体的にはi.ToString()、実装が実際に何を行うかに関係なく、例では 100 回呼び出されます。コンパイラはそれを最適化できません。そうしないと、パラメーター式に副作用がある場合にプログラムのセマンティクスが誤って変更される可能性があるためです。

于 2013-11-01T12:40:06.040 に答える
2

これを一般化して、「JIT はインターフェイス メソッドの仮想呼び出しをインライン化するか」と一般化できると思います。これに対する答えは「いいえ」であると強く疑っています (そうするには、関連する実装型がその 1 つの具体的なタイプになることはありません。これは、私が予想していたよりも多くの分析です)。

もちろん、呼び出しありで 5 億回実行できますが、何も実行しなくてもかまいません。そうすれば、適切な答えの出発点が得られます。

また、注意: が何もしない場合でも、副作用がある可能性があるため、Writeを実行する必要があります。i.ToString()

属性を見たほうがいいと思い[Conditional("...")]ます。これ物事を変えます。多くの。例えば:

public static class Logger
{
    [Conditional("TRACE")]
    public static void Write(string text)
    {
       // some stuff
    }
}

今; シンボルを定義せずにこれをコンパイルすると、次のようになります。TRACE

static void Main()
{
    for (int i = 0; i < 100; i++)
    {
        Logger.Write(i.ToString());
    }
 }

コードは次のようにコンパイルされます。

static void Main()
{
    for (int i = 0; i < 100; i++)
    {
    }
 }

呼び出しは削除されますが、パラメータ評価 ( i.ToString())削除されます。TRACEシンボルを定義してコンパイルすると、コードが存在します。

于 2013-11-01T12:36:41.083 に答える