2012-06-26 14 views
6

これらの中で最適化されたコードはどれですか?最初のケースでforループ/スイッチステートメントのパフォーマンス最適化

for(int i=0;i<count;i++) 
{ 
    switch(way) 
    { 
     case 1: 
      doWork1(i); 
      break; 
     case 2: 
      doWork2(i); 
      break; 
     case 3: 
      doWork3(i); 
      break; 
    } 
} 

OR

switch(way) 
{ 
    case 1: 
     for(int i=0;i<count;i++) 
     { 
      doWork1(i); 
     } 
     break; 
    case 2: 
     for(int i=0;i<count;i++) 
     { 
      doWork2(i); 
     } 
     break; 
    case 3: 
     for(int i=0;i<count;i++) 
     { 
      doWork3(i); 
     } 
     break; 
} 

、常にすべての反復でスイッチケース条件をチェックのオーバーヘッドがあるように起こります。 2番目のケースでは、オーバーヘッドは存在しません。私は第二のケースがはるかに良いと感じています。誰かが他の回避策を持っているなら、それを提案する際に私を助けてください。

あなたが何か行うことができ
+0

これを試してください:http://stackoverflow.com/questions/445067/if-vs-switch-speed – Shiham

+1

[対策](http://msdn.microsoft.com/en-us/library) /system.diagnostics.stopwatch.aspx)それは? –

+0

@TimSchmelter私のプロジェクトは銀色で、ストップウォッチはここでは利用できません。 – vaibhav

答えて

5

switchは、連続した値がです。微妙にです。このタイプのジャンプは、高度に最適化された処理を備えています。率直に言って、あなたが求めるものは、の違いは全くありません。の場合は、ほとんどの場合 - 何かdoWork2(i);はこれを沼にしようとしています。ちょっと、自体の仮想電話はそれを沼にするかもしれません。

本当に本当に重要なことは(私はここで実際のシナリオを考えるのに苦労します)、それを測定します。それが目立つどんなシナリオであれば、ちょうどあなたのの実際の、正確なコードと測定される方法 - あなたはピコ最適化を一般化することはできません。

ので:それは

  • 対策を重要ではありません

  • +0

    'switch'(IL)ジャンプテーブルのみを参照していますか?そうでなければ、プロパティを評価するために高価な 'way 'を検討してください - ジャンプテーブルを除いてコンパイラの最適化の機会はありますか? – fjdumont

    2

    Func(void, int> doWork; 
    switch(way) 
    { 
        case 1: 
         doWork = doWork1; 
         break; 
        case 2: 
         doWork = doWork2; 
         break; 
        case 3: 
         doWork = doWork3; 
         break; 
    } 
    for (int i=0;i<count;i++) 
    { 
        doWork(i); 
    } 
    

    (ここで書かれ、コードはかなりちょうどあなたのアイデアを与えるために、コンパイルされない場合がありますが...)

    +0

    私はそれをチェックアウトします。それがうまくいくようです – vaibhav

    0

    第二の方法は、より効率的です。あなたはループの完全なを完了する必要があります。しかし、最初の方法では、ケースステートメント回を不必要に繰り返すことになります。

    +0

    2番目のコードで起こった可能性があるより多くの最適化があった場合、それはより良いでしょう:) – vaibhav

    +0

    @vaibhav *パフォーマンス*の最適化を意味する、またはあなたの冗長性を減らすコード(DRY原則)?少なくとも、あなたが提供した情報で、それがより効率的になる方法はわかりません。 – McGarnagle

    +0

    2番目のコードの冗長性を減らして読みやすくするために、冗長コードがパフォーマンスを妨げるかどうかを知りたかった – vaibhav

    1

    あなたはそれが(私は最適化する価値があるかどうかを確認するためにそれを測定すべき問題ではありません。非常に確かにit's not)。個人的には、私は読みやすさと簡潔さのために最初のほうを好む(少ないコードで、エラーの発生が少ない、より多くの "dry")。

    ここでも、より簡潔である別のアプローチがあります:

    for(int i = 0; i < count; i++) 
    { 
        doAllWays(way, i); // let the method decide what to do next 
    } 
    

    すべて「の方法は、」そう、彼らは同じswitchには表示されません、releatedているように見えます。したがって、最初にswitchを実行する1つの方法でそれらをバンドルすることは意味があります。

    2

    私は最適化のために自分自身にすべての

    1. 最初に質問をしたい、どのように大きなカウントされますか?それは1,2,10,10000000000ですか?
    2. コードを実行するマシンはどれくらい強力ですか?
    3. 少ないコードを書いていますか?
    4. 私はそれを書いた後、誰かがこのコードを読むつもりですか?もしそうなら 彼はプロですか?
    5. 私は何が欠けていますか?時間?速度 ?他に何か?
    6. wayとは何ですか?どこから手に入れますか?確率 はwayの1または2または3となるでしょうか?

    iがスイッチに到達するまでの最初のコードスニペットは、カウントに達しても、カウントはどれくらいになるのでしょうか?非常に大きな数字でない場合は問題になりませんか?それが大きすぎて、あなたが非常に遅いランニングタイムを得るなら、それは役に立たない。しかし、読みやすさを欲しがり、カウントが小さいことを保証できると言ったように、なぜ最初のものを使用しないのですか?それは第2のものよりもはるかに読みやすく、私が好きなコードは少なくなっています。

    2番目のスニペットは醜いですが、カウントが大きい場合は優先する必要があります。 (スイッチは、ほとんどの場合は本当に速い、本当にのように)あなたがここにパフォーマンスの問題を持っていると仮定し

    0

    あなたは、あなたのswitch文について気にしている場合は、私がここにリファクタリングを適用する示唆しています。

    スイッチは、(スイッチされた値 はforループで変更されていないため、全く切り替える必要はありません)戦略パターンで簡単に置き換えることができます。

    実際の最適化対象はループのものですが、コンテキストなしでは はそれについて何ができるのか分かりにくいです。ここ

    がリファクタリングスイッチのいくつかの詳細情報(例えば戦略パターン)である

    CodeProject Article on refactoring switch
    1

    実際には、ここでは、コメントの一部にもかかわらず、多少速くすることができます。

    だが、実際にそれをテストしてみましょう:

    using System; 
    using System.Diagnostics; 
    
    namespace Demo 
    { 
        class Program 
        { 
         static void Main(string[] args) 
         { 
          int count = 1000000000; 
    
          Stopwatch sw = Stopwatch.StartNew(); 
    
          for (int way = 1; way <= 3; ++way) 
           test1(count, way); 
    
          var elapsed1 = sw.Elapsed; 
          Console.WriteLine("test1() took " + elapsed1); 
    
          sw.Restart(); 
    
          for (int way = 1; way <= 3; ++way) 
           test2(count, way); 
    
          var elapsed2 = sw.Elapsed; 
          Console.WriteLine("test2() took " + elapsed2); 
    
          Console.WriteLine("test2() was {0:f1} times as fast.", + ((double)elapsed1.Ticks)/elapsed2.Ticks); 
         } 
    
         static void test1(int count, int way) 
         { 
          for (int i = 0; i < count; ++i) 
          { 
           switch (way) 
           { 
            case 1: doWork1(); break; 
            case 2: doWork2(); break; 
            case 3: doWork3(); break; 
           } 
          } 
         } 
    
         static void test2(int count, int way) 
         { 
          switch (way) 
          { 
           case 1: 
            for (int i = 0; i < count; ++i) 
             doWork1(); 
            break; 
    
           case 2: 
            for (int i = 0; i < count; ++i) 
             doWork2(); 
            break; 
    
           case 3: 
            for (int i = 0; i < count; ++i) 
             doWork3(); 
            break; 
          } 
         } 
    
         static void doWork1() 
         { 
         } 
    
         static void doWork2() 
         { 
         } 
    
         static void doWork3() 
         { 
         } 
        } 
    } 
    

    doWork()メソッドは何もしないので、今これは、かなり非現実的です。しかし、それは私たちにベースラインのタイミングを与えるでしょう。私は私のWindows 7のx64システム上のリリースビルドのために得る

    結果は以下のとおりです。

    test1() took 00:00:03.8041522 
    test2() took 00:00:01.7916698 
    test2() was 2.1 times as fast. 
    

    だから、switch文の中にループを移動すると、TWICE AS FAST THANそれはMOREます。今

    doWork()の中にいくつかのコードを追加してのは、それを少しより現実的にしましょう:

    test1() took 00:00:03.9153776 
    test2() took 00:00:05.3220507 
    test2() was 0.7 times as fast. 
    

    今ではにループを入れても遅いです:

    using System; 
    using System.Diagnostics; 
    
    namespace Demo 
    { 
        class Program 
        { 
         static void Main(string[] args) 
         { 
          int count = 1000000000; 
    
          Stopwatch sw = Stopwatch.StartNew(); 
    
          for (int way = 1; way <= 3; ++way) 
           test1(count, way); 
    
          var elapsed1 = sw.Elapsed; 
          Console.WriteLine("test1() took " + elapsed1); 
    
          sw.Restart(); 
    
          for (int way = 1; way <= 3; ++way) 
           test2(count, way); 
    
          var elapsed2 = sw.Elapsed; 
          Console.WriteLine("test2() took " + elapsed2); 
    
          Console.WriteLine("test2() was {0:f1} times as fast.", + ((double)elapsed1.Ticks)/elapsed2.Ticks); 
         } 
    
         static int test1(int count, int way) 
         { 
          int total1 = 0, total2 = 0, total3 = 0; 
    
          for (int i = 0; i < count; ++i) 
          { 
           switch (way) 
           { 
            case 1: doWork1(i, ref total1); break; 
            case 2: doWork2(i, ref total2); break; 
            case 3: doWork3(i, ref total3); break; 
           } 
          } 
    
          return total1 + total2 + total3; 
         } 
    
         static int test2(int count, int way) 
         { 
          int total1 = 0, total2 = 0, total3 = 0; 
    
          switch (way) 
          { 
           case 1: 
            for (int i = 0; i < count; ++i) 
             doWork1(i, ref total1); 
            break; 
    
           case 2: 
            for (int i = 0; i < count; ++i) 
             doWork2(i, ref total2); 
            break; 
    
           case 3: 
            for (int i = 0; i < count; ++i) 
             doWork3(i, ref total3); 
            break; 
          } 
    
          return total1 + total2 + total3; 
         } 
    
         static void doWork1(int n, ref int total) 
         { 
          total += n; 
         } 
    
         static void doWork2(int n, ref int total) 
         { 
          total += n; 
         } 
    
         static void doWork3(int n, ref int total) 
         { 
          total += n; 
         } 
        } 
    } 
    

    今、私はこれらの結果を取得スイッチ!このような直感的ではない結果は、こうした種類の典型であり、コードを最適化しようとしているときにタイミングテストを実行する必要がある理由を示しています。 (そして、このようなコードを最適化するのは、通常、ボトルネックがあると疑う十分な理由がない限り、行うべきではありません。コードを整理するのが良いでしょう。))

    I他のいくつかのテストを行い、少し簡単なdoWork()メソッドの場合は、test2()メソッドが高速でした。これは、JITコンパイラが最適化を使用してできることに大きく依存します。

    注:私の2番目のテストコードの速度の違いは、JITコンパイラがdoWork()の呼び出しをインライン展開するときに 'ref'呼び出しを最適化できるためです。 in test1(); test2()の場合は(何らかの理由で)できません。

    関連する問題