假设我需要在两个函数中选择一个来动态调用(就像我们在C++中的vtable中所做的那样):
common.h
#include<stdlib.h>
#include<time.h>
#define TEST_N 100000000
static volatile int state = 0;
static void foo1() {
state = 1;
}
static void foo2() {
state = 2;
}
test1.c
#include"common.h"
int foo_choose(){
if (rand()%2 == 0){
return 2;
}
return 1;
}
int main(){
time_t t;
srand(time(&t));
for(size_t i = 0; i<TEST_N; i++){
switch(foo_choose()){
case 1:
foo1();
break;
case 2:
foo2();
break;
}
}
return 0;
}
test2.c
#include"common.h"
void (*foo_choose(void))(){
if (rand()%2 == 0){
return &foo2;
}
return &foo1;
}
int main(){
time_t t;
srand(time(&t));
for(size_t i = 0; i<TEST_N; i++){
(foo_choose())();
}
return 0;
}
我试着比较一下表演。显然,在带有-O1
或-O3
的ARM64 apple clang上,test1
明显快于test2
。使用gcc12,无论我们如何设置优化级别,它们都差不多快。我已经看过生产的组件,但没有发现任何重要的东西。
有谁知道为什么我们会有性能上的差异吗?有没有什么原因可以解释为什么当代码比上面的演示复杂得多时,一般来说应该更快?
在C中执行动态分派的更好方法是test1
还是test2
?毕竟,test1
更灵活,因为它允许处理不是函数指针的东西。
编辑:如果很难决定,请分享一些想法。如果这取决于体系结构,那么最好是同时编写适用于所有可能的体系结构的代码,那么有没有一种好的方法呢?
1条答案
按热度按时间l3zydbqr1#
我不认为C和C在这方面有太大的区别。
动态分派速度较慢的可能性是可以理解的,因为它不能内联调用的函数,这意味着不能优化那么多,这是一个额外的间接。
选择(不同的)随机数可能不是测试的好主意。不同的数字序列可能会影响结果(我不知道兰德()函数的成本有多确定)。你可以预先计算随机数序列。
我认为,当它不是在一个紧密的循环中,并且被调用的函数不是太短时,你不应该太担心选择一个解决方案而不是另一个解决方案。如果你想提高性能,较短的函数应该是可内联的。
还有一个令人担忧的因素,在很多情况下可能更重要:分支预测
失败的预测是昂贵的。如果你以一种可预测的方式分支,例如1000次到foo 1,然后1000次到foo 2,那么预测几乎总是成功的。可能存在分支预测器可能识别的其他模式。(cppcon,Fedor Pikus:无分支编程)
这就是为什么它可能会更好,例如在C中,如果你根据数组/向量中的多态元素的具体类型对其进行排序/分组,那么当你迭代数组并在对象上调用虚函数时,预测更有可能成功。
所以测量它,但尝试使用相同的数字为两个测试(随机或不)。