Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiently moving a class with PODs

Tags:

c++

c++11

How do I efficiently move a class with a large set of POD members? Example:

struct{
    int a1;
    int a2;
    int a3;
    ...
    ...
    ...
};

By 'move' I mean that the behavior is similar to the move semantic (std::move).

like image 520
ggg Avatar asked Jul 21 '12 02:07

ggg


People also ask

Why is move more efficient than copy?

It's faster because moving allows the source to be left in a invalid state, so you can steal it's resources. For example, if a object holds a pointer to a large block of allocated memory, a move can simply steal the pointer while a copy must allocate its own memory and copy the whole memory block.

Is std :: move faster than copy constructor?

Std::move is not faster than straight up copying.

Should I always use std move?

Q: When should it be used? A: You should use std::move if you want to call functions that support move semantics with an argument which is not an rvalue (temporary expression).

Is move constructor faster than copy constructor?

The move constructor is much faster than a copy constructor because it doesn't allocate memory nor does it copy memory buffers.


3 Answers

PODs don't move, they just copy. Because there's no indirection. So just use an ordinary assignment, and ask the compiler to optimize for whatever efficiency you have in mind. Remember to measure, systematically, before (what on Earth you are doing differently) and after. Also consider whether the wasted programmer time, which is money, is worth the micro-optimization.

like image 65
Cheers and hth. - Alf Avatar answered Oct 06 '22 07:10

Cheers and hth. - Alf


This question betrays a lack of understanding of what movement in C++11 is for.

When you copy an object that has pointers or otherwise owns resources, there are two ways to make that copy happen. You can either copy the pointers/resource references, or you can allocate new objects/resources and copy the value of the original ones into the new ones.

In the first case, you are left with two objects that have references to the same object. Qt does this a lot. If you use one object to modify something it references, you are also modifying the other one. You generally need some kind of reference counter in order to not double-delete the pointer or double-release the resource.

In the second case, you are left with two completely separate objects. This is commonly called "value semantics", because if you copy one POD into another, you have two completely separate objects afterwards. Changing one doesn't change another.

Move semantics are a C++11 mechanism to allow objects that normally have value semantics to have reference semantics under certain conditions. It essentially allows an object to steal the pointers/resources referenced by another object instance.

For example, take std::vector; this is just a wrapper around a dynamically allocated and resized array. As with most C++ standard library objects, vector implements value semantics. If you copy a vector, the new vector must allocate a new array and copy each element from the old one into the new one. Once you're done, you have two completely separate arrays.

Move semantics are a way to transfer the array contained within a vector into another. After the operation, the move source object is "empty" (technically in an undefined state, but it is effectively separated from the data it allocated). The new vector now has the exact same pointer that the old vector did; the new vector will delete memory that it didn't allocate.

As you can see, this is all based on the concept of an object owning resources. vector allocates and owns an array; it's destructor will destroy it. That's how you define ownership. Movement is about transferring ownership.

PODs cannot express ownership of pointers or resources, because PODs must have trivial destructors (ie: destructors who don't do anything). Because PODs can't express ownership, there's nothing to move. You can't transfer ownership between objects if they don't own anything.

like image 37
Nicol Bolas Avatar answered Oct 06 '22 07:10

Nicol Bolas


You have to bring engineering judgement in to play. Imagine for an example that you are talking about whether to use a std::array<int, N> or a std::vector<int> with N items, where N is a compile-time constant.

When N is very small, std::array<int, N> is the clear winner because there is no heap allocation. And copies (and moves which are equivalent to copies) are cheap.

When N is very large, std::vector<int> is the clear winner because although there is a heap allocation, and one can move around the vector with the cost of only moving 3 words, no matter the size of N.

When N == 3, there is no doubt which is the better choice. When N == 3,000,000 there is no doubt which is the better choice.

Your job, as the designer, is to step into that grey area between these two extremes and make the right decision. There is no always-right decision. Performance measurements against your expected use cases go a long way. Use <chrono> for those performance measurements. If you don't know what <chrono> is, search stackoverflow.

like image 25
Howard Hinnant Avatar answered Oct 06 '22 07:10

Howard Hinnant