summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2003-03-16 03:11:04 (GMT)
committerRaymond Hettinger <python@rcn.com>2003-03-16 03:11:04 (GMT)
commitf606f87b31186e7614cbae8e5b8ef05700f6267e (patch)
tree66c3ae468afb4b6ae4684a904d01d9a332a825b8 /Python/ceval.c
parent0070f007f4b30a31e7f59ec798ae6f555bcb0c08 (diff)
downloadcpython-f606f87b31186e7614cbae8e5b8ef05700f6267e.zip
cpython-f606f87b31186e7614cbae8e5b8ef05700f6267e.tar.gz
cpython-f606f87b31186e7614cbae8e5b8ef05700f6267e.tar.bz2
Introduced macros for a simple opcode prediction protocol.
Applied to common cases: COMPARE_OP is often followed by a JUMP_IF. JUMP_IF is usually followed by POP_TOP. Shows improved timings on PyStone, PyBench, and specific tests using timeit.py: python timeit.py -s "x=1" "if x==1: pass" python timeit.py -s "x=1" "if x==2: pass" python timeit.py -s "x=1" "if x: pass" python timeit.py -s "x=100" "while x!=1: x-=1" Potential future candidates: GET_ITER predicts FOR_ITER FOR_ITER predicts STORE_FAST or UNPACK_SEQUENCE Also, applied missing goto fast_next_opcode to DUP_TOPX.
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c41
1 files changed, 35 insertions, 6 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index 324008d..384bfbe 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -602,6 +602,26 @@ eval_frame(PyFrameObject *f)
#define JUMPTO(x) (next_instr = first_instr + (x))
#define JUMPBY(x) (next_instr += (x))
+/* OpCode prediction macros
+ Some opcodes tend to come in pairs thus making it possible to predict
+ the second code when the first is run. For example, COMPARE_OP is often
+ followed by JUMP_IF_FALSE or JUMP_IF_TRUE. And, those opcodes are often
+ followed by a POP_TOP.
+
+ Verifying the prediction costs a single high-speed test of register
+ variable against a constant. If the pairing was good, then the odds
+ processor has a high likelihood of making its own successful branch
+ prediction which results in a nearly zero overhead transition to the
+ next opcode.
+
+ A successful prediction saves a trip through the eval-loop including
+ its two unpredictable branches, the HASARG test and the switch-case.
+*/
+
+#define PREDICT(op) if (*next_instr == op) goto PRED_##op
+#define PREDICTED(op) PRED_##op: next_instr++
+#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = (next_instr += 3, (next_instr[-1]<<8) + next_instr[-2])
+
/* Stack manipulation macros */
#define STACK_LEVEL() (stack_pointer - f->f_valuestack)
@@ -873,6 +893,7 @@ eval_frame(PyFrameObject *f)
SETLOCAL(oparg, v);
goto fast_next_opcode;
+ PREDICTED(POP_TOP);
case POP_TOP:
v = POP();
Py_DECREF(v);
@@ -920,7 +941,7 @@ eval_frame(PyFrameObject *f)
STACKADJ(2);
SET_TOP(x);
SET_SECOND(w);
- continue;
+ goto fast_next_opcode;
} else if (oparg == 3) {
x = TOP();
Py_INCREF(x);
@@ -932,7 +953,7 @@ eval_frame(PyFrameObject *f)
SET_TOP(x);
SET_SECOND(w);
SET_THIRD(v);
- continue;
+ goto fast_next_opcode;
}
Py_FatalError("invalid argument to DUP_TOPX"
" (bytecode corruption?)");
@@ -1918,8 +1939,10 @@ eval_frame(PyFrameObject *f)
Py_DECREF(v);
Py_DECREF(w);
SET_TOP(x);
- if (x != NULL) continue;
- break;
+ if (x == NULL) break;
+ PREDICT(JUMP_IF_FALSE);
+ PREDICT(JUMP_IF_TRUE);
+ continue;
case IMPORT_NAME:
w = GETITEM(names, oparg);
@@ -1974,10 +1997,13 @@ eval_frame(PyFrameObject *f)
JUMPBY(oparg);
goto fast_next_opcode;
+ PREDICTED_WITH_ARG(JUMP_IF_FALSE);
case JUMP_IF_FALSE:
w = TOP();
- if (w == Py_True)
+ if (w == Py_True) {
+ PREDICT(POP_TOP);
goto fast_next_opcode;
+ }
if (w == Py_False) {
JUMPBY(oparg);
goto fast_next_opcode;
@@ -1991,10 +2017,13 @@ eval_frame(PyFrameObject *f)
break;
continue;
+ PREDICTED_WITH_ARG(JUMP_IF_TRUE);
case JUMP_IF_TRUE:
w = TOP();
- if (w == Py_False)
+ if (w == Py_False) {
+ PREDICT(POP_TOP);
goto fast_next_opcode;
+ }
if (w == Py_True) {
JUMPBY(oparg);
goto fast_next_opcode;