Isa Dzhumabaev
Isa Dzhumabaev

Reputation: 49

Hand written move

I have written a vector class to learn move semantics. I use move constructor to move T (commented line).

My question is why not to just copy all the bytes of temp object and set all bytes of temp object to zero like in C?

I know that the destructor will be called for temp object and it may require some initialized members to properly destruct. This can be reason why I must not change internal representation of the object.

But in case I'm 100% sure my ~T() don't have such requirements, is this a good optimization?

 20     void push_back(T&& val)
 21     {
 22         check_cap();
 23         //new (m_data + m_size) T(std::move(val));
 24         for(int i = 0; i < sizeof(T); ++i)
 25         {
 26             reinterpret_cast<char*> (m_data + m_size)[i] = reinterpret_cast<char*> (&val)[i];
 27             reinterpret_cast<char*> (&val)[i] = 0;
 28         }
 29         m_size++;
 30     }

(please don't tell anything about casts and their safety if it's not related to the actual question)

(I know it's not a good approach and better not to use it in real project. But I'm only interested how good is it from the point of efficiency.)

Upvotes: 3

Views: 153

Answers (3)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275585

Your plan is a bad one.

Compilers can, using the as-if rule, often work out that a memcpy and a zero - or even just skipping the destructor - is legal.

If such a compiler runs into your hand crafted undefined behaviour, it either gets confused by the undefined behaviour or becomes unable to further optimize because of it.


There are sometimes good reasons to resort to undefined behaviour. But they start with proving your solution makes things better, and exhausting the standard compliant solutions. And they end with a frank discussion of the real short, medium and long term risks, in exchange for only a short term guaranteed benefit.

Your case does none of this.

Optimizing move-destroy to memcpy is something compilers already do with types that are easy to understand in simple code flow. Doing it manually is 99/100 pointless and 90/100 times harmful.

If your types are not simple and the code flow easy to understand, then probably your optimization is also difficult to prove safe. And if you simplify your types and code flow, by the time you can reliably prove your memcpy zero is optimal, your compiler probably can as well.

Sit down with godbolt and play with the optimized compiler output. It is educational.

Upvotes: 4

Ted Lyngmo
Ted Lyngmo

Reputation: 117328

why not to just copy all the bytes of temp object and set all bytes of temp object to zero like in C?

is this a good optimization?

I think not. Here's a quick-bench comparison between your hand written version and the normal version using clang++:

enter image description here

When using g++ the result is a tie, so no gain there either.

Upvotes: 6

JVApen
JVApen

Reputation: 11317

Your optimization can be valid for a few cases. It can cause issues when your move constructor does something specific.

 class MyCustomClass : public IObserver
 {
     Registry &registry;
     // ...
 public:
     MyCustomClass(MyCustomClass &&rhs)
     : registry{rhs.registry}
     {
          registry.register(*this);
     }
     // ...
 };

If you do a bitwise copy of this class the moved instance ain't registered with the registry.

By zeroing out, the reference gets corrupted and the destructor of the original instance will most likely crash on deregistering from the registry.

Upvotes: 2

Related Questions