【发布时间】:2010-05-17 06:03:11
【问题描述】:
我正在尝试对函数指针调用和虚函数调用之间的差异进行基准测试。为此,我编写了两段代码,它们对数组执行相同的数学计算。一种变体使用一组指向函数的指针并在循环中调用它们。另一种变体使用指向基类的指针数组并调用其虚函数,该虚函数在派生类中被重载以执行与第一个变体中的函数完全相同的事情。然后我打印经过的时间,并使用一个简单的 shell 脚本多次运行基准测试并计算平均运行时间。
代码如下:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
using namespace std;
long long timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
void function_not( double *d ) {
*d = sin(*d);
}
void function_and( double *d ) {
*d = cos(*d);
}
void function_or( double *d ) {
*d = tan(*d);
}
void function_xor( double *d ) {
*d = sqrt(*d);
}
void ( * const function_table[4] )( double* ) = { &function_not, &function_and, &function_or, &function_xor };
int main(void)
{
srand(time(0));
void ( * index_array[100000] )( double * );
double array[100000];
for ( long int i = 0; i < 100000; ++i ) {
index_array[i] = function_table[ rand() % 4 ];
array[i] = ( double )( rand() / 1000 );
}
struct timespec start, end;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
for ( long int i = 0; i < 100000; ++i ) {
index_array[i]( &array[i] );
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
unsigned long long time_elapsed = timespecDiff(&end, &start);
cout << time_elapsed / 1000000000.0 << endl;
}
这里是虚函数变体:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
using namespace std;
long long timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
class A {
public:
virtual void calculate( double *i ) = 0;
};
class A1 : public A {
public:
void calculate( double *i ) {
*i = sin(*i);
}
};
class A2 : public A {
public:
void calculate( double *i ) {
*i = cos(*i);
}
};
class A3 : public A {
public:
void calculate( double *i ) {
*i = tan(*i);
}
};
class A4 : public A {
public:
void calculate( double *i ) {
*i = sqrt(*i);
}
};
int main(void)
{
srand(time(0));
A *base[100000];
double array[100000];
for ( long int i = 0; i < 100000; ++i ) {
array[i] = ( double )( rand() / 1000 );
switch ( rand() % 4 ) {
case 0:
base[i] = new A1();
break;
case 1:
base[i] = new A2();
break;
case 2:
base[i] = new A3();
break;
case 3:
base[i] = new A4();
break;
}
}
struct timespec start, end;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
for ( int i = 0; i < 100000; ++i ) {
base[i]->calculate( &array[i] );
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
unsigned long long time_elapsed = timespecDiff(&end, &start);
cout << time_elapsed / 1000000000.0 << endl;
}
我的系统是 LInux,Fedora 13,gcc 4.4.2。代码是用 g++ -O3 编译的。第一个是test1,第二个是test2。
现在我在控制台中看到了这个:
[Ignat@localhost circuit_testing]$ ./test2 && ./test2
0.0153142
0.0153166
嗯,或多或少,我想。然后,这个:
[Ignat@localhost circuit_testing]$ ./test2 && ./test2
0.01531
0.0152476
应该可见的 25% 在哪里?第一个可执行文件怎么会比第二个慢?
我问这个是因为我正在做一个项目,该项目涉及像这样连续调用许多小函数以计算数组的值,并且我继承的代码进行了非常复杂的操作以避免虚函数调用开销。现在这个著名的调用开销在哪里?
【问题讨论】:
-
你确实运行了
./test1 && ./test2,对吧?因为在您的帖子中,您已经运行了两次./test2。您还应该多次运行这些程序中的每一个并对结果进行平均;否则结果毫无意义。也就是说,您所说的“25% 在哪里”是什么意思?您是否预计虚函数调用会产生 25% 的开销? -
我不是这方面的专家,但 C++ 编译器被称为非常聪明的优化器。如果您的两个代码在优化后都编译成相同的东西,我不会感到惊讶。因此,如果您还没有这样做,请在编译基准之前禁用所有优化。
-
是的,对不起,它是 test2 && test2,但这是来自 test1 && test2 的一个: [Ignat@localhost circuit_testing]$ ./test1 && ./test2 0.0159555 0.0153605 还是一样的。当它不慢时,它就像我预期的那样快 0.0005 秒 - 而不是 25 &,或者至少 10%。
-
哇!同一个问题中的过早优化和微基准测试。添加一个 asm 块只是为了确保它真的快,你将完成代码邪恶三部曲...... muhahaha!
-
嗯,我正在做的是试图找出使用哪一个:带开关的常规函数调用、带表的指针函数调用或虚函数调用。而已。我需要对一个大小为 10000 的数组进行数千次迭代,因此,更快的速度很重要。
标签: c++ optimization virtual