Skip to content

Commit

Permalink
GH-91719: Make MSVC generate somewhat faster switch code (#91718)
Browse files Browse the repository at this point in the history
Apparently a switch on an 8-bit quantity where all cases are
present generates a more efficient jump (doing only one indexed
memory load instead of two).

So we make opcode and use_tracing uint8_t, and generate a macro
full of extra `case NNN:` lines for all unused opcodes.

See faster-cpython/ideas#321 (comment)
  • Loading branch information
gvanrossum authored Apr 21, 2022
1 parent d44815c commit f8dc618
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ typedef struct _PyCFrame {
* discipline and make sure that instances of this struct cannot
* accessed outside of their lifetime.
*/
int use_tracing;
uint8_t use_tracing; // 0 or 255 (or'ed into opcode, hence 8-bit type)
/* Pointer to the currently executing frame (it can be NULL) */
struct _PyInterpreterFrame *current_frame;
struct _PyCFrame *previous;
Expand Down
78 changes: 78 additions & 0 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
#ifdef Py_STATS
int lastopcode = 0;
#endif
int opcode; /* Current opcode */
// opcode is an 8-bit value to improve the code generated by MSVC
// for the big switch below (in combination with the EXTRA_CASES macro).
uint8_t opcode; /* Current opcode */
int oparg; /* Current opcode argument, if any */
_Py_atomic_int * const eval_breaker = &tstate->interp->ceval.eval_breaker;

Expand Down Expand Up @@ -5645,7 +5647,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
#if USE_COMPUTED_GOTOS
_unknown_opcode:
#else
default:
EXTRA_CASES // From opcode.h, a 'case' for each unused opcode
#endif
fprintf(stderr, "XXX lineno: %d, opcode: %d\n",
_PyInterpreterFrame_GetLine(frame), opcode);
Expand Down
7 changes: 7 additions & 0 deletions Tools/scripts/generate_opcode_h.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ def main(opcode_py, outfile='Include/opcode.h'):
fobj.write("};\n")
fobj.write("#endif\n")

fobj.write("\n")
fobj.write("#define EXTRA_CASES \\\n")
for i, flag in enumerate(used):
if not flag:
fobj.write(f" case {i}: \\\n")
fobj.write(" ;\n")

fobj.write(footer)


Expand Down

0 comments on commit f8dc618

Please sign in to comment.