簡體   English   中英

C 和 C++ 之間運行時多態性的性能差異

[英]Performance differences in run-time polymorphism between C and C++

我知道基准測試是一個非常微妙的主題,簡單的、沒有經過深思熟慮的基准測試對於性能比較幾乎沒有意義,但我現在所擁有的實際上是一個非常小的和做作的例子,我認為應該很容易解釋。 所以,即使這個問題看起來沒有幫助,它至少會幫助我理解基准測試。

所以,這里我是 go。

我試圖在 C 中嘗試簡單的 API 設計,通過void *使用運行時多態性行為。 然后我將它與使用常規虛函數在 C++ 中實現的相同事物進行了比較。 這是代碼:

#include <cstdlib>
#include <cstdio>
#include <cstring>

int dummy_computation()
{
    return 64 / 8;
}

/* animal library, everything is prefixed with al for namespacing */
#define AL_SUCCESS 0;
#define AL_UNKNOWN_ANIMAL 1;
#define AL_IS_TYPE_OF(animal, type) \
    strcmp(((type *)animal)->animal_type, #type) == 0\

typedef struct {
    const char* animal_type;
    const char* name;
    const char* sound;
} al_dog;

inline int make_dog(al_dog** d) {
    *d = (al_dog*) malloc(sizeof(al_dog));
    (*d)->animal_type = "al_dog";
    (*d)->name = "leslie";
    (*d)->sound = "bark";
    return AL_SUCCESS;
}

inline int free_dog(al_dog* d) {
    free(d);
    return AL_SUCCESS;
}
    
typedef struct {
    const char* animal_type;
    const char* name;
    const char* sound;
} al_cat;

inline int make_cat(al_cat** c) {
    *c = (al_cat*) malloc(sizeof(al_cat));
    (*c)->animal_type = "al_cat";
    (*c)->name = "garfield";
    (*c)->sound = "meow";
    return AL_SUCCESS;
}

inline int free_cat(al_cat* c) {
    free(c);
    return AL_SUCCESS;
}

int make_sound(void* animal) {
    if(AL_IS_TYPE_OF(animal, al_cat)) {
        al_cat *c = (al_cat*) animal;
        return dummy_computation();
    } else if(AL_IS_TYPE_OF(animal, al_dog)) {
        al_dog *d = (al_dog*) animal;
        return dummy_computation();
    } else {
        printf("unknown animal\n");
        return 0;
    }
}
/* c style library finishes here */

/* cpp library with OOP */
struct animal {
    animal(const char* n, const char* s) 
    :name(n)
    ,sound(s)
    {} 
    virtual int make_sound() {
        return dummy_computation();
    }
    const char* name;
    const char* sound;
};

struct cat : animal {
    cat() 
    :animal("garfield", "meow")
    {}
};

struct dog : animal {
    dog() 
    :animal("leslie", "bark")
    {}
};
/* cpp library finishes here */ 

我有一個叫做dummy_computation的東西,只是為了確保我在基准測試中得到一些計算。 對於這樣的示例,我通常會實現不同的printf調用來進行吠叫、喵喵叫等,但printf在快速基准測試中不容易進行基准測試。com。 我想要進行基准測試的實際事情是運行時多態性實現。 所以這就是為什么我選擇制作一些小的 function 並在 C 和 C++ 實現中使用它作為填充物。

現在,在 quick-benchmarks.com 中,我有一個如下基准:

static void c_style(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    al_dog* d = NULL;
    al_cat* c = NULL;

    make_dog(&d);
    make_cat(&c);
    
    int i1 = make_sound(d);
    benchmark::DoNotOptimize(i1);
    int i2 = make_sound(c);
    benchmark::DoNotOptimize(i2);

    free_dog(d);
    free_cat(c);
  }
}
// Register the function as a benchmark
BENCHMARK(c_style);

static void cpp_style(benchmark::State& state) {
  for (auto _ : state) {
    animal* a1 = new dog();
    animal* a2 = new cat();
    int i1 = a1->make_sound();
    benchmark::DoNotOptimize(i1);
    int i2 = a2->make_sound();
    benchmark::DoNotOptimize(i2);
    delete a1;
    delete a2;
  }
}
BENCHMARK(cpp_style); 

我添加了DoNotOptimize調用,以便虛擬調用最終不會被優化。

整個基准可以在這里找到,如果重新創建它看起來很痛苦。

https://quick-bench.com/q/ezul9hDXTjfSWijCfd2LMUUEH1I

現在,令我驚訝的是,C 版本的結果快了 27 倍。 我預計 C++ 版本可能會出現一些性能問題,因為它是一個更完善的解決方案,但絕對不是 27 倍。

有人可以解釋這些結果嗎? 與 C 相比,虛擬 function 調用真的會產生這么多開銷嗎? 還是我設置這個基准測試的方式完全沒有意義? 如果是這樣,如何更正確地對此類問題進行基准測試?

這是因為你沒有實現同樣的事情。 如果您在 C 中執行switch鏈的if鏈,那么您(在數學上)有一個可區分的聯合,即 C++ 中的std::variant

如果您希望將 C++ 版本移植到 C,那么您需要 function 指針。 它很可能會同樣緩慢。 背后的原因, virtual意味着向前兼容:任何代碼,包括稍后加載的庫,都可以從您的基礎下降並實現virtual方法。 這意味着,有時您甚至在基本模塊的編譯時都不知道它可能需要處理哪些(后代)類(類型系統是開放的)。 沒有為std::variant提供這種前向兼容性,它是封閉的(僅限於固定的類型列表)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM