10

私はこれについて考えました:これらの2つのプラクティスにパフォーマンスの違いはありますか?

  1. 関数の戻り値を一時変数に格納してから、その変数を別の関数のパラメーターとして指定します。
  2. 関数を他の関数に入れます。

仕様

すべてのクラスと関数が正しく記述されていると仮定します。

ケース1。

ClassA a = function1();
ClassB b = function2(a);
function3(b);

ケース2。

function3(function2(function1()));

1回の実行で大きな違いはないことはわかっていますが、これをループで何度も実行できると想定して、いくつかのテストを作成しました。

テスト

#include <iostream>
#include <ctime>
#include <math.h>
using namespace std;

int main()
{
   clock_t start = clock();
   clock_t ends = clock();

   // Case 1.
   start = clock();
   for (int i=0; i<10000000; i++)
   {
      double a = cos(1);
      double b = pow(a, 2);
      sqrt(b);
   }
   ends = clock();
   cout << (double) (ends - start) / CLOCKS_PER_SEC << endl;

   // Case 2.
   start = clock();
   for (int i=0; i<10000000; i++)
      sqrt(pow(cos(1),2));
   ends = clock();
   cout << (double) (ends - start) / CLOCKS_PER_SEC << endl;
   return 0;
}

結果

  • ケース1=6.375
  • ケース2=0.031

なぜ最初のものははるかに遅いのですか、そして2番目のものが速いのならなぜ私たちはいつもそのようにコードを書かないのですか?とにかく、2番目の練習には名前がありますか?
また、最初のケースでforループの外側に変数を作成するとどうなるのか疑問に思いましたが、結果は同じでした。なんで?

4

2 に答える 2

4

計算のクランチが必要な場合は、これをすべて捨てる最適化を破り、数値がより一貫したものになります。適切な値を取得するコードが実際に実行され、完全に破棄されないようにするために、両方のテストの結果を volatile ローカルに割り当てました (これは volatile の正確な使用法ではありませんが、価値創造は重要なデルタです)。

#include <iostream>
#include <ctime>
#include <cmath>
using namespace std;

int main()
{
    clock_t start;
    volatile double val;

    for (int j=1;j<=10;j++)
    {
        // Case 1.
        start = clock();
        for (int i=0; i<2000000; i++)
        {
            double a = cos(1);
            double b = pow(a, 2);
            val = sqrt(b);
        }
        cout << j << ':' << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

        // Case 2.
        start = clock();
        for (int i=0; i<2000000; i++)
            val = sqrt(pow(cos(1),2));
        cout << j << ':' << (double) (clock() - start) / CLOCKS_PER_SEC << endl << endl;
    }
    return 0;
}

私のMacbook Airで次のリリースコンパイルされた出力を生成します(これは決してスピードデーモンではありません):

1:0.001465
1:0.001305

2:0.001292
2:0.001424

3:0.001297
3:0.001351

4:0.001366
4:0.001342

5:0.001196
5:0.001376

6:0.001341
6:0.001303

7:0.001396
7:0.001422

8:0.001429
8:0.001427

9:0.001408
9:0.001398

10:0.001317
10:0.001353
于 2012-12-19T17:08:38.200 に答える
0

上記の両方のループの適切かつ合法的な完全な最適化は、「ループを実行しない」ことです。最初のケースで初期化されていない変数を使用してコンパイラを混乱させた場合、または変数を使用したためにコンパイラが混乱した場合、または最適化レベルによって名前付き変数が実際に存在するように強制された場合がよく見られます。

C++11 では、一時変数の暗黙的な移動を含む 2 つの違いがありますが、 を使用してこれを修正できますstd::move。(よくわかりませんが、スコープ外になるローカル変数の最後の使用は、暗黙的な移動の対象となる可能性があります)。a の場合、doubleこれは違いではありませんが、より複雑な型の場合は違います。

于 2012-12-19T17:10:41.327 に答える