性能的std :: function比原始函数指针和void * this? [英] Performance of std::function compared to raw function pointer and void* this?

查看:227
本文介绍了性能的std :: function比原始函数指针和void * this?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

图书馆代码:

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
};

应用程式代码:

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
}

上面是回调的最小实现机制。它更冗长,不支持绑定,占位符等,就像std :: function。
如果我对上面的usecase使用 std :: function boost :: function 任何性能的缺点?这个回调将在一个实时应用程序的非常关键的路径。我听说boost :: function使用虚拟函数来做实际的调度。

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?

推荐答案

我很想知道自己已经很频繁,所以我开始写作一些非常小的基准,试图模拟循环的原子计数器为每个函数指针回调版本的性能。

I wondered myself quite frequently already, so I started writing some very minimal benchmark that attempts to simulate the performance by looped atomic counters for each function-pointer callback version.

请记住,这些是 bare 调用只做一件事情的函数,原子地递增它的计数器;

Keep in mind, these are bare calls to functions that do only one thing, atomically incrementing its counter;

通过检查生成的汇编器输出,你可能会发现,一个裸C函数指针循环编译成3个CPU指令;

By checking the generated assembler output you may find out, that a bare C-function pointer loop is compiled into 3 CPU instructions;

一个C ++ 11的 std :: function ,因此在我们的例子中是5。作为结论:绝对不关心你使用什么方式的函数指针技术,开销差异在任何情况下非常小。

a C++11's std::function call just adds 2 more CPU instructions, thus 5 in our example. As a conclusion: it absolutely doesn't matter what way of function pointer technique you use, the overhead differences are in any case very small.

((令人困惑的是,分配的lambda表达式似乎运行得比其他人更快,甚至比C-one更快。))

((Confusing however is that the assigned lambda expression seems to run faster than the others, even than the C-one.))

编译示例: clang ++ -o tests / perftest-fncb tests / perftest-fncb.cpp -std = c ++ 11 -pthread -lpthread -lrt -O3 -march = native -mtune = native

#include <functional>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

typedef unsigned long long counter_t;

struct Counter {
    volatile counter_t bare;
    volatile counter_t cxx;
    volatile counter_t cxo1;
    volatile counter_t virt;
    volatile counter_t lambda;

    Counter() : bare(0), cxx(0), cxo1(0), virt(0), lambda(0) {}
} counter;

void bare(Counter* counter) { __sync_fetch_and_add(&counter->bare, 1); }
void cxx(Counter* counter) { __sync_fetch_and_add(&counter->cxx, 1); }

struct CXO1 {
    void cxo1(Counter* counter) { __sync_fetch_and_add(&counter->cxo1, 1); }
    virtual void virt(Counter* counter) { __sync_fetch_and_add(&counter->virt, 1); }
} cxo1;

void (*bare_cb)(Counter*) = nullptr;
std::function<void(Counter*)> cxx_cb;
std::function<void(Counter*)> cxo1_cb;
std::function<void(Counter*)> virt_cb;
std::function<void(Counter*)> lambda_cb;

void* bare_main(void* p) { while (true) { bare_cb(&counter); } }
void* cxx_main(void* p) { while (true) { cxx_cb(&counter); } }
void* cxo1_main(void* p) { while (true) { cxo1_cb(&counter); } }
void* virt_main(void* p) { while (true) { virt_cb(&counter); } }
void* lambda_main(void* p) { while (true) { lambda_cb(&counter); } }

int main()
{
    pthread_t bare_thread;
    pthread_t cxx_thread;
    pthread_t cxo1_thread;
    pthread_t virt_thread;
    pthread_t lambda_thread;

    bare_cb = &bare;
    cxx_cb = std::bind(&cxx, std::placeholders::_1);
    cxo1_cb = std::bind(&CXO1::cxo1, &cxo1, std::placeholders::_1);
    virt_cb = std::bind(&CXO1::virt, &cxo1, std::placeholders::_1);
    lambda_cb = [](Counter* counter) { __sync_fetch_and_add(&counter->lambda, 1); };

    pthread_create(&bare_thread, nullptr, &bare_main, nullptr);
    pthread_create(&cxx_thread, nullptr, &cxx_main, nullptr);
    pthread_create(&cxo1_thread, nullptr, &cxo1_main, nullptr);
    pthread_create(&virt_thread, nullptr, &virt_main, nullptr);
    pthread_create(&lambda_thread, nullptr, &lambda_main, nullptr);

    for (unsigned long long n = 1; true; ++n) {
        sleep(1);
        Counter c = counter;

        printf(
            "%15llu bare function pointer\n"
            "%15llu C++11 function object to bare function\n"
            "%15llu C++11 function object to object method\n"
            "%15llu C++11 function object to object method (virtual)\n"
            "%15llu C++11 function object to lambda expression %30llu-th second.\n\n",
            c.bare, c.cxx, c.cxo1, c.virt, c.lambda, n
        );
    }
}

这篇关于性能的std :: function比原始函数指针和void * this?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆