CFD Online Discussion Forums

CFD Online Discussion Forums (https://www.cfd-online.com/Forums/)
-   Main CFD Forum (https://www.cfd-online.com/Forums/main/)
-   -   Can we optimize C++ operator overloads ? (https://www.cfd-online.com/Forums/main/234167-can-we-optimize-c-operator-overloads.html)

aerosayan February 25, 2021 00:00

Can we optimize C++ operator overloads ?
 
Dear C++ experts, can we optimize the C++ operator overloads by preventing creation of temporary variables?



Generally C++ operator overloads are frowned upon due to performance degradation caused by dynamic memory allocation used while creating and deleting temporary objects in operations such as veca + vecb - vecx * vecy.


Code:

VECTOR operator+(VECTOR& a, VECTOR& b)
{
    VECTOR c; // sloooooooowwwwww

    c[0] = a[0]+b[0];
    c[1] = a[1]+b[1];

    return c;
}

This is of huge concern when the elements take up a large amount of memory (say a 1000*1000) matrix.


Now, what if we were able to use pre-allocated objects instead of creating new ones?



Code:

VECTOR operator+(VECTOR& a, VECTOR& b)
{
    auto& c = global_array_of_preallocated_objects[iobject]; // fast?

    c[0] = a[0]+b[0];
    c[1] = a[1]+b[1];

    iobject++;  // iobject is in global scope  and available everywhere

    return c;
}

Would that be a good optimization, or am I shooting myself in the foot here?

praveen February 25, 2021 01:32

You can define an add function

Code:

class VECTOR
{
void add(VECTOR& a, VECTOR& b);
};

VECTOR a, b, c;
a.add(b, c); // a = b + c

but not a good solution if you have to add several vectors in one line.

I think Chapel can do in place updates. See slide 13 here

https://chapel-lang.org/CHIUW/2020/C...-Chapel101.pdf

which shows a similar operation as in your question.

I wrote some simple example codes in Chapel here

https://github.com/cpraveen/cfdlab/tree/master/chapel

aerosayan February 25, 2021 03:27

Quote:

Originally Posted by praveen (Post 797203)
You can define an add function

Code:

class VECTOR
{
void add(VECTOR& a, VECTOR& b);
};

VECTOR a, b, c;
a.add(b, c); // a = b + c

but not a good solution if you have to add several vectors in one line.

I think Chapel can do in place updates. See slide 13 here

https://chapel-lang.org/CHIUW/2020/C...-Chapel101.pdf

which shows a similar operation as in your question.

I wrote some simple example codes in Chapel here

https://github.com/cpraveen/cfdlab/tree/master/chapel


I'm planning to use a free mathematical functions like you showed for some operations, if the C++ operator overload can't be optimized, and I don't have enough RAM to store the intermediary vectors or matrices in the method I mentioned.


some operation like `res = a+b+c+d+e+f+g+h` could be simplified into add(res, a,b,c,d,e,f,g,h), without needing to write loops, but I have to see what new features of C++ I can use to do it and make the function take arbitrary number of arguments. Probably through variadic templates, it will be possible.



Of course, the final aim would be to be able to write very complex operations like `res = a.dot.b + b.cross.c + div(delta)` etc in a compact form, but I don't know if it's possible.:confused:

aerosayan February 25, 2021 07:04

Quote:

Originally Posted by praveen (Post 797203)
You can define an add function

Code:

class VECTOR
{
void add(VECTOR& a, VECTOR& b);
};

VECTOR a, b, c;
a.add(b, c); // a = b + c

but not a good solution if you have to add several vectors in one line.

I think Chapel can do in place updates. See slide 13 here

https://chapel-lang.org/CHIUW/2020/C...-Chapel101.pdf

which shows a similar operation as in your question.

I wrote some simple example codes in Chapel here

https://github.com/cpraveen/cfdlab/tree/master/chapel


I have a reaaaaaalllyyy bad idea on how we can get "similar" compact mathematical form of Fortran, in C++. :rolleyes:


Code:



#define xa  [IVAR] +
#define xs  [IVAR] -
#define xm  [IVAR] *
#define xd  [IVAR] /
#define xe [IVAR]



#define IVAR i

for(int i=0; i<n; ++i)
{

    c = a xa b xa c xs d xm e xe;
    e = a xs b xe;
    c = c xm e xe;

}

#undef IVAR

This might seem stupid (and might actually be), but I think it can be useful.

praveen February 25, 2021 07:07

Have you looked at "Expression Templates", see

https://en.wikipedia.org/wiki/Expression_templates

It describes almost same situation which you are asking.

aerosayan February 25, 2021 11:26

Quote:

Originally Posted by praveen (Post 797229)
Have you looked at "Expression Templates"


I created an Expression Template library last year. Not worth the effort. Highly overrated in my humble opinion, as it becomes very difficult to find optimization possibilities in the code.


I came up with a hack to make it a little bit easier to work with such long expressions. I just want to write simple code for N dimensional arrays, like in Fortran 95, but in C++.


Looks like the operator overloading may not be the best solution.


This hack works for now, I guess.



Code:

#include <iostream>
#include <vector>
#include <array>

using namespace std;

// Short form of a for loop
//
#define DO(IX, START, END) for(int (IX)=(START); (IX)<(END); ++(IX))
#define DO2(IX, START1, END1, IY, START2, END2) DO((IX), (START1), (END1)) { DO((IY), (START2), (END2)) {

// Short form to close multiple braces
//
#define END }
#define END2 }}

// Access array with index
//
#define ix [i]
#define jx [j]
#define kx [k]

// Access 2D arrays with index
//
#define ix0 [i][0]
#define ix1 [i][1]
#define ix2 [i][2]

// Access 2D arrays with DO2 loop
#define ijx [i][j]

int main()
{
    //-----------------------------------------------------------------------//
    //                              EXAMPLE-1                                //
    //-----------------------------------------------------------------------//

    // No. of elements
    int n = 100;

    // 3 arrays
    int a[n], b[n], c[n];

    // Fill up values of a and b
    DO(i,0,n)
    {
        a ix = 1;
        b ix = 2;
    }

    // Do addition : c = a + b
    DO(i,0,n)
    {
        c ix = a ix + b ix;
    }

    // Print results
    DO(i,0,5)
    {
        cout << a ix << " + " << b ix << " = " << c ix << endl;
    }

    //-----------------------------------------------------------------------//
    //                              EXAMPLE-2                                //
    //-----------------------------------------------------------------------//

    // Vector of arrays
    std::vector<std::array<double, 2>> va(n), vb(n), vc(n);

    // Fill up values of a and b
    DO(i,0,n)
    {
        va ix0 = 1.0;
        va ix1 = 2.0;

        vb ix0 = 1.0;
        vb ix1 = 2.0;
    }

    // Add the two vectors elementwise : vc = va + vb
    // NOTE : We are writing only one equation, and both elements are used
    //
    DO2(i,0,n, j,0,2)
        vc ijx = va ijx + vb ijx;
    END2

    // Print results
    DO(i,0,5)
    {
        cout << "<"
        << va ix0 << ", " << va ix1 << "> + <"
        << vb ix0 << ", " << vb ix1 << "> = <"
        << vc ix0 << ", " << vc ix1 << ">"<< endl;
    }
    return 0;
}

Output :
Code:

1 + 2 = 3
1 + 2 = 3
1 + 2 = 3
1 + 2 = 3
1 + 2 = 3
<1, 2> + <1, 2> = <2, 4>
<1, 2> + <1, 2> = <2, 4>
<1, 2> + <1, 2> = <2, 4>
<1, 2> + <1, 2> = <2, 4>
<1, 2> + <1, 2> = <2, 4>


aerosayan February 25, 2021 14:25

My macro based "hack" might actually be a very good solution.
It feels good to write, it feels fast to write, it feels easy to read, and it doesn't break anything as far as I know.



Using it in my codebase from now on.


All times are GMT -4. The time now is 11:30.