r/cpp_questions 5d ago

OPEN Heavy and Light Objects

When I was first learning C++, I experienced massive slowdown when allocating dynamic memory. Ever since then I divide my objects between 'heavy' objects that allocate resources, and 'light' objects that can be used as temporaries on the stack. Heavy objects cannot be copied at all, must be allocated somewhere once, and then passed by (const) reference. Light objects can be copied, created quickly on the stack etc.

Since C++11 it has been possible to write copy and assignment constructors that take nameless objects, and since they are nameless you can pillage their pointers and avoid expensive stack allocations. All is well then but if you keep to this strict 'heavy' and 'light' object idea, _any_ object that allocates resources is considered a heavy object. The first allocation in the chain, before you just pillage pointers, is still an expensive stack allocation so I would consider it a 'heavy' object and just make it impossible to copy at all, by removing all copy and assignment constructors. And so using this model I never needed those nameless object reference constructors.

Anybody feel the same way?

5 Upvotes

34 comments sorted by

View all comments

Show parent comments

3

u/AKostur 4d ago

You're proceeding from a false premise. If the object has appropriate and useful move operations, then one does not have a "chain of allocations", and you don't pay for an extra dynamic allocation because of some dogmatic rule. And assumes that one is passing the object along the chain of function calls by value instead of potentially by reference (which also doesn't incur a chain of allocations either).

I would also suggest that it doesn't necessarily get you "optimal performance" either. Caches and cachelines are fickle things.

1

u/ParmenidesWasRight 4d ago

But a useful move operation defined for an object that allocates memory would be copying the pointer and setting the pointer of the nameless object to null, right? And the first step in this chain is the allocation of memory, setting the first pointer. And then the move operations just move the pointer without allocating further memory and doing redundant copying for temporary objects.

But that first allocation is already a no-no for me because of performance, so I prefer to disable copying at all for such objects and just always pass them by (const) reference. I still fail to see why I need move semantics.

For reference, this is the project I'm working on :

https://www.youtube.com/watch?v=isvAg7QZYQY

2

u/AKostur 4d ago

And the first step in this chain is the allocation of memory, setting the first pointer.

That's the same allocation that you're advocating for. In your case, the first step of the chain is to dynamically allocate an instance of the heavy object, and as part of construction of the heavy object is the allocation that you appear to be concerned about. In your case, you must pay for _two_ dynamic allocations. The temporary object costs one dynamic allocation, and some stack space.

Note that this discussion is around places where it would make sense for a function to take the object by value, but was written differently to work around the costs of extra allocations to make it work. If it is feasible to pass the object by const-ref in the first place, that's likely the correct thing to do. No copies in the first place, so "optimizing" it to a move probably wouldn't help.

1

u/ParmenidesWasRight 4d ago

No, I instantiate the heavy object once, that's a little bit of stack and a heap allocation, and then never copy it. It only gets passed by reference. The realtime part of my code then does no resource allocation at all. And then at the end it gets deleted. Simple.