I implemented some algorithm where the main data structure is a tree. I use a class to represent a node and a class to represent a tree. Because the nodes get updated a lot, I call many setters and getters.
Because I have heard many times that function calls are expensive, I was thinking that maybe if I represented the nodes and the tree using structs, it would make my algorithm more efficient in practice.
Before doing so I decided to run a small experiment to see if this is actually the case.
I created a class that had one private variable, a setter and a getter. Also I created a struct that had one variable as well, without setters/getters since we can just update the variable by calling struct.varName
. Here are the results:
The number of runs is just how many times we call the setter/getter. Here is the code of the experiment:
#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;
}
I don't understand why I get this behaviour. I thought that function calls were more expensive?
EDIT: I rerun the same experiment without -O3
EDIT: OK this is very surprising, by declaring the class in a separate file called foo.h
, implementing the getters/setters in foo.cpp
and running with -O3, it seems that the class becomes even more inefficient.
I have heard many times that function calls are expensive.
Was this in 1970 by any chance?
Compilers are smart. Very smart. They produce the best program they can given your source code, and unless you're doing something very weird, these sorts of design changes are unlikely to make much (if any) performance difference.
Most notably here, a simple getter/setter can even be completely inlined in most cases (unless you're doing something weird), making your two programs effectively the same once compiled! You can see this result on your graph.
Meanwhile, the specific change of replacing class
with struct
has no effect on performance whatsoever - both keywords define a class.
I don't understand why I get this behaviour. I thought that function calls were more expensive?
See, this is why we don't prematurely optimise. Write clear, easy-to-read code without tricks and let your compiler take care of the rest. That's its job, and it's generally very good at it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With