4

次のプログラムを作成して、自分のマシンでの仮想機能のコストをテストしました。

#include <iostream>
#include <ctime>
#define NUM_ITER 10000000000
//   5 seconds = 1000000000

static volatile int global_a;

void spin()
{
    int a = global_a;
    int b = a*a;
    int c = a+5;
    int d = a^b^c;
    global_a = b*d;
}

struct A {
    virtual void a() = 0;
};

struct B : A {
    virtual void a() { spin(); }
};

struct C : A {
    virtual void a() { spin(); }
};

void run_A1(A* a)
{
    a->a();
}

void run_A(A* a)
{
    for (long long i = 0; i < NUM_ITER; i++) {
        run_A1(a);
    }
}

void run()
{
    for (long long i = 0; i < NUM_ITER; i++) {
        spin();
    }
}

int main()
{
    global_a = 2;

    A* a1 = new B;
    A* a2 = new C;

    std::clock_t c_begin, c_end;

    c_begin = std::clock();
    run_A(a1);
    c_end = std::clock();

    std::cout << "Virtual | CPU time used: "
              << 1000.0 * (c_end-c_begin) / CLOCKS_PER_SEC
              << " ms\n";

    c_begin = std::clock();
    run_A(a2);
    c_end = std::clock();

    std::cout << "Virtual | CPU time used: "
              << 1000.0 * (c_end-c_begin) / CLOCKS_PER_SEC
              << " ms\n";

    c_begin = std::clock();
    run();
    c_end = std::clock();

    std::cout << "Normal  | CPU time used: "
              << 1000.0 * (c_end-c_begin) / CLOCKS_PER_SEC
              << " ms\n";

    delete a1;
    delete a2;
}

結果は予想とは逆で、仮想関数は一貫して高速でした。たとえば、これは私が得た出力の1つですNUM_ITER = 10000000000:

Virtual | CPU time used: 49600 ms
Virtual | CPU time used: 50270 ms
Normal  | CPU time used: 52890 ms

結果のアセンブラー ファイルの分析から、コンパイラーが重要なものを最適化していないことを確認できます。次のオプションで GCC-4.7 を使用しました。

g++ -O3 -std=c++11 -save-temps -masm=intel -g0 -fno-exceptions -fno-inline test.cc -o test

仮想関数の呼び出しが速いのはなぜですか? または、非仮想関数の呼び出しが遅いのはなぜですか? 分岐予測子は非常に優れていますか? または、それは私のマシンだけかもしれません。たぶん、誰かが彼のタイミングをテストして投稿することもできますか?

4

2 に答える 2

4

global_aへの各呼び出しの前にリセットしてみてくださいrun():

void run()
{
    global_a = 2;

    ...
}

void run_A(A *a)
{    
    global_a = 2;

    ...
}

これが何らかの影響を与えているかどうかはわかりませんが、すべての数学演算に同じ時間がかかるわけではありません!

于 2012-05-27T16:02:33.463 に答える
2

コンパイラは、仮想関数がグローバル関数spin()を呼び出してそれらを非仮想化することを確認するのに十分賢いかもしれません。呼び出しもおそらくインライン化されます。

これを確認してください。

于 2012-05-27T16:11:59.847 に答える