gh-149180: avoid double checking tp_as_number, tp_as_sequence and tp_as_mapping#149476
gh-149180: avoid double checking tp_as_number, tp_as_sequence and tp_as_mapping#149476NekoAsakura wants to merge 1 commit into
tp_as_number, tp_as_sequence and tp_as_mapping#149476Conversation
…e` and `tp_as_mapping`
Documentation build overview
40 files changed ·
|
|
Can you remove the JIT as well and only do PGO/LTO please? |
|
MojoVampire
left a comment
There was a problem hiding this comment.
This needs a docs change for https://docs.python.org/3/c-api/typeobj.html, which currently claims the structs aren't set on PyBaseObject_Type or PyType_Type, and are not defaulted (it might make sense to add a new code for the default column to indicate that yes, it's defaulted, to a fully NULL set of values).
Also, just to be clear (comment is there as well), type_ready's docs say "Still, not all type objects go through PyType_Ready." If that's true, then we can't actually guarantee non-null tp_as_(number|sequence|mapping) slots, right? Unless the CheckConsistency checks are guaranteed to be hit at some point? Those CHECK checks go to ASSERT macros/functions that, despite the name, are never removed on NDEBUG builds, so they might protect us?
There was a problem hiding this comment.
This feels like a place where replacing the != NULL check for base overrides with _PyType_HasOwnNumberMethods(base) would be a good idea, just so all subtypes with operator overloads aren't performing 35 COPYNUM ops (involving ~70 branches) per non-overloaded MRO base when we could know up front whether or not the parent type has any number methods that might need to be inherited. No, the cost isn't paid often, but given that literally every newly defined type includes object in its MRO, that means the definition of every class with a single operator overload from the set of 35 (e.g. nb_bool) now goes through ~70 extra branches for object (and another ~70 for numbers.Number if they directly inherit rather than register as a virtual subclass). Could conceivably harm startup time given how many types include some overload.
There was a problem hiding this comment.
Same comment as for tp_as_number (just less critical, since there are fewer slots here).
There was a problem hiding this comment.
Same comment as for tp_as_number (just less critical, since there are fewer slots here).
| @@ -9303,6 +9230,7 @@ type_ready_inherit(PyTypeObject *type) | |||
| if (base != NULL) { | |||
| type_ready_inherit_as_structs(type, base); | |||
| } | |||
| type_ready_install_method_tables(type); | |||
There was a problem hiding this comment.
There is a comment in type_ready that says "Still, not all type objects go through PyType_Ready." Is that comment still accurate? Because if so, then:
- Any internal types that skip
PyType_Readywill need this changed manually, and - If there are external types that skip
PyType_Ready(I'd hope not, the current docs say it should be done for all types, but I'm not sure how far back that requirement goes), they're going to violate the "tp_as_XXXis neverNULL" constraint and segfault when tested for any of these features, and make me worried about the fate of this change entirely (though I suppose we could just say "Fix your extensions" to low-side packages that aren't callingPyType_Ready)
| @@ -1503,8 +1503,9 @@ store_subscr_fail_kind(PyObject *container, PyObject *sub) | |||
| { | |||
| PyTypeObject *container_type = Py_TYPE(container); | |||
| PyMappingMethods *as_mapping = container_type->tp_as_mapping; | |||
| if (as_mapping && (as_mapping->mp_ass_subscript | |||
| == PyDict_Type.tp_as_mapping->mp_ass_subscript)) { | |||
| if (as_mapping->mp_ass_subscript | |||
There was a problem hiding this comment.
Could probably combine these lines at this point (yes, it's technically >79 characters, but only just, and the source code regularly goes over that limit by a few characters; ~5% of all lines are 80+ characters), and it feels more readable testing on one line.
Benchmark results
map_check_intmap_check_objseq_check_objseq_check_intseq_check_tupleseq_check_listmap_check_dictmap_check_tuplegetitem_dictmul_str_intmul_int_strmap_check_listmul_int_listmul_list_intgetitem_listgetitem_tuplegetitem_strbool_nonebool_dictbool_listseq_check_dictbool_objbool_intBenchmark scripts
Build flags:
./configure --enable-experimental-jit --enable-optimizations --with-ltoP.S.
Different build flags might affect the benchmarks, but I haven't had the time to test them yet.
I did see a regression on
PyMapping_Checkmatching @picnixz's concern(#149317 (comment)) w/--enable-experimental-jit(no PGO/LTO) build only, but I suspect my testing at the time wasn't sufficiently rigorous. Will take a closer look later.tp_as_number,tp_as_sequenceandtp_as_mapping#149180