3

以前に質問したのですが、dynamic_cast はなぜ悪かそうでないのですか? 答えは、次のように のパフォーマンスに関するコードを書くことdynamic_castになりました.そして、私がコンパイルしてテストしたところ、 によって費やされた時間は、そうでないdynamic_cast場合よりもわずかに大きくなりました.時間がかかるdynamic_castという証拠は見られませんでしdynamic_castた.正しいコードを書きましたか?

コードは次のとおりです。

class Animal
{
public:
    virtual ~Animal(){};
};

class Cat : public Animal
{
public:
    std::string param1;
    std::string param2;
    std::string param3;
    std::string param4;
    std::string param5;
    int param6;
    int param7;
};

bool _process(Cat* cat)
{
    cat->param1 = "abcde";
    cat->param2 = "abcde";
    cat->param3 = "abcde";
    cat->param4 = "abcde";
    cat->param5 = "abcde";
    cat->param6 = 1;
    cat->param7 = 2;
    return true;
}

bool process(Animal *ptr)
{
    Cat *cat = dynamic_cast<Cat*>(ptr);
    if (cat == NULL)
    {
        return false;
    } 
    _process(cat);
    return true;
}
int main(int argc, char* argv[])
{
    /*
    argv[1] : object num
    */

    if (argc != 2)
    {
        std::cout << "Error: invalid argc " << std::endl;
        return -1;
    }

    int obj_num = atoi(argv[1]);
    if (obj_num <= 0)
    {
        std::cout << "Error: object num" << std::endl;
    }

    int c = 0;
    for (; c < obj_num; c++)
    {
        Cat cat;
        #ifdef _USE_CAST
        if (!process(&cat))
        {
            std::cout << "Error: failed to process " << std::endl;
            return -3;
        }
        #else
        if (!_process(&cat))
        {
            std::cout << "Error: failed to process " << std::endl;
            return -3;
        }

        #endif
    }

    return 0;
}

次を使用してコンパイルします。

g++ -D_USE_CAST -o dynamic_cast_test  dynamic_cast_benchmark.c
g++ -o dynamic_cast_no_test dynamic_cast_benchmark.c

1,10,100 ... である num を使用してそれらを実行します。

$time ./dynamic_cast_test num
$time ./dynamic_cast_no_test num

結果:

                 dynamic_cast               non_dynamic_cast
num  10,000   
                real    0m0.010s            real    0m0.008s
                user    0m0.006s            user    0m0.006s
                sys     0m0.001s            sys     0m0.001s

     100,000 
                real    0m0.059s            real    0m0.056s
                user    0m0.054s            user    0m0.054s
                sys     0m0.001s            sys     0m0.001s

     1,000,000
                real    0m0.523s            real    0m0.519s
                user    0m0.517s            user    0m0.511s
                sys     0m0.001s            sys     0m0.004s

     10,000,000
                real    0m6.050s            real    0m5.126s
                user    0m5.641s            user    0m4.986s
                sys     0m0.036s            sys     0m0.019s

     100,000,000
                real    0m52.962s           real    0m51.178s
                user    0m51.697s           user    0m50.226s
                sys     0m0.173s            sys     0m0.092s

ハードウェアと OS:

OS:Linux
CPU:Intel(R) Xeon(R) CPU E5607  @ 2.27GHz  (4 cores)
4

2 に答える 2

1

あなたは正しいコードを書きましたが、型を Cat にハードコードすることはしませんでした。安全のために、コマンドライン引数を使用して、猫を作成するか、たとえば犬を作成するかを決定できます (これも実装する必要があります)。最適化が重要な役割を果たしているかどうかを確認するために、最適化も無効にしてみてください。

最後に、注意事項があります。プロファイリングは、コンピューターで測定を行うほど単純ではないため、自分が行っていることはそれだけにとどまることを認識しておく必要があります。それはあなたにアイデアを与えますが、すべてを網羅する答えを得ているとは思わないでください。

于 2013-09-09T06:55:29.273 に答える
-1

私は自分の投稿を再構築します。

あなたのコードは正しく、うまくコンパイルされます。

仮想メソッドと dynamic_cast 演算子は関連する問題であるため、wiki からこの情報を確認してください。

ウィキ:

仮想呼び出しには、コンパイル済みポインターへの単なるジャンプである非仮想呼び出しと比較して、少なくとも追加のインデックス付き逆参照が必要であり、場合によっては「フィックスアップ」の追加が必要です。したがって、仮想関数の呼び出しは、非仮想関数の呼び出しより本質的に遅くなります。1996 年に行われた実験では、実行時間の約 6 ~ 13% が正しい関数への単純なディスパッチに費やされていることが示されていますが、オーバーヘッドは 50% にもなる可能性があります [4]。仮想関数のコストは、キャッシュがはるかに大きく、分岐予測が優れているため、最新の CPU アーキテクチャではそれほど高くない場合があります。

さらに、JIT コンパイルが使用されていない環境では、通常、仮想関数呼び出しをインライン化できません。コンパイラはルックアップと間接呼び出しを、たとえばインライン化された各本体の条件付き実行に置き換えることができますが、そのような最適化は一般的ではありません。

このオーバーヘッドを回避するために、コンパイラは通常、コンパイル時に呼び出しを解決できる場合は常に vtables の使用を避けます。

したがって、上記の f1 の呼び出しでは、vtable ルックアップが必要ない場合があります。これは、d がこの時点で D のみを保持できること、および D が f1 をオーバーライドしないことをコンパイラが判断できるためです。または、コンパイラ (またはオプティマイザ) は、プログラム内のどこにも f1 をオーバーライドする B1 のサブクラスがないことを検出できる場合があります。B1::f1 または B2::f2 の呼び出しでは、実装が明示的に指定されているため、おそらく vtable ルックアップは必要ありません (ただし、「this」ポインターの修正は必要です)。

また、おそらくご存じのとおり、クラスで仮想メソッドを宣言するときは、実現に依存しますが、ほとんどの場合、コンパイラは仮想メソッド テーブルを新しいメンバーとしてクラスに暗黙的に追加するため、このクラスの各インスタンスはより多くのメモリ空間を占有します。 、vmを使用するクラスと使用しないクラスでsizeofを試してください。

于 2013-09-09T06:43:24.823 に答える