2

動的コード生成を使用してパフォーマンスを向上させる方法については考えていますが、この問題に取り組む最善の方法はどれかわかりません。

クラスがあるとします


class Calculator
{
  int Value1;
  int Value2;
  //.......... 
  int ValueN;

  void DoCalc()
  {
    if (Value1 > 0)
    {
      DoValue1RelatedStuff();    
    }
    if (Value2 > 0)
    {
      DoValue2RelatedStuff();    
    }
    //....
    //....
    //....
    if (ValueN > 0)
    {
      DoValueNRelatedStuff();    
    }
  }
}

DoCalc メソッドは最下位レベルにあり、計算中に何度も呼び出されます。もう 1 つの重要な側面は、ValueN が最初に設定されるだけで、計算中に変更されないことです。ValueN の多くが 0 であるため、DoCalc メソッドの if の多くは不要です。そのため、動的コード生成がパフォーマンスの向上に役立つことを期待していました。

たとえば、メソッドを作成した場合


  void DoCalc_Specific()
  {
    const Value1 = 0;
    const Value2 = 0;
    const ValueN = 1;

    if (Value1 > 0)
    {
      DoValue1RelatedStuff();    
    }
    if (Value2 > 0)
    {
      DoValue2RelatedStuff();    
    }
    ....
    ....
    ....
    if (ValueN > 0)
    {
      DoValueNRelatedStuff();    
    }
  }

C#コンパイラは、必要なものだけを保持するのに十分スマートです。したがって、ValueN の値に基づいて実行時にそのようなメソッドを作成し、計算中に生成されたメソッドを使用したいと思います。

式木を使えばいいのかなと思いますが、式木は単純なラムダ関数でしか動かないので、関数本体の中でifやwhileなどは使えません。したがって、この場合、このメソッドを適切な方法で変更する必要があります。

もう 1 つの可能性は、必要なコードを文字列として作成し、動的にコンパイルすることです。しかし、既存の方法を使用してそれに応じて変更できれば、私にとってははるかに良いでしょう.

Reflection.Emit もありますが、維持するのが非常に難しいため、これに固執したくありません。

ところで。私は C# に限定されません。したがって、この種の問題に最適なプログラミング言語の提案を歓迎します。いくつかの理由から、LISP を除きます。

1 つの重要な説明。DoValue1RelatedStuff() は、私のアルゴリズムではメソッド呼び出しではありません。これは数式ベースの計算であり、非常に高速です。こう書くべきだった


if (Value1 > 0)
{
  // Do Value1 Related Stuff
}

いくつかのパフォーマンス テストを実行したところ、1 つが無効になっている場合に 2 つの if を使用すると、最適化された方法が冗長な if を使用した場合よりも約 2 倍高速であることがわかります。

テストに使用したコードは次のとおりです。


    public class Program
    {
        static void Main(string[] args)
        {
            int x = 0, y = 2;

            var if_st = DateTime.Now.Ticks;
            for (var i = 0; i  < 10000000; i++)
            {
                WithIf(x, y);
            }
            var if_et = DateTime.Now.Ticks - if_st;
            Console.WriteLine(if_et.ToString());

            var noif_st = DateTime.Now.Ticks;
            for (var i = 0; i  < 10000000; i++)
            {
                Without(x, y);
            }
            var noif_et = DateTime.Now.Ticks - noif_st;
            Console.WriteLine(noif_et.ToString());

            Console.ReadLine();

        }

        static double WithIf(int x, int y)
        {
            var result = 0.0;
            for (var i = 0; i  < 100; i++)
            {
                if (x > 0)
                {
                    result += x * 0.01;
                }
                if (y > 0)
                {
                    result += y * 0.01;
                }
            }
            return result;
        }

        static double Without(int x, int y)
        {
            var result = 0.0;
            for (var i = 0; i < 100; i++)
            {
                result += y * 0.01;
            }
            return result;
        }
    }
4

4 に答える 4

2

私は通常、そのような最適化について考えさえしません。どのくらいの仕事をしDoValueXRelatedStuff()ますか?10 から 50 プロセッサー・サイクルを超えていますか? はい?つまり、非常に複雑なシステムを構築して実行時間を 10% 未満に短縮することになります (これは私には非常に楽観的です)。これは簡単に 1% 未満にまで下がる可能性があります。

他の最適化の余地はありませんか? より良いアルゴリズム?(分岐予測が正しい場合) 1 つのプロセッサ サイクルだけを使用する単一の分岐を本当に排除する必要がありますか? はい?.NET を使用する代わりに、アセンブラーまたはその他のよりマシン固有のものでコードを作成することを検討すべきではありませんか?

の順序N、典型的なメソッドの複雑さ、および通常 true と評価される式の比率を教えてください。

于 2009-08-31T22:06:27.193 に答える
1

コードの最適化に本当に興味がある場合は、何かを行う前に、プロファイラーを実行してください。ボトルネックがどこにあり、どの領域を最適化する価値があるかを示します。

また、言語の選択が制限されていない場合 (LISP を除く)、パフォーマンスの点でアセンブラに勝るものはありません;)

アセンブラーを使用していくつかの内部関数 (あなたが持っているものなど) を書き直すことで、パフォーマンスの魔法を達成したことを覚えています。

于 2009-08-31T22:08:21.620 に答える
1

ifステートメントを評価するオーバーヘッドが、動的にコードを発行する労力に見合うだけのシナリオを見つけたら、私は驚くでしょう。

最新の CPU は分岐予測分岐予測をサポートしており、コードの小さなセグメントでの分岐のオーバーヘッドがゼロに近づきます。

コードの 2 つのハンドコーディング バージョンのベンチマークを試みましたか? 1 つはすべての if ステートメントが配置されていますが、ほとんどのステートメントにゼロ値を提供し、もう 1 つは同じ if 分岐をすべて削除していますか?

于 2009-08-31T21:56:05.747 に答える
0

何かをする前に、実際に問題がありますか?

つまり、あなたを悩ませるほど長く実行されますか?

もしそうなら、あなたが推測するものではなく、実際に時間がかかっているものを見つけてください。これは、時間の経過を確認するために私が使用する、手っ取り早く、汚れた、非常に効果的な方法です。

今、あなたは解釈とコンパイルについて話しています。解釈されたコードは、通常、コンパイルされたコードよりも 1 ~ 2 桁遅くなります。その理由は、インタプリタが次に何をすべきかを継続的に考え出し、コンパイルされたコードは を知っているのに、を忘れているからです。

このような状況にある場合、コンパイルされたコードの速度を得るために翻訳の代償を払うことは理にかなっているかもしれません。

于 2009-09-28T01:03:14.737 に答える