You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Implement LOAD_METHOD/CALL_METHOD for C functions #73449
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2017-02-02.23:13:20.079>created_at=<Date2017-01-13.16:09:52.414>labels= ['3.7', 'performance']
title='Implement LOAD_METHOD/CALL_METHOD for C functions'updated_at=<Date2017-02-02.23:13:20.078>user='https://github.com/vstinner'
The issue bpo-29259 implements tp_fastcall on method_descriptor (PyMethodDescr_Type). According to http://bugs.python.org/issue26110#msg283093 it would allow to implement LOAD_METHOD and CALL_METHOD for C functions.
INADA Naoki: "Maybe, we should do: * Make clinic use more METH_FASTCALL * Use clinic more in builtin methods; before trying this optimization."
I created the issue bpo-29286 "Use METH_FASTCALL in str methods" which is now fixed. I started to propose patches for other funnctions, types and modules.
Would you mind to rebase your loadmethod-fastcall.patch patch Naoki?
With my change 0b219348ec9e, I'm not sure that your change on methoddescr_call() is still needed.
I tried to rebase your change, but I failed to get the "1.10x faster" result. Maybe I did something wrong, maybe my change 0b219348ec9e already optimized method calls for C functions?
INADA Naoki: "LOAD_METHOD support was based on tp_fastcall. (...) Other ideas to support LOAD_METHOD for C function are: * Add _PyMethodDescr_FastCallKeywords and call it in call_function."
It seems like a very small subset of tp_fastcall. If it provides a significant speedup, I really like the idea :-) I would be happy to see a "subset of tp_fastcall" merged. At least my whole tp_fastcall experiment wouldn't be worthless :-)
What would be the main change which would provide a speedup? Avoid the creation of a temporary tuple to pass positional arguments?
Does it mean create a new private type, similar to PyCFunction_Type (PyCFunctionObject) but without the self attribute?
IMHO creating a new type is complex. I'm not sure that it's worth it, especially if the _PyMethodDescr_FastCallKeywords() idea is simpler to implement and "fast enough".
I tried to benchmark b''.decode(encoding='ascii'): CALL_METHOD is not used for this call, but CALL_FUNCTION_KW :-/ So the call is not affected by your patch.
I also ran a quick benchmark on loadmethod-methoddescr.patch:
In my experience, the best fix is PGO. Slowly, I consider that it's worthless to try to "fight" against code placement, and that benchmark results are only reliable when PGO compilation was used. Otherwise, you should ignore small performance differences. Problem: What is the threshold? 5%? 10%? I already noticed a difference up to 70% only caused by code placement! https://haypo.github.io/analysis-python-performance-issue.html
--
I ran benchmarks on loadmethod-methoddescr.patch on haypo@speed-python with LTO+PGO. If you only show performance difference of at least 5%, only 3 benchmarks are significant and are faster:
---
haypo@speed-python$ python3 -m perf compare_to ~/benchmarks/*762a93935afd*json loadmethod-methoddesc_ref_762a93935afd.json -G --min-speed=5
Faster (3):
regex_v8: 50.3 ms +- 0.4 ms -> 43.2 ms +- 0.3 ms: 1.17x faster (-14%)
scimark_monte_carlo: 230 ms +- 6 ms -> 208 ms +- 4 ms: 1.11x faster (-10%)
scimark_lu: 390 ms +- 17 ms -> 370 ms +- 13 ms: 1.05x faster (-5%)
Benchmark hidden because not significant (61): (...)
---
In my experience, regex_v8 and scimark_* benchmarks are not really reliable. I'm not surprised to not see a major speedup on performance, since the speedup on microbenchmarks is only 10% faster.
IHMO 10% faster on method calls is significant enough, since it's a core, very common, and widely used Python feature.
--
To be more explicitl: loadmethod-methoddescr.patch LGTM except of minor comments on the review.
Activity
vstinner commentedon Jan 13, 2017
The issue bpo-29259 implements tp_fastcall on method_descriptor (PyMethodDescr_Type). According to http://bugs.python.org/issue26110#msg283093 it would allow to implement LOAD_METHOD and CALL_METHOD for C functions.
methane commentedon Jan 14, 2017
This is proof of concept patch to support LOAD_METHOD & CALL_METHOD for METH_FASTCALL methods.
(This patch should be applied after fastcall.patch)
Median +- std dev: [python-fastcall] 91.0 ns +- 1.0 ns -> [python] 80.3 ns +- 0.3 ns: 1.13x faster
Median +- std dev: [python-fastcall] 116 ns +- 1 ns -> [python] 106 ns +- 1 ns: 1.10x faster
Since PyCFunction is lighter than PyMethodObject, performance benefit seems smaller than
Python method (up to 20%).
Sadly speaking, there are only few METH_FASTCALL in builtin type.
methane commentedon Jan 14, 2017
Maybe, we should do
before trying this optimization.
python-dev commentedon Jan 18, 2017
New changeset 0b219348ec9e by Victor Stinner in branch 'default':
Optimize methoddescr_call(): avoid temporary PyCFunction
https://hg.python.org/cpython/rev/0b219348ec9e
vstinner commentedon Feb 1, 2017
INADA Naoki: "Maybe, we should do: * Make clinic use more METH_FASTCALL * Use clinic more in builtin methods; before trying this optimization."
I created the issue bpo-29286 "Use METH_FASTCALL in str methods" which is now fixed. I started to propose patches for other funnctions, types and modules.
Would you mind to rebase your loadmethod-fastcall.patch patch Naoki?
With my change 0b219348ec9e, I'm not sure that your change on methoddescr_call() is still needed.
I tried to rebase your change, but I failed to get the "1.10x faster" result. Maybe I did something wrong, maybe my change 0b219348ec9e already optimized method calls for C functions?
methane commentedon Feb 2, 2017
LOAD_METHOD support was based on tp_fastcall.
Without LOAD_METHOD support, methoddescr_get is called and
stack layout after LOAD_METHOD is:
NULL, (bound)PyCFunction, arg1, arg2, ... argN
With LOAD_METHOD support, stack layout is:
PyMethodDescrObject, self, arg1, ... argN
And tp_call (or tp_fastcall) of method descriptor is called when CALL_METHOD.
Without tp_fastcall, it means pack&unpack tuple happens.
It is more heavy than creating temporary PyCFunction.
---
Other ideas to support LOAD_METHOD for C function are:
1st1 commentedon Feb 2, 2017
This option seems to be the easiest to implement.
vstinner commentedon Feb 2, 2017
INADA Naoki: "LOAD_METHOD support was based on tp_fastcall. (...) Other ideas to support LOAD_METHOD for C function are: * Add _PyMethodDescr_FastCallKeywords and call it in call_function."
It seems like a very small subset of tp_fastcall. If it provides a significant speedup, I really like the idea :-) I would be happy to see a "subset of tp_fastcall" merged. At least my whole tp_fastcall experiment wouldn't be worthless :-)
What would be the main change which would provide a speedup? Avoid the creation of a temporary tuple to pass positional arguments?
vstinner commentedon Feb 2, 2017
Does it mean create a new private type, similar to PyCFunction_Type (PyCFunctionObject) but without the self attribute?
IMHO creating a new type is complex. I'm not sure that it's worth it, especially if the _PyMethodDescr_FastCallKeywords() idea is simpler to implement and "fast enough".
methane commentedon Feb 2, 2017
I'm running pyperformance.
methane commentedon Feb 2, 2017
Here is the result. I'll investigate mako result.
Faster (11):
Benchmark hidden because not significant (33):
methane commentedon Feb 2, 2017
microbench:
$ ./python.patched -m perf timeit --compare-to
pwd/python.default -s "d = b''" -- "d.decode('ascii')"python.default: ..................... 109 ns +- 1 ns
python.patched: ..................... 102 ns +- 1 ns
Median +- std dev: [python.default] 109 ns +- 1 ns -> [python.patched] 102 ns +- 1 ns: 1.08x faster (-7%)
Median +- std dev: [python.default] 66.0 ns +- 0.9 ns -> [python.patched] 60.0 ns +- 0.3 ns: 1.10x faster (-9%)
vstinner commentedon Feb 2, 2017
I tried to benchmark b''.decode(encoding='ascii'): CALL_METHOD is not used for this call, but CALL_FUNCTION_KW :-/ So the call is not affected by your patch.
I also ran a quick benchmark on loadmethod-methoddescr.patch:
b''.decode(): 71.1 ns +- 0.5 ns -> 65.4 ns +- 0.2 ns: 1.09x faster (-8%)
b''.decode('utf8'): 92.8 ns +- 0.4 ns -> 85.5 ns +- 0.3 ns: 1.09x faster (-8%)
I confirm the speedup.
methane commentedon Feb 2, 2017
I confirmed bm_mako performance degrade is caused by L1 cache miss.
(I didn't use --with-optimization)
https://gist.github.com/methane/b33edbf1f123ae026e704b0e005c3606
vstinner commentedon Feb 2, 2017
Naoki> I confirmed bm_mako performance degrade is caused by L1 cache miss.
I know this performance instability very well, the issue is called "code placement":
https://haypo.github.io/journey-to-stable-benchmark-deadcode.html
I tried to fight it with GCC __attribute__((hot)) in the issue bpo-28618, but it doesn't fix the issue (at least, not completely):
http://bugs.python.org/issue28618#msg281459
In my experience, the best fix is PGO. Slowly, I consider that it's worthless to try to "fight" against code placement, and that benchmark results are only reliable when PGO compilation was used. Otherwise, you should ignore small performance differences. Problem: What is the threshold? 5%? 10%? I already noticed a difference up to 70% only caused by code placement!
https://haypo.github.io/analysis-python-performance-issue.html
--
I ran benchmarks on loadmethod-methoddescr.patch on haypo@speed-python with LTO+PGO. If you only show performance difference of at least 5%, only 3 benchmarks are significant and are faster:
---
haypo@speed-python$ python3 -m perf compare_to ~/benchmarks/*762a93935afd*json loadmethod-methoddesc_ref_762a93935afd.json -G --min-speed=5
Faster (3):
Benchmark hidden because not significant (61): (...)
---
In my experience, regex_v8 and scimark_* benchmarks are not really reliable. I'm not surprised to not see a major speedup on performance, since the speedup on microbenchmarks is only 10% faster.
IHMO 10% faster on method calls is significant enough, since it's a core, very common, and widely used Python feature.
--
To be more explicitl: loadmethod-methoddescr.patch LGTM except of minor comments on the review.
vstinner commentedon Feb 2, 2017
loadmethod-methoddescr-2.patch LGTM.
python-dev commentedon Feb 2, 2017
New changeset 135a9a0c09f9 by INADA Naoki in branch 'default':
Issue bpo-29263: LOAD_METHOD support for C methods
https://hg.python.org/cpython/rev/135a9a0c09f9
python-dev commentedon Feb 2, 2017
New changeset 48bde5d2cc5b9e98f55cd23ee57f3996d81caeb5 by INADA Naoki in branch 'master':
Issue bpo-29263: LOAD_METHOD support for C methods
48bde5d