1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C++Advent Calendar 2020

Day 13

仮想継承は遅いのか

Last updated at Posted at 2020-12-12

はじめに

C++には菱形継承への解決方法として仮想継承というものがある
これまでの人生で使う場面がなかったので知らなかったのだが、どうもこいつは遅いみたいだ
要因としては、vtableが複雑になってオーバーヘッドが増すからっぽい

ここでは、深く調べるつもりはなかったのでアセンブリを読んだりはしてないが、
参考程度に、いくつかのパターンでとったベンチマークを紹介する

ベンチマーク

使用したサイトは
https://quick-bench.com
最適化はO2を使った

計測には以下のようなclassを使用した

struct Super{virtual ~Super()=default;};

struct Sub : Super{};
struct Sub2 : Super{};
struct VSub : virtual Super{};
struct VSub2 : virtual Super{};

今回は上記のように、継承したか、仮想継承したかで比較しているだけなので
菱形継承とか複雑化していくにつれて、結果が大きく変わってくることはあるかもしれない。

生成 ~ 破棄

以下のように生成し、基底クラスにアップキャストして破棄されるだけのコードで計測してみた

    Super* ptr = new Sub{};
    delete ptr;
------------- vs --------------
    Super* ptr = new VSub{};
    delete ptr;

image.png

これはあまり変わらなかった。

vtableが複雑になるぶん、初期化コストがかかったりしないのかな?

一応確認してみて、仮想継承してるほうが遅そうなものの
僅かな差すぎてベンチマークでは大きな差がでてこなかったっぽそう

dynamic_cast

いろいろな組あわせでdynamic_castによる型チェックの計測をしてみた
こちらも基底クラスにアップキャストしたものから行う

    Super* ptr = new Sub{};
    bool result = dynamic_cast<Sub*>(ptr) != nullptr;
------------- vs --------------
    Super* ptr = new VSub{};
    bool result = dynamic_cast<VSub*>(ptr) != nullptr;

https://quick-bench.com/q/lnApnNVNEYX_VFo0iDOH7lPMdpQ
image.png

仮想継承した派生クラスから他の派生クラスへdynamic_castする際は重たそうだ

仮想関数呼び出し

仮想関数を追加して速度比較

    Super* ptr = new Sub{};
    ptr->func();
------------- vs --------------
    Super* ptr = new VSub{};
    ptr->func();

image.png

これはあまり変わらない。
vtableの仕組み的には自然に思う。

typeid

いろいろな組あわせでtypeidによる型チェックの計測をしてみた
こちらも基底クラスにアップキャストしたものから行う

    Super* ptr = new Sub{};
    bool result = typeid(*ptr) == typeid(Sub);
------------- vs --------------
    Super* ptr = new VSub{};
    bool result = typeid(*ptr) == typeid(VSub);

image.png

これも大きな違いはなさそうだった

アップキャスト

今度は関数引数に渡す際のアップキャストで計測してみた

    bool Func(Super*);

    Sub* ptr = new Sub{};
    bool result = Func(ptr);
------------- vs --------------
    VSub* ptr = new VSub{};
    bool result = Func(ptr);

image.png

これもそんな大きな差はなさそう

まとめ

  • 仮想継承してるほうが、わずかに遅そう
  • dynamic_castを使う際は速度に大きな差が見えたが、それ以外のところでは大きな差はでなかった
    • ひし形継承など複雑になれば、大きなボトルネックになってくる可能性はあるのかも?

$\huge{仮想継承も菱形継承もしないに越したことはない}$

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?