Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is accessing a private variable of a class as efficient as accessing a variable of a struct?

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:

enter image description here

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

enter image description here

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.

enter image description here

like image 615
jsguy Avatar asked Apr 28 '16 12:04

jsguy


1 Answers

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.

like image 158
Lightness Races in Orbit Avatar answered Sep 21 '22 08:09

Lightness Races in Orbit