图书馆代码:
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
);
}
}