Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9a2310d

Browse files
committed
Merged revisions 65240-65242 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r65240 | antoine.pitrou | 2008-07-26 00:02:07 +0200 (sam., 26 juil. 2008) | 3 lines add a pybench test for complex function calls (part of #1819) ........ r65241 | antoine.pitrou | 2008-07-26 00:13:52 +0200 (sam., 26 juil. 2008) | 4 lines Raymond's patch for #1819: speedup function calls with named parameters (35% faster according to pybench) ........ r65242 | antoine.pitrou | 2008-07-26 00:22:08 +0200 (sam., 26 juil. 2008) | 3 lines add a NEWS entry ........
1 parent 83d6a87 commit 9a2310d

3 files changed

Lines changed: 100 additions & 26 deletions

File tree

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ What's new in Python 3.0b3?
99

1010
*Release date: XX-XXX-2008*
1111

12+
Core and Builtins
13+
-----------------
14+
15+
- Issue #1819: function calls with several named parameters are now on
16+
average 35% faster (as measured by pybench).
17+
1218
Library
1319
-------
1420

Python/ceval.c

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -642,9 +642,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
642642
processor's own internal branch predication has a high likelihood of
643643
success, resulting in a nearly zero-overhead transition to the
644644
next opcode. A successful prediction saves a trip through the eval-loop
645-
including its two unpredictable branches, the HAS_ARG test and the
645+
including its two unpredictable branches, the HAS_ARG test and the
646646
switch-case. Combined with the processor's internal branch prediction,
647-
a successful PREDICT has the effect of making the two opcodes run as if
647+
a successful PREDICT has the effect of making the two opcodes run as if
648648
they were a single new opcode with the bodies combined.
649649
650650
If collecting opcode statistics, your choices are to either keep the
@@ -796,7 +796,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
796796
an argument which depends on the situation.
797797
The global trace function is also called
798798
whenever an exception is detected. */
799-
if (call_trace_protected(tstate->c_tracefunc,
799+
if (call_trace_protected(tstate->c_tracefunc,
800800
tstate->c_traceobj,
801801
f, PyTrace_CALL, Py_None)) {
802802
/* Trace function raised an error */
@@ -828,10 +828,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
828828
this wasn't always true before 2.3! PyFrame_New now sets
829829
f->f_lasti to -1 (i.e. the index *before* the first instruction)
830830
and YIELD_VALUE doesn't fiddle with f_lasti any more. So this
831-
does work. Promise.
831+
does work. Promise.
832832
833833
When the PREDICT() macros are enabled, some opcode pairs follow in
834-
direct succession without updating f->f_lasti. A successful
834+
direct succession without updating f->f_lasti. A successful
835835
prediction effectively links the two codes together as if they
836836
were a single new opcode; accordingly,f->f_lasti will point to
837837
the first code in the pair (for instance, GET_ITER followed by
@@ -1678,7 +1678,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
16781678
{
16791679
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
16801680
v = POP();
1681-
1681+
16821682
if (unpack_iterable(v, oparg & 0xFF, oparg >> 8,
16831683
stack_pointer + totalargs)) {
16841684
stack_pointer += totalargs;
@@ -2071,7 +2071,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
20712071
because it prevents detection of a control-break in tight loops like
20722072
"while 1: pass". Compile with this option turned-on when you need
20732073
the speed-up and do not need break checking inside tight loops (ones
2074-
that contain only instructions ending with goto fast_next_opcode).
2074+
that contain only instructions ending with goto fast_next_opcode).
20752075
*/
20762076
goto fast_next_opcode;
20772077
#else
@@ -2257,7 +2257,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
22572257
break;
22582258
}
22592259

2260-
case MAKE_CLOSURE:
2260+
case MAKE_CLOSURE:
22612261
case MAKE_FUNCTION:
22622262
{
22632263
int posdefaults = oparg & 0xff;
@@ -2267,7 +2267,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
22672267
v = POP(); /* code object */
22682268
x = PyFunction_New(v, f->f_globals);
22692269
Py_DECREF(v);
2270-
2270+
22712271
if (x != NULL && opcode == MAKE_CLOSURE) {
22722272
v = POP();
22732273
err = PyFunction_SetClosure(x, v);
@@ -2650,6 +2650,7 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
26502650
}
26512651
}
26522652
for (i = 0; i < kwcount; i++) {
2653+
PyObject **co_varnames;
26532654
PyObject *keyword = kws[2*i];
26542655
PyObject *value = kws[2*i + 1];
26552656
int j;
@@ -2659,16 +2660,25 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
26592660
co->co_name);
26602661
goto fail;
26612662
}
2662-
/* XXX slow -- speed up using dictionary? */
2663+
/* Speed hack: do raw pointer compares. As names are
2664+
normally interned this should almost always hit. */
2665+
co_varnames = PySequence_Fast_ITEMS(co->co_varnames);
2666+
for (j = 0;
2667+
j < co->co_argcount + co->co_kwonlyargcount;
2668+
j++) {
2669+
PyObject *nm = co_varnames[j];
2670+
if (nm == keyword)
2671+
goto kw_found;
2672+
}
2673+
/* Slow fallback, just in case */
26632674
for (j = 0;
26642675
j < co->co_argcount + co->co_kwonlyargcount;
26652676
j++) {
2666-
PyObject *nm = PyTuple_GET_ITEM(
2667-
co->co_varnames, j);
2677+
PyObject *nm = co_varnames[j];
26682678
int cmp = PyObject_RichCompareBool(
26692679
keyword, nm, Py_EQ);
26702680
if (cmp > 0)
2671-
break;
2681+
goto kw_found;
26722682
else if (cmp < 0)
26732683
goto fail;
26742684
}
@@ -2685,20 +2695,20 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
26852695
goto fail;
26862696
}
26872697
PyDict_SetItem(kwdict, keyword, value);
2698+
continue;
26882699
}
2689-
else {
2690-
if (GETLOCAL(j) != NULL) {
2691-
PyErr_Format(PyExc_TypeError,
2692-
"%U() got multiple "
2693-
"values for keyword "
2694-
"argument '%S'",
2695-
co->co_name,
2696-
keyword);
2697-
goto fail;
2698-
}
2699-
Py_INCREF(value);
2700-
SETLOCAL(j, value);
2700+
kw_found:
2701+
if (GETLOCAL(j) != NULL) {
2702+
PyErr_Format(PyExc_TypeError,
2703+
"%U() got multiple "
2704+
"values for keyword "
2705+
"argument '%S'",
2706+
co->co_name,
2707+
keyword);
2708+
goto fail;
27012709
}
2710+
Py_INCREF(value);
2711+
SETLOCAL(j, value);
27022712
}
27032713
if (co->co_kwonlyargcount > 0) {
27042714
for (i = co->co_argcount;
@@ -2930,7 +2940,7 @@ do_raise(PyObject *exc, PyObject *cause)
29302940

29312941
/* Iterate v argcnt times and store the results on the stack (via decreasing
29322942
sp). Return 1 for success, 0 if error.
2933-
2943+
29342944
If argcntafter == -1, do a simple unpack. If it is >= 0, do an unpack
29352945
with a variable target.
29362946
*/

Tools/pybench/Calls.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,64 @@ def h(a,b,c,d=1,e=2,f=3):
109109

110110
###
111111

112+
class ComplexPythonFunctionCalls(Test):
113+
114+
version = 2.0
115+
operations = 4*5
116+
rounds = 100000
117+
118+
def test(self):
119+
120+
# define functions
121+
def f(a,b,c,d=1,e=2,f=3):
122+
return f
123+
124+
args = 1,2
125+
kwargs = dict(c=3,d=4,e=5)
126+
127+
# do calls
128+
for i in range(self.rounds):
129+
f(a=i,b=i,c=i)
130+
f(f=i,e=i,d=i,c=2,b=i,a=3)
131+
f(1,b=i,**kwargs)
132+
f(*args,**kwargs)
133+
134+
f(a=i,b=i,c=i)
135+
f(f=i,e=i,d=i,c=2,b=i,a=3)
136+
f(1,b=i,**kwargs)
137+
f(*args,**kwargs)
138+
139+
f(a=i,b=i,c=i)
140+
f(f=i,e=i,d=i,c=2,b=i,a=3)
141+
f(1,b=i,**kwargs)
142+
f(*args,**kwargs)
143+
144+
f(a=i,b=i,c=i)
145+
f(f=i,e=i,d=i,c=2,b=i,a=3)
146+
f(1,b=i,**kwargs)
147+
f(*args,**kwargs)
148+
149+
f(a=i,b=i,c=i)
150+
f(f=i,e=i,d=i,c=2,b=i,a=3)
151+
f(1,b=i,**kwargs)
152+
f(*args,**kwargs)
153+
154+
155+
def calibrate(self):
156+
157+
# define functions
158+
def f(a,b,c,d=1,e=2,f=3):
159+
return f
160+
161+
args = 1,2
162+
kwargs = dict(c=3,d=4,e=5)
163+
164+
# do calls
165+
for i in range(self.rounds):
166+
pass
167+
168+
###
169+
112170
class BuiltinFunctionCalls(Test):
113171

114172
version = 2.0

0 commit comments

Comments
 (0)