4 ms·
> So exceptions […] completely free when not thrown unlike return values. In C++, they are not. Since C++ allows objects to be created on the stack within the
by inkyoto 4y ago
> So exceptions […] completely free when not thrown unlike return values.
In C++, they are not. Since C++ allows objects to be created on the stack within the each current scope, every constructor call for such a new object has to register its destructor with the exception handler's object cleanup jump tables.
Consider an edge case with a loop where 100k local objects are created. Each constructor invocation will incur an overhead of an indexed memory store of the destructor's address in the exception handler's table[0] for each object.
An indexed memory store is typically 1x instruction for a CISC architecture (it can be more if the data section is located too «far» in the address space, and the ISA limits the offset width in the instruction encoding). It is typically several load low and shift, load low and store instructions on a RISC architecture. L1 D-cache and L2 cache, sometimes L3 cache as well (if the exception table grows large), and TLB reloads[1] get involved at all times. All of the aforementioned is just to register a destructor for an exception that might not occur. So, no, it is not free and (occasionally) the time dimension is not even clearly defined.
This is rather unique and specific to C++ because it is an outlier and allows new objects to be created on the heap AND also on the local stack. Languages that allow new objects to be only created on the heap do not incur such an overhead.
[0] Bonus point: 100k local objects being created inside a try/catch block will also blow the jump table out of proportions and add more cache pressure and cache line reloads.
[1] And even page faults might occur – if the exception table crosses memory pages.
- zugi 4y agoCalling destructors on scope exit is a C++ language feature with or without exceptions. I'm no expert but I believe these addresses can be determined relative to the stack frame pointer, so they don't need to be registered in advance. Instead extra code is created to call all those destructors; this code is only ever called if an exception is thrown. That results larger executables, but no extra CPU instructions are executed unless an exception is actually thrown. (In theory a compiler-writer could try to reuse destructor-calling code between the normal exit case and the exception case, but that might force one extra branch in the non-exception case.)