Given the following classes:
class Base
{
public:
virtual ~Base() = default;
virtual void Call() = 0;
virtual void CallParams(int a, int b) = 0;
};
class A : public Base
{
public:
void Call() override
{
calls++;
position += calls % 10;
}
void CallParams(int a, int b) override
{
calls++;
position += calls % b;
}
int calls = 0;
int position = 0;
};
class B : public Base
{
public:
void Call() override final
{
calls++;
position += calls % 10;
}
void CallParams(int a, int b) override
{
calls++;
position += calls % b;
}
int calls = 0;
int position = 0;
};
I wanted to compare the performance of various call types:
- virtual
- direct member calls
- std::function
- boost::function
- My hacky implementation of a std function like object
In my implementation I essentially just get the member function pointer and object pointer then cast to what GCC internally expects, func(void*,params...).
I tried building with clang but it doesn't like my member function to void* cast.
Results:
Debug
g++-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
`-g -std=gnu++20`
| ns/op | op/s | err% | total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
| 30.49 | 32,793,129.79 | 2.0% | 8.57 | `base->Call()`
| 23.01 | 43,458,913.64 | 1.9% | 6.48 | `a->Call()`
| 21.77 | 45,926,564.46 | 2.3% | 6.22 | `b->Call()`
| 99.60 | 10,040,487.24 | 1.6% | 27.72 | `function &Base::Call, base`
| 90.66 | 11,030,408.01 | 1.8% | 25.21 | `function &A::Call, a`
| 91.46 | 10,934,136.38 | 1.4% | 25.37 | `function &B::Call, b`
| 83.14 | 12,027,721.56 | 1.7% | 23.06 | `boost function &Base::Call, base`
| 75.94 | 13,168,925.84 | 2.3% | 21.04 | `boost function &A::Call, a`
| 72.89 | 13,718,884.93 | 2.3% | 20.33 | `boost function &B::Call, b`
| 31.40 | 31,847,168.31 | 2.7% | 8.71 | `function pointer direct base, &Base::Call `
| 24.96 | 40,071,270.39 | 2.9% | 7.04 | `function pointer direct a, &A::Call`
| 26.24 | 38,110,580.64 | 1.8% | 7.36 | `function pointer direct b, &B::Call`
| 37.16 | 26,907,735.45 | 2.0% | 10.19 | `Sean::function a|b, &A|B::Call`
| 33.42 | 29,924,713.87 | 1.8% | 9.23 | `Sean::function a, &A::Call`
| 31.93 | 31,318,966.50 | 2.3% | 8.83 | `Sean::function b, &B::Call`
| 26.97 | 37,078,341.54 | 2.8% | 7.52 | `Sean::function direct a|b, &A|B::Call`
| 21.92 | 45,629,125.73 | 2.6% | 6.13 | `Sean::function direct a, &A::Call`
| 21.73 | 46,022,102.98 | 2.9% | 6.07 | `Sean::function direct b, &B::Call`
| 33.24 | 30,086,109.45 | 3.0% | 9.23 | `Sean::function a|b, &A|B::CallParams`
| 31.82 | 31,428,888.00 | 2.9% | 8.80 | `Sean::function a, &A::CallParams`
| 32.14 | 31,111,968.50 | 2.5% | 8.86 | `Sean::function b, &B::CallParams`
| 28.15 | 35,519,154.34 | 3.2% | 7.86 | `Sean::function direct a|b, &A|B::CallParams`
| 22.08 | 45,284,728.74 | 3.7% | 6.16 | `Sean::function direct a, &A::CallParams`
| 21.74 | 45,999,870.49 | 3.3% | 6.10 | `Sean::function direct b, &B::CallParams`
| 33.97 | 29,440,925.19 | 2.9% | 9.44 | `template sean::function a|b, &A|B::CallParams`
| 31.43 | 31,813,589.58 | 2.2% | 8.70 | `template sean::function a, &A::CallParams`
| 31.62 | 31,620,852.76 | 2.3% | 8.76 | `template sean::function b, &B::CallParams`
| 33.81 | 29,576,909.20 | 2.4% | 9.39 | `template check sean::function a|b, &A|B::CallParams`
| 30.79 | 32,481,714.89 | 2.9% | 8.54 | `template check sean::function a, &A::CallParams`
| 29.19 | 34,256,903.56 | 3.1% | 8.16 | `template check sean::function b, &B::CallParams`