Library code:
class Resource
{
public:
typedef void (*func_sig)(int, char, double, void*);
//Registration
registerCallback(void* app_obj, func_sig func)
{
_app_obj = app_obj;
_func = func;
}
//Calling when the time comes
void call_app_code()
{
_func(231,'a',432.4234,app_obj);
}
//Other useful methods
private:
void* app_obj;
func_sig _func;
//Other members
};
Application Code:
class App
{
public:
void callme(int, char, double);
//other functions, members;
};
void callHelper(int i, char c, double d, void* app_obj)
{
static_cast<App*>(app_obj)->callme(i,c,d);
}
int main()
{
App a;
Resource r;
r.registercallback(&a, callHelper);
//Do something
}
The above is a minimal implementation of callback mechanism. It is more verbose, doesn't support binding, placeholders etc., like std::function.
If I use a std::function
or boost::function
for the above usecase, will there be any performance drawbacks? This callback is going to be in the very very critical path of a real time application. I heard that boost::function uses virtual functions to do the actual dispatch. Will that be optimized out if there are no binding/placeholders involved?
Update
For those interested in inspecting the assemblies in latest compilers: https://gcc.godbolt.org/z/-6mQvt
I run a quick benchmark using Google Benchmark Those are the results:
Run on (4 X 2712 MHz CPU s)
----------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------
RawFunctionPointer 11 ns 11 ns 56000000
StdBind 12 ns 12 ns 64000000
StdFunction 11 ns 11 ns 56000000
Lambda 9 ns 9 ns 64000000
It seems that the most optimal solution is using lambdas (just like user christianparpart mentioned in this thread). The code I used for benchmark can be found below.
#include <benchmark/benchmark.h>
#include <cstdlib>
#include <cstdio>
#include <functional>
static volatile int global_var = 0;
void my_int_func(int x)
{
global_var = x + x + 3;
benchmark::DoNotOptimize(global_var);
benchmark::DoNotOptimize(x);
}
static void RawFunctionPointer(benchmark::State &state)
{
void (*bar)(int) = &my_int_func;
srand (time(nullptr));
for (auto _ : state)
{
bar(rand());
benchmark::DoNotOptimize(my_int_func);
benchmark::DoNotOptimize(bar);
}
}
static void StdFunction(benchmark::State &state)
{
std::function<void(int)> bar = my_int_func;
srand (time(nullptr));
for (auto _ : state)
{
bar(rand());
benchmark::DoNotOptimize(my_int_func);
benchmark::DoNotOptimize(bar);
}
}
static void StdBind(benchmark::State &state)
{
auto bar = std::bind(my_int_func, std::placeholders::_1);
srand (time(nullptr));
for (auto _ : state)
{
bar(rand());
benchmark::DoNotOptimize(my_int_func);
benchmark::DoNotOptimize(bar);
}
}
static void Lambda(benchmark::State &state)
{
auto bar = [](int x) {
global_var = x + x + 3;
benchmark::DoNotOptimize(global_var);
benchmark::DoNotOptimize(x);
};
srand (time(nullptr));
for (auto _ : state)
{
bar(rand());
benchmark::DoNotOptimize(my_int_func);
benchmark::DoNotOptimize(bar);
}
}
BENCHMARK(RawFunctionPointer);
BENCHMARK(StdBind);
BENCHMARK(StdFunction);
BENCHMARK(Lambda);
BENCHMARK_MAIN();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With