类似 C 的回调处理:哪种算法执行速度更快?
我有一系列像这样的回调 void (*callbacks[n])(void* sender)
我想知道其中哪一个代码执行速度更快:
//Method A
void nullcallback(void* sender){};
void callbacka(void* sender)
{
printf("Hello ");
}
void callbackb(void* sender)
{
printf("world\n");
}
int main()
{
void (*callbacks[5])(void* sender);
unsigned i;
for (i=0;i<5;++i)
callbacks[i] = nullcallback;
callbacks[2] = callbacka;
callbacks[4] = callbackb;
for (i=0;i<5;++i)
callbacks[i](NULL);
};
或
//Method B
void callbacka(void* sender)
{
printf("Hello ");
}
void callbackb(void* sender)
{
printf("world\n");
}
int main()
{
void (*callbacks[5])(void* sender);
unsigned i;
for (i=0;i<5;++i)
callbacks[i] = NULL;
callbacks[2] = callbacka;
callbacks[4] = callbackb;
for (i=0;i<5;++i)
if (callbacks[i])
callbacks[i](NULL);
};
某些条件:
- 这很重要我是否知道我的大部分回调是否有效?
- 如果我使用 C 或 C++ 编译器编译代码,会有什么不同吗?
- 目标平台(Windows、Linux、Mac、iOS、Android)是否会改变结果? (这个回调数组的全部目的是为了管理游戏中的回调)
I have an array of call backs like this void (*callbacks[n])(void* sender)
and I'm wondering which one of these codes will preform faster :
//Method A
void nullcallback(void* sender){};
void callbacka(void* sender)
{
printf("Hello ");
}
void callbackb(void* sender)
{
printf("world\n");
}
int main()
{
void (*callbacks[5])(void* sender);
unsigned i;
for (i=0;i<5;++i)
callbacks[i] = nullcallback;
callbacks[2] = callbacka;
callbacks[4] = callbackb;
for (i=0;i<5;++i)
callbacks[i](NULL);
};
or
//Method B
void callbacka(void* sender)
{
printf("Hello ");
}
void callbackb(void* sender)
{
printf("world\n");
}
int main()
{
void (*callbacks[5])(void* sender);
unsigned i;
for (i=0;i<5;++i)
callbacks[i] = NULL;
callbacks[2] = callbacka;
callbacks[4] = callbackb;
for (i=0;i<5;++i)
if (callbacks[i])
callbacks[i](NULL);
};
some conditions:
- Does it matter if I know most of my callbacks are valid or not?
- Does it make a difference if I'm compiling my code using C or C++ compiler?
- Does the target platform (windows, linux, mac, iOS, android) change any thing in the results? (the whole reason for this callback array is to manage callbacks in a game)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您必须为此查看汇编代码。在我的平台(gcc,32 位)上,我发现编译器无法优化对
nullcallback
的调用。但是,如果我将您的方法 A 改进为以下内容,编译器就能够展开循环并优化调用,结果就是
You'd have to look into the assembler code for that. On my platform (gcc, 32bit) I found that the compiler is not able to optimize the call to
nullcallback
out. But if I improve your method A to the followingthe compiler is able to unroll the loop and optimize the calls the result is just
这完全取决于你的实际情况。如果可能的话,我更喜欢方法 A,因为它更容易阅读和生成更清晰的代码,特别是如果您的函数有返回值:
当然,当您不仅有一个函数签名,而且有 100 个不同的签名时,方法 A 就会变得 tedouis 。对于每个你都必须编写一个空函数。
出于性能考虑,这取决于 nullcallback() 是否是罕见情况。如果很少见,方法A显然更快。如果不是,方法 B 可能会稍微快一些,但这取决于很多因素:您使用哪个平台,您的函数有多少参数等。但无论如何,如果您的回调正在做“真正的工作”,即。不仅仅是一些简单的计算,根本不重要。
当您不仅为一个发送者调用回调,而且为许多发送者调用回调时,您的方法 B 确实可以更快:
这里,当没有有效的回调时,将跳过整个循环。如果 nullcallback() 地址已知,也可以使用方法 A 来完成此调整,即仅在某些模块中未定义。
This totally depends on your actual situation. If possible I would prefer methode A, because it is simply easier to read and produce cleaner code, in particular if your function has a return value:
Of course methode A becomes tedouis when you have not only one function signature but let's say 100 different signature. And for each you have to write a null function.
For the performance consideration it depends if the nullcallback() is a rare case or not. If it is rare, methode A is obviously faster. If not methode B could be slightly faster, but that depends on many factors: which platform you use, how many arguments your functions have, etc. But in any case if your callbacks are doing "real work", ie. not only some simple calculations, it shouldn't matter at all.
Where your methode B could really be faster is when you not only call the callback for one sender but for very many:
Here the entire loop is skipped when there is no valid callback. This tweak can also be done with methode A if the nullcallback() address is known, ie. not defined in some module only.
您可以通过简单地对数组进行零初始化来进一步优化您的代码,如下所示:
然后您就完全消除了 for 循环将每个指针设置为 NULL 的需要。您现在只需为
callbacka
和callbackb
进行分配。You could optimize your code further by simply zero-initializing the array to start with like:
Then you've completely eliminated the need for your for-loop to set each pointer to
NULL
. You now just have to make assignments forcallbacka
andcallbackb
.对于一般情况,方法 B 是首选,但对于函数指针 LUT(当 NULL 为例外时)比方法 A 微观上更快。
主要示例是 Linux 系统调用表,NULL 调用只应在运行在较新系统上构建的二进制文件或程序员错误的极少数情况下发生。系统调用发生的频率足够高,以至于纳秒甚至皮秒的改进可以有所帮助。
其他可能证明它有价值的实例是仿真器(例如 MAME)内的操作码 LUT。
For the general case method B is preferred, but for function pointer LUTs when NULL is the exception than method A is microscopically faster.
The primary example is Linux system call table, NULL calls should only occur in rare circumstances when running binaries built on newer systems, or programmer error. Systems calls occur often enough that nanosecond or even picosecond improvements can help.
Other instances it may prove worthy is for opcode LUTs inside emulators such as MAME.