This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Ensure that all PyTypeObject fields are set to non-NULL defaults
Type: performance Stage: resolved
Components: Interpreter Core Versions: Python 3.7
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: pdox, rhettinger, serhiy.storchaka
Priority: normal Keywords:

Created on 2017-10-15 06:22 by pdox, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (5)
msg304420 - (view) Author: (pdox) * Date: 2017-10-15 06:22
Ensure that every function pointer in every PyTypeObject is set to a non-NULL default, to spare the cost of checking for NULL with every use. As a basic example, consider PyNumber_Negative:

PyObject *
PyNumber_Negative(PyObject *o)
{
    PyNumberMethods *m;

    if (o == NULL) {
        return null_error();
    }

    m = o->ob_type->tp_as_number;
    if (m && m->nb_negative)
        return (*m->nb_negative)(o);

    return type_error("bad operand type for unary -: '%.200s'", o);
}

If "tp_as_number" and "nb_negative" were always guaranteed non-NULL, then the function could omit the second if statement, and invoke the function pointer directly. To maintain the existing behavior, the default nb_negative function would be set to the following:

PyObject* nb_negative_default(PyObject *o) {
    return type_error("bad operand type for unary -: '%.200s'", o);
}

This removes two NULL-checks from the PyNumber_Negative. Many other operators and builtins would be able to benefit in the same way.
msg304421 - (view) Author: (pdox) * Date: 2017-10-15 06:33
I believe it would also make sense to inline the 'as' structures (tp_as_async, tp_as_number, tp_as_sequence, tp_as_mapping, and tp_as_buffer), instead of having them be pointers. This would save one pointer dereference with each use. But this would have API compatibility consequences, so for this change it is off the table.
msg304422 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-15 06:59
1. This will increase the size of type object. Most types are not numbers, not collections and don't support the buffer protocol, thus save memory for tp_as_number, tp_as_sequence, tp_as_mapping and tp_as_buffer. This also can increase the time of type creation.

2. This will make harder testing that the particular protocol is supported (e.g. see PyIndex_Check or PyObject_CheckBuffer). And this will break any third-party code that does this directly, without using C API.

3. Calling function instead of executing inlined code after checking can increase stack consumption and slow down execution (see the usage of tp_descr_get).
msg304423 - (view) Author: (pdox) * Date: 2017-10-15 07:25
serhiy.storchaka:

1) Where tp_as_number would normally be NULL, instead it would point to a fixed PyNumberMethods structure containing the default functions. This would make the memory increase negligible, as all non-number types would use the same structure.

2) If this is behavior we want to formally support, then we should provide macros for it. (all they must do is compare against the default pointer value, rather than NULL). Are there particular extension(s) you suspect may be doing this?

3) This has to be handled on a case-by-case basis. I would not remove inlined optimizations. If there are some fields that truly benefit from remaining NULL, those can be left alone. I would like to focus on the functions for which the common case (in code without type errors) is to make the indirect call.
msg304448 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-10-15 20:34
For all the reasons Serhiy mentioned, I recommend rejecting this request.  This was an intentional and long-standing design decision. A substantial part of the Python ecosystem depends on it.
History
Date User Action Args
2022-04-11 14:58:53adminsetgithub: 75972
2021-07-15 12:46:38iritkatrielsetstatus: open -> closed
resolution: rejected
stage: resolved
2017-10-15 20:34:11rhettingersetnosy: + rhettinger
messages: + msg304448
2017-10-15 07:25:24pdoxsetmessages: + msg304423
2017-10-15 06:59:30serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg304422
2017-10-15 06:33:10pdoxsetmessages: + msg304421
2017-10-15 06:22:29pdoxcreate