因为我听到很多次函数调用很昂贵,我在想,也许如果我使用结构体代表节点和树,这将使我的算法在实践中更有效率.
在这样做之前,我决定进行一个小实验,看看是否真的是这样.
我创建了一个有一个私有变量,一个setter和一个getter的类.此外,我创建了一个也有一个变量的结构,没有setter / getter,因为我们可以通过调用struct.varName更新变量.结果如下:
运行次数就是我们称之为setter / getter的次数.以下是实验的代码:
#include <iostream> #include <fstream> #define BILLION 1000000000LL using namespace std; class foo{ private: int a; public: void set(int newA){ a = newA; } int get(){ return a; } }; struct bar{ int a; }; timespec startT,endT; void startTimer(){ clock_gettime(CLOCK_PROCESS_cpuTIME_ID,&startT); } double endTimer(){ clock_gettime(CLOCK_PROCESS_cpuTIME_ID,&endT); return endT.tv_sec * BILLION + endT.tv_nsec - (startT.tv_sec * BILLION + startT.tv_nsec); } int main() { int runs = 10000000; int startRun = 10000; int step = 10000; int iterations = 10; int res = 0; foo f; ofstream fout; fout.open("stats.txt",ios_base::out); fout<<"alg\truns\ttime"<<endl; cout<<"First experiment progress: "<<endl; int cnt = 0; for(int run = startRun; run <= runs; run += step){ double curTime = 0.0; for(int iter = 0; iter < iterations; iter++) { startTimer(); for (int i = 1; i <= run; i++) { f.set(i); res += f.get(); } curTime += endTimer()/iterations; cnt++; if(cnt%10 == 0) cout<<cnt/(((double)runs-startRun+1)/step*iterations)*100<<"%\r"; } fout<<"class\t"<<run<<"\t"<<curTime/BILLION<<endl; } int res2 = 0; bar b; cout<<"Second experiment progress: "<<endl; cnt = 0; for(int run = startRun; run <= runs; run += step){ double curTime = 0.0; for(int iter = 0; iter < iterations; iter++) { startTimer(); for (int i = 1; i <= run; i++) { b.a = i; res2 += b.a; } curTime += endTimer()/iterations; cnt++; if(cnt%10 == 0) cout<<cnt/(((double)runs-startRun+1)/step*iterations)*100<<"%\r"; } fout<<"struct\t"<<run<<"\t"<<curTime/BILLION<<endl; } fout.close(); cout<<res<<endl; cout<<res2<<endl; return 0; }
编辑:我没有-O3重新运行相同的实验
编辑:这是非常令人惊讶的,通过在一个单独的文件foo.h中声明类,在foo.cpp中实现getters / setters并运行-O3,似乎该类变得更加低效.
解决方法
I have heard many times that function calls are expensive.
这是1970年的任何机会吗?
编译器很聪明很聪明.他们会产生最佳的程序,他们可以提供您的源代码,除非你做的事情非常奇怪,否则这些设计变更不太可能产生很大的影响(如果有的话).
最值得注意的是,在大多数情况下,一个简单的getter / setter甚至可以被完全嵌入(除非你在做一些奇怪的事情),使你的两个程序有效地一次编译!您可以在图表上看到此结果.
同时,使用struct替换类的具体变化对性能没有影响 – 两个关键字都定义了一个类.
I don’t understand why I get this behavIoUr. I thought that function calls were more expensive?
看,这就是为什么我们不会过早地优化.编写清晰,易于阅读的代码,无需任何技巧,让编译器保持休息.这是它的工作,它通常是非常好的.