Title: Performance degradation of attribute accesses in Python 3.7.4
Type: performance Stage: resolved
Components: Interpreter Core Versions: Python 3.7
Status: closed Resolution: out of date
Dependencies: Superseder:
Assigned To: Nosy List: iritkatriel, serhiy.storchaka, terry.reedy, ttrd
Priority: normal Keywords:

Created on 2019-10-21 08:15 by ttrd, last changed 2021-11-22 21:52 by iritkatriel. This issue is now closed.

File name Uploaded Description Edit ttrd, 2019-10-21 08:15
Messages (4)
msg355029 - (view) Author: (ttrd) Date: 2019-10-21 08:15
After upgrading from Python 3.7.3 to Python 3.7.4, we noticed an increased runtime of about 50% in some tests of our application.

After some testing we constructed the following hypothesis: Accessing instance attributes of classes that define a custom metaclass take more time in Python 3.7.4. The access takes the more time the more classes are in the MRO of the concerning class (in other words the more base classes the concerning class has got).

We verified the hypothesis by creating a minimal example (file attached) that produced the output at the bottom. (The index defines the number of base classes. The four variants are with/without a metaclass and with simple/multiple inheritance. The kind of inheritance has no influence on the runtime.)

In Python 3.7.4 (in contrast to 3.7.3 and 3.8), you can see that the usage of a metaclass results in a linear increase of the runtime of attribute accesses depending on the number of base classes.

We could also identify the issue/commit that introduced the performance issue:

In the Python 3.8 branch, the performance issue seems to be resolved by:

Will this performance issue be addressed in a future 3.7 version (e.g. 3.7.6) or is it in your optinion sufficient that the issue seems to be resolved in 3.8?

Python 3.7.3 (constant runtime)

3.668036e-08 noMetaBaseX[1].attr
3.630355e-08 noMetaBaseX[2].attr
3.687350e-08 noMetaBaseX[5].attr
3.827102e-08 noMetaBaseX[10].attr
3.724956e-08 noMetaBaseX[20].attr
3.912778e-08 noMetaBaseX[50].attr
4.072552e-08 noMetaBaseX[100].attr
3.620875e-08 metaBaseX[1].attr
3.640054e-08 metaBaseX[2].attr
3.599339e-08 metaBaseX[5].attr
3.606570e-08 metaBaseX[10].attr
3.946514e-08 metaBaseX[20].attr
3.715085e-08 metaBaseX[50].attr
3.762364e-08 metaBaseX[100].attr
3.667802e-08 noMetaRecBaseX[1].attr
3.579415e-08 noMetaRecBaseX[2].attr
3.647997e-08 noMetaRecBaseX[5].attr
3.577428e-08 noMetaRecBaseX[10].attr
3.686319e-08 noMetaRecBaseX[20].attr
3.832261e-08 noMetaRecBaseX[50].attr
4.087625e-08 noMetaRecBaseX[100].attr
3.654509e-08 noMetaRecBaseX[1].attr
3.617825e-08 noMetaRecBaseX[2].attr
3.573970e-08 noMetaRecBaseX[5].attr
3.664559e-08 noMetaRecBaseX[10].attr
3.702526e-08 noMetaRecBaseX[20].attr
3.774067e-08 noMetaRecBaseX[50].attr
3.836915e-08 noMetaRecBaseX[100].attr

Python 3.7.4 (see lines marked with ->)

4.042378e-08 noMetaBaseX[1].attr
3.704071e-08 noMetaBaseX[2].attr
3.868766e-08 noMetaBaseX[5].attr
3.716869e-08 noMetaBaseX[10].attr
3.693100e-08 noMetaBaseX[20].attr
3.947849e-08 noMetaBaseX[50].attr
3.790146e-08 noMetaBaseX[100].attr
-> 5.010019e-08 metaBaseX[1].attr
-> 5.215336e-08 metaBaseX[2].attr
-> 6.103393e-08 metaBaseX[5].attr
-> 7.947347e-08 metaBaseX[10].attr
-> 1.082155e-07 metaBaseX[20].attr
-> 2.264330e-07 metaBaseX[50].attr
-> 4.951039e-07 metaBaseX[100].attr
3.727378e-08 noMetaRecBaseX[1].attr
3.815971e-08 noMetaRecBaseX[2].attr
3.661390e-08 noMetaRecBaseX[5].attr
3.709824e-08 noMetaRecBaseX[10].attr
3.785757e-08 noMetaRecBaseX[20].attr
3.813983e-08 noMetaRecBaseX[50].attr
3.856352e-08 noMetaRecBaseX[100].attr
-> 5.260115e-08 metaRecBaseX[1].attr
-> 5.661043e-08 metaRecBaseX[2].attr
-> 6.760995e-08 metaRecBaseX[5].attr
-> 8.038124e-08 metaRecBaseX[10].attr
-> 1.100538e-07 metaRecBaseX[20].attr
-> 2.246786e-07 metaRecBaseX[50].attr
-> 4.877147e-07 metaRecBaseX[100].attr
​Python 3.8.0rc1 (constant runtime)

​4.045787e-08 noMetaBaseX[1].attr
4.206206e-08 noMetaBaseX[2].attr
4.209608e-08 noMetaBaseX[5].attr
4.049552e-08 noMetaBaseX[10].attr
4.133343e-08 noMetaBaseX[20].attr
4.229599e-08 noMetaBaseX[50].attr
4.259420e-08 noMetaBaseX[100].attr
4.146996e-08 metaBaseX[1].attr
4.140459e-08 metaBaseX[2].attr
4.143048e-08 metaBaseX[5].attr
4.054639e-08 metaBaseX[10].attr
4.219960e-08 metaBaseX[20].attr
4.222810e-08 metaBaseX[50].attr
4.420123e-08 metaBaseX[100].attr
4.044410e-08 noMetaRecBaseX[1].attr
4.043790e-08 noMetaRecBaseX[2].attr
4.209061e-08 noMetaRecBaseX[5].attr
4.042041e-08 noMetaRecBaseX[10].attr
4.048202e-08 noMetaRecBaseX[20].attr
4.260607e-08 noMetaRecBaseX[50].attr
4.267928e-08 noMetaRecBaseX[100].attr
4.037958e-08 metaRecBaseX[1].attr
3.968827e-08 metaRecBaseX[2].attr
4.011758e-08 metaRecBaseX[5].attr
3.954960e-08 metaRecBaseX[10].attr
4.057979e-08 metaRecBaseX[20].attr
4.145640e-08 metaRecBaseX[50].attr
4.148783e-08 metaRecBaseX[100].attr
msg355378 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2019-10-25 19:50
I added a note to #28866 about this.  The fix in #36922 is likely an enhancement that cannot be backported.  Please retest with 3.7.5 if you can, though I have no expectation of any change.
msg355624 - (view) Author: (ttrd) Date: 2019-10-29 07:34
I just ran the test with Python 3.7.5: As expected, I got the same results as in Python 3.7.4.

Nevertheless, thanks for the response. So we will stay with Python 3.7.3 for now and switch to Python 3.8 as soon as possible.
msg406805 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-11-22 21:52
Closing as this is resolved in 3.8 and we won't do anything about 3.7.
Date User Action Args
2021-11-22 21:52:57iritkatrielsetstatus: open -> closed

nosy: + iritkatriel
messages: + msg406805

resolution: out of date
stage: resolved
2019-10-29 07:34:04ttrdsetmessages: + msg355624
2019-10-25 19:50:03terry.reedysetnosy: + terry.reedy, serhiy.storchaka
messages: + msg355378
2019-10-21 08:15:44ttrdcreate