Avoiding virtual function calls in numerical C ++

I am writing numerical simulation code in C ++. In this simulation, there are some things that are “local” with a floating point value at each point of the two-dimensional grid, and others that are “global” with only one global floating point value.

Besides this difference, the two types of objects behave the same, so I would like to have an array containing both types of objects. However, since this is a numerical simulation, I need to do it in such a way that (a) avoids the overhead of invoking virtual functions as much as possible and (b) allows the compiler to maximize the use of optimization - and, in particular, allows the compiler to perform automatic SIMD vectorization, where it perhaps.

Currently, I find that I am writing code like this (which, I understand, will not actually work as intended):

class Base {};

class Local: public Base {
public:
    float data[size];
    // plus constructors etc.
};

class Global: public Base {
public:
    float data;
    // ...
};

void doStuff(Local a, Local b) {
    for (int i; i<size; ++i) {
        a.data[i] += b.data[i];
    }
}

void doStuff(Local a, Global b) {
    for (int i; i<size; ++i) {
        a.data[i] += b.data;
    }
}

void doStuff(Global a, Local b) {
    for (int i; i<size; ++i) {
        a.data += b.data[i];
    }
}

void doStuff(Global a, Global b) {
    a.data += b.data*size;
}

My code is a bit more complicated than this - the array is two-dimensional, and there are several doStufftypes that have three rather than two arguments, so I have to write eight specializations for each.

The reason this does not work is because argument types are doStuffnot known at compile time. I want to make an array Base *and call doStufffor its two members. Then I want the right specialization to be doStuffinvoked for the specific types of its arguments. (It doesn't matter if a doStuffvirtual method call is involved - I just want to avoid them in the inner loop.)

, , () operator[], , () SIMD doStuff(Local, Local) doStuff(Local, Global), doStuff(Global, Global). , , .

. , , , , doStuff(Base, Base), , . (, gcc , doStuff(Global, Global).)

, , , , , , .

class Base {
    virtual float &operator[](int) = 0;
};

class Local: public Base {
    float data[size];
public:
    float &operator[](int i) {
        return data[i];
    }
    // …
};

class Global: public Base {
    float data;
public:
    float &operator[](int i) {
        return data;
    }
    // ...
};

void doStuff(Base a, Base b) {
    for (int i; i<size; ++i) {
        a[i] += b[i];
    }
}

, . ( , , . , !)

CRTP, , , , , - doStuff.

+4
2

. ( , size):

template<typename A, typename B>
void doStuff(A & a, B & b) {
    for (int i; i<size; ++i) {
        a[i] += b[i];
    }
}

operator[], .


, , , -

void doStuff( Base & a, Base & b ) {
    Local * a_local = dynamic_cast<Local*>(&a);
    Global * a_global = dynamic_cast<Global*>(&a);
    //same for b
    if( a_local && b_local ) {
        doStuffImpl(*a, *b); {
    } else if( a_local && b_global ) {
        doStuffImpl(*a, *b):
    } ...
}

, if , doStuffImpl . , . dynamic_cast. Base, . , doStuff.

, . . , .

+2

Local , Global Global, Local?

, .

class Base {
    public:
       virtual void doStuff(Base& b) = 0;
};

class Local: public Base {
    public:
       virtual void doStuff(Base& b);
       float data[size];
       // plus constructors etc.
};

class Global: public Base {
    public:
       virtual void doStuff(Base& b);
       float data;
       // ...
};

void Local::doStuff(Base& b) {
    Local* lb = NULL;
    Global* gb = NULL;
    if ( NULL != (lb = dynamic_cast<Local*>(&b)) )
    {
       // Do Local+Local stuff.
    }
    else if ( NULL != (gb = dynamic_cast<Global*>(&b)))
    {
       // Do Local+Global stuff.
    }

}

void Global::doStuff(Base& b) {
    Local* lb = NULL;
    Global* gb = NULL;
    if ( NULL != (lb = dynamic_cast<Local*>(&b)) )
    {
       // Do Global+Local stuff.
    }
    else if ( NULL != (gb = dynamic_cast<Global*>(&b)))
    {
       // Do Global+Global stuff.
    }

}

void doStuff(Base a, Base b) {
    a.doStuff(b);
}
0

Source: https://habr.com/ru/post/1529718/


All Articles