Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance of std::function compared to raw function pointer and void* this?

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

like image 442
balki Avatar asked Jan 13 '13 18:01

balki


1 Answers

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();
like image 86
Kamil Kuczaj Avatar answered Nov 03 '22 02:11

Kamil Kuczaj