Moving big objects

Normally when I work with big objects, I like to use pointers just to ensure that I'm not copying data by accident (when I don't specify a copy constructor).

However, I also like to stay away from pointers to avoid memory corruption.

Today I'm using a library which returns objects directly. I'm wondering if it's safe for me to do the same without affecting performance:
1
2
3
4
5
6
BigClass someFunc()
{
  BigClass bc();
  bc.Load("GiantFile.csv");
  return bc; //<-- Does this get optimized to avoid performance issues?
}
Last edited on
Prior to C++11 some compilers were able to optimize it. After C++11, the language makes it optimized due to move constructors.
You could return the values by reference if you dislike the pointer syntax.
Just try to avoid pitfalls such as those found here:
http://stackoverflow.com/questions/752658/is-the-practice-of-returning-a-c-reference-variable-evil

Also are you really sure that you are moving a giant object? Even if the giant file is really giant, are you really loading all that into the encapsulating object? Or does that object just hold a reference to it?

Try these examples if you are still interested:
http://www.tutorialspoint.com/cplusplus/returning_values_by_reference.htm
http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/
http://cplus.about.com/od/learning1/ss/references.htm

The about.com has three nice rules about references:
1. A reference must always refer to something. NULLs are not allowed.
2. A reference must be initialized when it is created. An unassigned reference can not exist.
3. Once initialized, it cannot be changed to another variable.
Kevinkjt2000, none of that is really relevant in C++11 anymore ;)
+1
See: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


After reading that article, I will never pass parameters any other way.
That was a really good article. But how can we be sure that our compiler implements Copy Elision? On occasion kicking up the optimizer has caused runtime problems for me when using rediculous amounts of polymorphism. In those cases, I take off the optimizer completely. Would I still get Copy Elision?
Last edited on
> After C++11, the language makes it optimized due to move constructors.
> Kevinkjt2000, none of that is really relevant in C++11 anymore ;)
1
2
3
struct big{
   int array [42];
};
¿how could the move constructor and move assignment operator.be implemented?
For primitives a move is a copy, but if you think copying primitives is inefficient you could always copy primitive pointers instead...
> how could the move constructor and move assignment operator.be implemented?

Trivially.


> but if you think copying primitives is inefficient you could always copy primitive pointers instead...

If you measure, and determine that copying these trivially copyable objects creates a performance bottleneck. And then determine that copying pointers actually improves performance.

On coliru, it does a million trivial copies of big in 0.03 seconds.

1
2
3
4
5
6
7
8
9
10
#include <cstdlib>

struct big{
   int array [42];
};

big foo() 
{
    return big{ { std::rand() } } ;
}

http://coliru.stacked-crooked.com/a/bb27088855a3d8a4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <type_traits>
#include <cstdlib>

struct big{
   int array [42];
};

big foo() ;

int main()
{
    std::srand(123456) ;
    
    std::cout << std::boolalpha
              << std::is_trivial<big>::value << '\n' // true
              << std::is_trivially_copy_constructible<big>::value << '\n' // true
              << std:: is_trivially_move_constructible<big>::value << '\n' // true
              << std::is_trivially_copy_assignable<big>::value << '\n' // true
              << std:: is_trivially_move_assignable<big>::value << '\n' ; // true
    
    
    int x = 0 ;
    for( int i = 0 ; i < 1000*1000 ; ++i ) x += foo().array[0]%2 ;
    std::cout << x << '\n' ;
}

http://coliru.stacked-crooked.com/a/26a94222a0bf39bd

Dynamic memory allocation with copy of pointers is about 32 times slower (0.98 seconds)

http://coliru.stacked-crooked.com/a/3be5877aa14466da
http://coliru.stacked-crooked.com/a/f4f98566735aa72b

Take your pick.
> For primitives a move is a copy
I don't want to move the elements of the array, I want to move the array.


@JLBorges: I see RVO, no move assignment.

> Dynamic memory allocation with copy of pointers is about 32 times slower
stack vs heap.
Last edited on
> @JLBorges: I see RVO, no move assignment.

And how much slower do you expect a rep movsd (copy) would be in comparison to a rep stosd (RVO)?

Why make wild guesses, when we can easily measure it.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <cstdlib>

struct big
{
   int array [42];
};

big foo( const big& b )
{
    big rv = b ; 
    rv.array[0] = std::rand() ;
    return rv ;
}

http://coliru.stacked-crooked.com/a/b38e8cfad11cfdea

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <cstdlib>

struct big
{
   int array [42];
};

big foo( const big& ) ;

int main()
{
    std::srand(123456) ;
    int x = 0 ;
    big b ;
    
    for( int i = 0 ; i < 1000*1000 ; ++i ) x += foo(b).array[0]%2 ;
    std::cout << x << '\n' ; // statistically expected value: 500000
}

http://coliru.stacked-crooked.com/a/c542ace3277a8edc

Phew, a whopping 0.035 seconds with copy + NRVO, instead of a mere 0.030 seconds with RVO alone.

For the same code, with a larger object (4KB, 24 times larger) and rep stosq enabled (alignment), the million copies takes 0.266 seconds (8 times longer).

1
2
3
4
struct alignas(8) big
{
   int array [1000];
};

http://coliru.stacked-crooked.com/a/4bd441e603e85b14
Topic archived. No new replies allowed.