classification
Title: Performance regression for making bound methods
Type: performance Stage:
Components: Interpreter Core Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: benjamin.peterson, inada.naoki, pablogsal, rhettinger, serhiy.storchaka, vstinner
Priority: normal Keywords:

Created on 2019-12-21 20:13 by rhettinger, last changed 2020-04-28 21:51 by vstinner.

Messages (7)
msg358781 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-12-21 20:13
$ python3.9 -m timeit -r 11 -s 'class A: pass' -s 'A.m = lambda s: None' -s 'a = A()' 'a.m; a.m; a.m; a.m; a.m'
1000000 loops, best of 11: 230 nsec per loop

$ python3.8 -m timeit -r 11 -s 'class A: pass' -s 'A.m = lambda s: None' -s 'a = A()' 'a.m; a.m; a.m; a.m; a.m'
2000000 loops, best of 11: 149 nsec per loop

$ python3.7 -m timeit -r 11 -s 'class A: pass' -s 'A.m = lambda s: None' -s 'a = A()' 'a.m; a.m; a.m; a.m; a.m'
2000000 loops, best of 11: 159 nsec per loop

$ python3.6 -m timeit -r 11 -s 'class A: pass' -s 'A.m = lambda s: None' -s 'a = A()' 'a.m; a.m; a.m; a.m; a.m'
10000000 loops, best of 11: 0.159 usec per loop

Timings made using the recent released python.org macOS 64-bit builds.
msg358802 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2019-12-23 01:27
Probably #37340
msg358807 - (view) Author: Inada Naoki (inada.naoki) * (Python committer) Date: 2019-12-23 08:50
Is this regression is large enough to revive the free_list for bound methods?
msg367556 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-04-28 19:18
This performance regression in still present in 3.9.0a6

Results from Tools/scripts/var_access_benchmark.py:

                         Python 3.8.2       Python 3.9.0a6
                         ------------       --------------
  read_boundmethod          27.7 ns	        47.1 ns




> Is this regression is large enough to revive the free_list 
> for bound methods?

Yes!
msg367566 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2020-04-28 21:37
>  read_boundmethod          27.7 ns	        47.1 ns

Extract of Tools/scripts/var_access_benchmark.py:

def read_boundmethod(trials=trials, a=A()):
    for t in trials:
        a.m;    a.m;    a.m;    a.m;    a.m
        a.m;    a.m;    a.m;    a.m;    a.m
        a.m;    a.m;    a.m;    a.m;    a.m
        a.m;    a.m;    a.m;    a.m;    a.m
        a.m;    a.m;    a.m;    a.m;    a.m

Which kind of code pattern is impacted by this performance regression, apart this micro-benchmark? Do you notice a significant slowdown in pyperformance?

When pyperformance was run before the change was merged, there was no significant difference:
https://bugs.python.org/issue37340#msg348425

In bpo-37340, you wrote that sorted(data, key=str.upper) is 70% slower. Would you mind to provide the benchmark?
msg367567 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2020-04-28 21:49
I compared sorted(data, key=str.upper) performance between before the removal of the free list (parent of commit 3e54b575313c64f541e98216ed079fafed01ff5d) and the current master branch, I get:

Mean +- std dev: [master] 167 us +- 4 us -> [cache] 179 us +- 7 us: 1.07x slower (+7%)

I cannot reproduce the "70% slowdown".

Raymond: How did you compile Python? What is your OS? How did you run your benchmark?

---

It's a quick & dirty benchmark run. I didn't use LTO+PGO and I didn't isolate my CPU. Maybe the difference of 12 ns is just caused by the noise of my coarse benchmark procedure.

I used commands:

# master
git checkout master
git clean -fdx
./configure && make && ./python -m venv env && env/bin/python -m pip install pyperf
env/bin/python -m pyperf timeit -s 'import random; data = [f"x{i}" for i in range(1000)]; random.shuffle(data)' 'sorted(data, key=str.upper)' -o ../master.json

# before removal of the free list
git checkout 3e54b575313c64f541e98216ed079fafed01ff5d^
git clean -fdx
./configure && make && ./python -m venv env && env/bin/python -m pip install pyperf
env/bin/python -m pyperf timeit -s 'import random; data = [f"x{i}" for i in range(1000)]; random.shuffle(data)' 'sorted(data, key=str.upper)' -o ../master.json

That's a Fedora 31 using GCC -O3 (GCC 9.3.1).

--

The commit before the removal of the free list is:

commit 76b645124b3aaa34bc664eece43707c01ef1b382 (HEAD)
Date:   Fri Jul 26 03:30:33 2019 +0200

Well, maybe I did a mistake.
msg367568 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2020-04-28 21:51
By the way, in sorted(data, key=str.upper): str.upper is an unbound method, no?

$ ./python
Python 3.9.0a6+ (heads/master:d9a43e20fa, Apr 28 2020, 23:50:37) 
>>> type(str.upper)
<class 'method_descriptor'>
History
Date User Action Args
2020-04-28 21:51:46vstinnersetmessages: + msg367568
2020-04-28 21:49:27vstinnersetmessages: + msg367567
2020-04-28 21:37:41vstinnersetnosy: + vstinner
messages: + msg367566
2020-04-28 19:18:56rhettingersetmessages: + msg367556
2019-12-27 20:35:52pablogsalsetnosy: + pablogsal
2019-12-23 08:50:39inada.naokisetmessages: + msg358807
2019-12-23 08:15:02serhiy.storchakasetnosy: + inada.naoki, serhiy.storchaka
2019-12-23 01:27:24benjamin.petersonsetnosy: + benjamin.peterson
messages: + msg358802
2019-12-21 20:13:01rhettingercreate