Skip the C++ construct walk with a class flag - #413
Conversation
| { | ||
| if (NULL == class->cxx_destruct) | ||
| { | ||
| objc_set_class_flag(class, objc_class_flag_no_cxx_destruct); |
There was a problem hiding this comment.
I'm not quite following the logic here, it looks as if this is never set. I'd expect each class being resolved to initialise it to the superclass's value and then clear it if the superclass had it set but we found a matching method.
| } | ||
|
|
||
| if (cls->super_class) | ||
| if (cls->super_class && |
There was a problem hiding this comment.
Why not do the test in cls here and then recurse at the end?
|
This would be good to have some tests for. These methods are generated by the compiler if an ivar is a C++ class with a constructor or destructor. Note: I'm not 100% sure that the destruct optimisation will be a net win in modern Objective-C, because the compiler emits the |
Agreed - #413 is a work in progress, I worked on this during my flight yesterday, so have some more instrumented tests I need to do - once I am comfortable I'll send you a better report and we can decide. |
call_cxx_construct_for_class recurses up the superclass chain on every allocation. A class whose superclasses have no .cxx_construct between them walks the whole chain to do nothing, and the check is a no-op for nearly every class in a Foundation workload. A class flag means this class and all of its superclasses have no .cxx_construct, so the walk ends at the first class that has one. It is set where the dispatch table is built, by which point the superclass chain is resolved, and cleared where a method is installed. The selector is registered in the init phase rather than tested for null on every recursive call. Measured on whole workloads rather than a loop around the call. Against master, minimum of 5 interleaved rounds: Foundation string, array and dictionary churn under ARC 2.3% faster, and allocation of classes compiled with ARC 19.7% faster, the latter because ARC emits no .cxx_construct at all so the walk ends immediately. A genomic coder that is AVX2 bound with few allocations is unchanged. The matching flag for .cxx_destruct is not here. It measured between 0.3 and 0.6 points on the same workloads, which is at or below the run to run spread of the machine, and it costs a conditional branch in the destruction path. Suite passes, 198 of 198.
1ee020d to
ca1603f
Compare
call_cxx_construct_for_class recurses up the superclass chain on every allocation. A class whose superclasses have no .cxx_construct between them walks the chain to do nothing, which is nearly every class in a Foundation workload.
A class flag records that neither a class nor any superclass has one. It is set where the dispatch table is created, after the superclass chain is initialised, and cleared where installMethodInDtable assigns cxx_construct, so a method installed later invalidates it. init_cxx_selectors registers the selector once, in place of a null check on every recursive call.
Minimum of 5 interleaved rounds against master, on whole workloads: Foundation churn under ARC 2.3% faster, allocation of ARC compiled classes about 20% faster, since ARC emits no .cxx_construct and the walk ends immediately. A genomic coder that is AVX2 bound is unchanged.
The matching flag for .cxx_destruct was measured and dropped. It was worth 0.3 to 0.6 points, at or below the spread of the machine, against a branch in every destruction.
ctest passes 198 of 198. Draft because the flag is read on the allocation path without synchronisation, as the other class flags are.