Mercurial > cpython-lru_cache-weakref
diff Python/ceval.c @ 28302:8c082936daaf legacy-trunk
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.
| author | Raymond Hettinger <python@rcn.com> |
|---|---|
| date | Sun, 16 Mar 2003 03:11:04 +0000 |
| parents | 7654c24decc4 |
| children | 470504ef578b |
line wrap: on
line diff
--- a/Python/ceval.c Sat Mar 15 12:25:00 2003 +0000 +++ b/Python/ceval.c Sun Mar 16 03:11:04 2003 +0000 @@ -602,6 +602,26 @@ #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 @@ SETLOCAL(oparg, v); goto fast_next_opcode; + PREDICTED(POP_TOP); case POP_TOP: v = POP(); Py_DECREF(v); @@ -920,7 +941,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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;
