Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reduce the + operator memory consumption for self-defined class in C++?

Tags:

c++

c++11

I give the following examples to illustrate my question:

 class BigClass
{
public:
    static int destruct_num;
    friend BigClass operator + (const BigClass &obj1, const BigClass &obj2);
    std::vector<int> abc;

    BigClass()
    {

    }

    ~BigClass()
    {
        destruct_num++;
        std::cout << "BigClass destructor " <<destruct_num<< std::endl;

    }

    BigClass(BigClass&& bc) :abc(std::move(bc.abc))
    {
        std::cout << "move operation is involed" << std::endl;
    }
};

int BigClass::destruct_num = 0;

BigClass operator + (const BigClass &obj1, const BigClass &obj2)
{
    BigClass temp;
    temp.abc = obj1.abc;
    temp.abc.insert(temp.abc.end(), obj2.abc.begin(), obj2.abc.end());

    return temp;

}

int main(void)
{

    BigClass a;
    a.abc = { 1,2,3 };
    BigClass b;
    b.abc = { 5,6,7 };
    BigClass c = a + b;

//  for (auto& v : c.abc)
//      std::cout << v << "  ";

    return 0;


}   

One problem with regard to operator + is that we have to generate a temp BigClass object temporally and then return it. Are there someways to reduce this burden?

like image 214
feelfree Avatar asked Jul 12 '17 08:07

feelfree


2 Answers

Generally:

[...] the compilers are permitted, but not required to omit the copy [...]

Anyway your function should be optimised by any modern compiler because copy elision.

Here an example:

The result of the operator+ is in the assembly section:

call    operator+(BigClass const&, BigClass const&)
addl    $12, %esp

As you can see, no copy constructor is invoked in order to copy the result.

Indeed, if we disable the copy elision optimisation in GCC, the result changes:

call    operator+(BigClass const&, BigClass const&)
addl    $12, %esp
subl    $8, %esp
leal    -20(%ebp), %eax
pushl   %eax
leal    -56(%ebp), %eax
pushl   %eax
call    BigClass::BigClass(BigClass&&)
addl    $16, %esp
subl    $12, %esp
leal    -20(%ebp), %eax
pushl   %eax
call    BigClass::~BigClass()
addl    $16, %esp

After the call of operator+ the copy (or move in this case) constructor is called, and after the destructor of the temporary object.

Note that the copy elision is obtained even disabling optimisations (-O0).

The same result is obtained with an older version: GCC 4.4.7.


Since copy elision is not guaranteed for all architectures, you might implement some different solutions.

One possible solution is to avoid the allocation of a temporary variable inside the function, demanding the caller the reservation of that space. In order to do that, you should use a "custom" method and avoid to overload the operator+.

void sum_bigClasses(const BigClass& obj1, const BigClass& obj2, BigClass& output) {
   // output.resize(obj1.size() + obj2.size());
   // std::copy(...);
}

Another solution it could be to implement a non-const operator for sum. An example:

BigClass& operator+=(const BigClass& rhs) {
   // std::copy(rhs.cbegin(), rsh.cend(), std::back_inserter(abc));
   return *this;
}

In this way the class interface allows different strategies:

  • Avoid to allocate 3 different object but only 2, if you don't need to preserve all different states.
  • Allow to allocate 3 different object and avoid temporary construction inside the operator.

Here, the two examples.

The first point:

BigClass o1;
BigClass o2;
// Fill o1 and o2;
o1 += o2;
// only 2 object are alive

The second point:

BigClass o1;
BigClass o2;
// Fill o1 and o2;
BigClass o3 = o1;  // Costructor from o1
o3 += o2;
// three different object

EDIT: Since the function it's a NRVO (the returned expression is not a prvalue) neither the new standard C++17 will guarantee the copy elision.

like image 76
BiagioF Avatar answered Nov 19 '22 10:11

BiagioF


If you run your code than you see that only 3 destructors are called. That means that the value of tmp object is moved, not copied, because of RVO (Return Value Optimization). The compiler doesn't copy it, because it sees that's not necessary.

like image 26
banana36 Avatar answered Nov 19 '22 08:11

banana36