图书馆代码:
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,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.
如果我使用std :: function或boost ::函数为上述用例,会有任何性能缺陷吗?这种回调将在实时应用程序的非常关键的路径中.我听说boost :: function使用虚拟函数做实际的调度.如果没有约束/占位符参与,会被优化吗?
解决方法
我很经常想到自己,所以我开始写一些非常小的基准,试图通过循环的原子计数器来模拟每个函数指针回调版本的性能.
请记住,这些都是对只做一件事的功能的裸露的调用,原子地增加其计数器;
通过检查生成的汇编器输出,您可以发现,一个裸C函数指针循环被编译成3个cpu指令;
C 11的std ::函数调用仅添加了2个cpu指令,因此在我们的示例中增加了5个.作为一个结论:绝对不用你使用什么方式的函数指针技术,开销差异在任何情况下都非常小.
((但令人困惑的是,分配的lambda表达式似乎运行速度比其他,甚至比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,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,&cxx_main,nullptr); pthread_create(&cxo1_thread,&cxo1_main,nullptr); pthread_create(&virt_thread,&virt_main,nullptr); pthread_create(&lambda_thread,&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 ); } }