Kaydet (Commit) 8b17d6bd authored tarafından Guido van Rossum's avatar Guido van Rossum

Changes to speed up local variables enormously, by avoiding dictionary

lookup (opcode.h, ceval.[ch], compile.c, frameobject.[ch],
pythonrun.c, import.c).  The .pyc MAGIC number is changed again.
Added get_menu_text to flmodule.
üst 0023078a
...@@ -28,6 +28,7 @@ object *call_object PROTO((object *, object *)); ...@@ -28,6 +28,7 @@ object *call_object PROTO((object *, object *));
object *getglobals PROTO((void)); object *getglobals PROTO((void));
object *getlocals PROTO((void)); object *getlocals PROTO((void));
void mergelocals PROTO((void));
void printtraceback PROTO((object *)); void printtraceback PROTO((object *));
void flushline PROTO((void)); void flushline PROTO((void));
......
...@@ -36,6 +36,8 @@ typedef struct _frame { ...@@ -36,6 +36,8 @@ typedef struct _frame {
codeobject *f_code; /* code segment */ codeobject *f_code; /* code segment */
object *f_globals; /* global symbol table (dictobject) */ object *f_globals; /* global symbol table (dictobject) */
object *f_locals; /* local symbol table (dictobject) */ object *f_locals; /* local symbol table (dictobject) */
object *f_fastlocals; /* fast local variables (listobject) */
object *f_localmap; /* local variable names (dictobject) */
object **f_valuestack; /* malloc'ed array */ object **f_valuestack; /* malloc'ed array */
block *f_blockstack; /* malloc'ed array */ block *f_blockstack; /* malloc'ed array */
int f_nvalues; /* size of f_valuestack */ int f_nvalues; /* size of f_valuestack */
......
...@@ -72,10 +72,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ...@@ -72,10 +72,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define RAISE_EXCEPTION 81 #define RAISE_EXCEPTION 81
#define LOAD_LOCALS 82 #define LOAD_LOCALS 82
#define RETURN_VALUE 83 #define RETURN_VALUE 83
/*
#define REQUIRE_ARGS 84
#define REFUSE_ARGS 85
*/
#define BUILD_FUNCTION 86 #define BUILD_FUNCTION 86
#define POP_BLOCK 87 #define POP_BLOCK 87
#define END_FINALLY 88 #define END_FINALLY 88
...@@ -113,14 +110,15 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ...@@ -113,14 +110,15 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define LOAD_LOCAL 115 /* Index in name list */ #define LOAD_LOCAL 115 /* Index in name list */
#define LOAD_GLOBAL 116 /* Index in name list */ #define LOAD_GLOBAL 116 /* Index in name list */
#define LOAD_FAST 117 /* Local variable number */
#define STORE_FAST 118 /* Local variable number */
#define RESERVE_FAST 119 /* Number of local variables */
#define SETUP_LOOP 120 /* Target address (absolute) */ #define SETUP_LOOP 120 /* Target address (absolute) */
#define SETUP_EXCEPT 121 /* "" */ #define SETUP_EXCEPT 121 /* "" */
#define SETUP_FINALLY 122 /* "" */ #define SETUP_FINALLY 122 /* "" */
#define RESERVE_FAST 123 /* Number of local variables */
#define LOAD_FAST 124 /* Local variable number */
#define STORE_FAST 125 /* Local variable number */
#define DELETE_FAST 126 /* Local variable number */
#define SET_LINENO 127 /* Current line number */ #define SET_LINENO 127 /* Current line number */
/* Comparison operator codes (argument to COMPARE_OP) */ /* Comparison operator codes (argument to COMPARE_OP) */
......
...@@ -1200,6 +1200,14 @@ get_menu (g, args) ...@@ -1200,6 +1200,14 @@ get_menu (g, args)
return call_forms_Ri (fl_get_menu, g-> ob_generic, args); return call_forms_Ri (fl_get_menu, g-> ob_generic, args);
} }
static object *
get_menu_text (g, args)
genericobject *g;
object *args;
{
return call_forms_Rstr (fl_get_menu_text, g-> ob_generic, args);
}
static object * static object *
addto_menu (g, args) addto_menu (g, args)
genericobject *g; genericobject *g;
...@@ -1211,6 +1219,7 @@ addto_menu (g, args) ...@@ -1211,6 +1219,7 @@ addto_menu (g, args)
static struct methodlist menu_methods[] = { static struct methodlist menu_methods[] = {
{"set_menu", set_menu}, {"set_menu", set_menu},
{"get_menu", get_menu}, {"get_menu", get_menu},
{"get_menu_text", get_menu_text},
{"addto_menu", addto_menu}, {"addto_menu", addto_menu},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -38,6 +38,8 @@ static struct memberlist frame_memberlist[] = { ...@@ -38,6 +38,8 @@ static struct memberlist frame_memberlist[] = {
{"f_code", T_OBJECT, OFF(f_code)}, {"f_code", T_OBJECT, OFF(f_code)},
{"f_globals", T_OBJECT, OFF(f_globals)}, {"f_globals", T_OBJECT, OFF(f_globals)},
{"f_locals", T_OBJECT, OFF(f_locals)}, {"f_locals", T_OBJECT, OFF(f_locals)},
{"f_fastlocals",T_OBJECT, OFF(f_fastlocals)},
{"f_localmap", T_OBJECT, OFF(f_localmap)},
{"f_lasti", T_INT, OFF(f_lasti)}, {"f_lasti", T_INT, OFF(f_lasti)},
{"f_lineno", T_INT, OFF(f_lineno)}, {"f_lineno", T_INT, OFF(f_lineno)},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
...@@ -82,6 +84,8 @@ frame_dealloc(f) ...@@ -82,6 +84,8 @@ frame_dealloc(f)
XDECREF(f->f_code); XDECREF(f->f_code);
XDECREF(f->f_globals); XDECREF(f->f_globals);
XDECREF(f->f_locals); XDECREF(f->f_locals);
XDECREF(f->f_fastlocals);
XDECREF(f->f_localmap);
f->f_back = free_list; f->f_back = free_list;
free_list = f; free_list = f;
} }
...@@ -142,6 +146,8 @@ newframeobject(back, code, globals, locals, nvalues, nblocks) ...@@ -142,6 +146,8 @@ newframeobject(back, code, globals, locals, nvalues, nblocks)
f->f_globals = globals; f->f_globals = globals;
INCREF(locals); INCREF(locals);
f->f_locals = locals; f->f_locals = locals;
f->f_fastlocals = NULL;
f->f_localmap = NULL;
if (nvalues > f->f_nvalues || f->f_valuestack == NULL) { if (nvalues > f->f_nvalues || f->f_valuestack == NULL) {
XDEL(f->f_valuestack); XDEL(f->f_valuestack);
f->f_valuestack = NEW(object *, nvalues+1); f->f_valuestack = NEW(object *, nvalues+1);
......
...@@ -81,6 +81,7 @@ static int cmp_member PROTO((object *, object *)); ...@@ -81,6 +81,7 @@ static int cmp_member PROTO((object *, object *));
static object *cmp_outcome PROTO((int, object *, object *)); static object *cmp_outcome PROTO((int, object *, object *));
static int import_from PROTO((object *, object *, object *)); static int import_from PROTO((object *, object *, object *));
static object *build_class PROTO((object *, object *)); static object *build_class PROTO((object *, object *));
static void locals_2_fast PROTO((frameobject *, int));
/* Pointer to current frame, used to link new frames to */ /* Pointer to current frame, used to link new frames to */
...@@ -994,19 +995,51 @@ eval_code(co, globals, locals, arg) ...@@ -994,19 +995,51 @@ eval_code(co, globals, locals, arg)
break; break;
case RESERVE_FAST: case RESERVE_FAST:
if (oparg > 0) { x = GETCONST(oparg);
XDECREF(fastlocals); if (x == None)
x = newlistobject(oparg); break;
fastlocals = (listobject *) x; if (x == NULL || !is_dictobject(x)) {
fatal("bad RESERVE_FAST");
err_setstr(SystemError, "bad RESERVE_FAST");
x = NULL;
break;
} }
XDECREF(f->f_fastlocals);
XDECREF(f->f_localmap);
INCREF(x);
f->f_localmap = x;
f->f_fastlocals = x = newlistobject(
x->ob_type->tp_as_mapping->mp_length(x));
fastlocals = (listobject *) x;
break; break;
case LOAD_FAST: case LOAD_FAST:
/* NYI */ x = GETLISTITEM(fastlocals, oparg);
if (x == NULL) {
err_setstr(NameError,
"undefined local variable");
break;
}
INCREF(x);
PUSH(x);
break; break;
case STORE_FAST: case STORE_FAST:
/* NYI */ w = GETLISTITEM(fastlocals, oparg);
XDECREF(w);
w = POP();
GETLISTITEM(fastlocals, oparg) = w;
break;
case DELETE_FAST:
x = GETLISTITEM(fastlocals, oparg);
if (x == NULL) {
err_setstr(NameError,
"undefined local variable");
break;
}
DECREF(x);
GETLISTITEM(fastlocals, oparg) = NULL;
break; break;
case BUILD_TUPLE: case BUILD_TUPLE:
...@@ -1068,6 +1101,7 @@ eval_code(co, globals, locals, arg) ...@@ -1068,6 +1101,7 @@ eval_code(co, globals, locals, arg)
w = GETNAMEV(oparg); w = GETNAMEV(oparg);
v = TOP(); v = TOP();
err = import_from(f->f_locals, v, w); err = import_from(f->f_locals, v, w);
locals_2_fast(f, 0);
break; break;
case JUMP_FORWARD: case JUMP_FORWARD:
...@@ -1299,8 +1333,6 @@ eval_code(co, globals, locals, arg) ...@@ -1299,8 +1333,6 @@ eval_code(co, globals, locals, arg)
current_frame = f->f_back; current_frame = f->f_back;
DECREF(f); DECREF(f);
XDECREF(fastlocals);
return retval; return retval;
} }
...@@ -1418,10 +1450,92 @@ call_trace(p_trace, p_newtrace, f, msg, arg) ...@@ -1418,10 +1450,92 @@ call_trace(p_trace, p_newtrace, f, msg, arg)
object * object *
getlocals() getlocals()
{ {
if (current_frame == NULL) /* Merge f->f_fastlocals into f->f_locals, then return the latter */
frameobject *f;
object *locals, *fast, *map;
int i;
f = current_frame;
if (f == NULL)
return NULL; return NULL;
else locals = f->f_locals;
return current_frame->f_locals; fast = f->f_fastlocals;
map = f->f_localmap;
if (locals == NULL || fast == NULL || map == NULL)
return locals;
if (!is_dictobject(locals) || !is_listobject(fast) ||
!is_dictobject(map))
return locals;
i = getdictsize(map);
while (--i >= 0) {
object *key;
object *value;
int j;
key = getdict2key(map, i);
if (key == NULL)
continue;
value = dict2lookup(map, key);
if (value == NULL || !is_intobject(value))
continue;
j = getintvalue(value);
value = getlistitem(fast, j);
if (value == NULL) {
err_clear();
if (dict2remove(locals, key) != 0)
err_clear();
}
else {
if (dict2insert(locals, key, value) != 0)
err_clear();
}
}
return locals;
}
static void
locals_2_fast(f, clear)
frameobject *f;
int clear;
{
/* Merge f->f_locals into f->f_fastlocals */
object *locals, *fast, *map;
int i;
if (f == NULL)
return;
locals = f->f_locals;
fast = f->f_fastlocals;
map = f->f_localmap;
if (locals == NULL || fast == NULL || map == NULL)
return;
if (!is_dictobject(locals) || !is_listobject(fast) ||
!is_dictobject(map))
return;
i = getdictsize(map);
while (--i >= 0) {
object *key;
object *value;
int j;
key = getdict2key(map, i);
if (key == NULL)
continue;
value = dict2lookup(map, key);
if (value == NULL || !is_intobject(value))
continue;
j = getintvalue(value);
value = dict2lookup(locals, key);
if (value == NULL)
err_clear();
else
INCREF(value);
if (value != NULL || clear)
if (setlistitem(fast, j, value) != 0)
err_clear();
}
}
void
mergelocals()
{
locals_2_fast(current_frame, 1);
} }
object * object *
......
...@@ -27,7 +27,6 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ...@@ -27,7 +27,6 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* XXX TO DO: /* XXX TO DO:
XXX Compute maximum needed stack sizes while compiling XXX Compute maximum needed stack sizes while compiling
XXX Generate simple jump for break/return outside 'try...finally' XXX Generate simple jump for break/return outside 'try...finally'
XXX Include function name in code (and module names?)
*/ */
#include "allobjects.h" #include "allobjects.h"
...@@ -2032,7 +2031,7 @@ compile_funcdef(c, n) ...@@ -2032,7 +2031,7 @@ compile_funcdef(c, n)
node *ch; node *ch;
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */ REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
c->c_name = STR(CHILD(n, 1)); c->c_name = STR(CHILD(n, 1));
com_addoparg(c, RESERVE_FAST, 0); /* Patched up later */ com_addoparg(c, RESERVE_FAST, com_addconst(c, None)); /* Patched! */
ch = CHILD(n, 2); /* parameters: '(' [varargslist] ')' */ ch = CHILD(n, 2); /* parameters: '(' [varargslist] ')' */
ch = CHILD(ch, 1); /* ')' | varargslist */ ch = CHILD(ch, 1); /* ')' | varargslist */
if (TYPE(ch) == RPAR) if (TYPE(ch) == RPAR)
...@@ -2100,79 +2099,95 @@ compile_node(c, n) ...@@ -2100,79 +2099,95 @@ compile_node(c, n)
} }
} }
/* Optimization for local and global variables. /* Optimization for local variables in functions (and *only* functions).
XXX Need to update this text for LOAD_FAST stuff... This replaces all LOAD_NAME, STORE_NAME and DELETE_NAME
instructions that refer to local variables with LOAD_FAST etc.
Attempt to replace all LOAD_NAME instructions that refer to a local The latter instructions are much faster because they don't need to
variable with LOAD_LOCAL instructions, and all that refer to a global look up the variable name in a dictionary.
variable with LOAD_GLOBAL instructions.
To find all local variables, we check all STORE_NAME and IMPORT_FROM To find all local variables, we check all STORE_NAME and IMPORT_FROM
instructions. This yields all local variables, including arguments, instructions. This yields all local variables, including arguments,
function definitions, class definitions and import statements. function definitions, class definitions and import statements.
(We don't check DELETE_NAME instructions, since if there's no
STORE_NAME the DELETE_NAME will surely fail.)
There is one leak: 'from foo import *' introduces local variables There is one problem: 'from foo import *' introduces local variables
that we can't know while compiling. If this is the case, LOAD_GLOBAL that we can't know while compiling. If this is the case, wo don't
instructions are not generated -- LOAD_NAME is left in place for optimize at all (this rarely happens, since import is mostly used
globals, since it first checks for globals (LOAD_LOCAL is still used at the module level).
for recognized locals, since it doesn't hurt).
Note that, because of this optimization, code like the following
This optimization means that using the same name as a global and won't work:
as a local variable within the same scope is now illegal, which eval('x = 1')
is a change to the language! Also using eval() to introduce new print x
local variables won't work. But both were bad practice at best.
The optimization doesn't save much: basically, it saves one
unsuccessful dictionary lookup per global (or built-in) variable
reference. On the (slow!) Mac Plus, with 4 local variables,
this saving was measured to be about 0.18 ms. We might save more
by using a different data structure to hold local variables, like
an array indexed by variable number.
NB: this modifies the string object co->co_code! NB: this modifies the string object co->co_code!
*/ */
static void static void
optimizer(co) optimize(c)
codeobject *co; struct compiling *c;
{ {
unsigned char *next_instr, *cur_instr; unsigned char *next_instr, *cur_instr;
object *locals; object *locals;
int nlocals;
int opcode; int opcode;
int oparg; int oparg;
object *name; object *name;
int star_used; int fast_reserved;
object *error_type, *error_value;
#define NEXTOP() (*next_instr++) #define NEXTOP() (*next_instr++)
#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2]) #define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
#define GETITEM(v, i) (getlistitem((v), (i))) #define GETITEM(v, i) (getlistitem((v), (i)))
#define GETNAMEOBJ(i) (GETITEM(co->co_names, (i))) #define GETNAMEOBJ(i) (GETITEM(c->c_names, (i)))
locals = newdictobject(); locals = newdictobject();
if (locals == NULL) { if (locals == NULL) {
err_clear(); c->c_errors++;
return; /* For now, this is OK */ return;
} }
nlocals = 0;
err_get(&error_type, &error_value);
next_instr = (unsigned char *) GETSTRINGVALUE(co->co_code); next_instr = (unsigned char *) getstringvalue(c->c_code);
for (;;) { for (;;) {
opcode = NEXTOP(); opcode = NEXTOP();
if (opcode == STOP_CODE) if (opcode == STOP_CODE)
break; break;
if (HAS_ARG(opcode)) if (HAS_ARG(opcode))
oparg = NEXTARG(); oparg = NEXTARG();
if (opcode == STORE_NAME || opcode == IMPORT_FROM) { if (opcode == STORE_NAME || opcode == DELETE_NAME ||
opcode == IMPORT_FROM) {
object *v;
name = GETNAMEOBJ(oparg); name = GETNAMEOBJ(oparg);
if (dict2insert(locals, name, None) != 0) { if (dict2lookup(locals, name) != NULL)
DECREF(locals); continue;
return; /* Sorry */ err_clear();
v = newintobject(nlocals);
if (v == NULL) {
c->c_errors++;
goto err;
} }
nlocals++;
if (dict2insert(locals, name, v) != 0) {
DECREF(v);
c->c_errors++;
goto err;
}
DECREF(v);
} }
} }
star_used = (dictlookup(locals, "*") != NULL); if (nlocals == 0 || dictlookup(locals, "*") != NULL) {
next_instr = (unsigned char *) GETSTRINGVALUE(co->co_code); /* Don't optimize anything */
goto end;
}
next_instr = (unsigned char *) getstringvalue(c->c_code);
fast_reserved = 0;
for (;;) { for (;;) {
cur_instr = next_instr; cur_instr = next_instr;
opcode = NEXTOP(); opcode = NEXTOP();
...@@ -2180,18 +2195,40 @@ optimizer(co) ...@@ -2180,18 +2195,40 @@ optimizer(co)
break; break;
if (HAS_ARG(opcode)) if (HAS_ARG(opcode))
oparg = NEXTARG(); oparg = NEXTARG();
if (opcode == LOAD_NAME) { if (opcode == RESERVE_FAST) {
int i = com_addconst(c, locals);
cur_instr[1] = i & 0xff;
cur_instr[2] = (i>>8) & 0xff;
fast_reserved = 1;
continue;
}
if (!fast_reserved)
continue;
if (opcode == LOAD_NAME ||
opcode == STORE_NAME ||
opcode == DELETE_NAME) {
object *v;
int i;
name = GETNAMEOBJ(oparg); name = GETNAMEOBJ(oparg);
if (dict2lookup(locals, name) != NULL) v = dict2lookup(locals, name);
*cur_instr = LOAD_LOCAL; if (v == NULL) {
else {
err_clear(); err_clear();
if (!star_used) continue;
*cur_instr = LOAD_GLOBAL;
} }
i = getintvalue(v);
switch (opcode) {
case LOAD_NAME: cur_instr[0] = LOAD_FAST; break;
case STORE_NAME: cur_instr[0] = STORE_FAST; break;
case DELETE_NAME: cur_instr[0] = DELETE_FAST; break;
}
cur_instr[1] = i & 0xff;
cur_instr[2] = (i>>8) & 0xff;
} }
} }
end:
err_setval(error_type, error_value);
err:
DECREF(locals); DECREF(locals);
} }
...@@ -2206,6 +2243,8 @@ compile(n, filename) ...@@ -2206,6 +2243,8 @@ compile(n, filename)
return NULL; return NULL;
compile_node(&sc, n); compile_node(&sc, n);
com_done(&sc); com_done(&sc);
if (TYPE(n) == funcdef && sc.c_errors == 0)
optimize(&sc);
co = NULL; co = NULL;
if (sc.c_errors == 0) { if (sc.c_errors == 0) {
object *v, *w; object *v, *w;
...@@ -2218,7 +2257,5 @@ compile(n, filename) ...@@ -2218,7 +2257,5 @@ compile(n, filename)
XDECREF(w); XDECREF(w);
} }
com_free(&sc); com_free(&sc);
if (co != NULL && filename[0] != '<')
optimizer(co);
return co; return co;
} }
...@@ -54,7 +54,7 @@ extern char *argv0; ...@@ -54,7 +54,7 @@ extern char *argv0;
/* Magic word to reject pre-0.9.9 .pyc files */ /* Magic word to reject pre-0.9.9 .pyc files */
#define MAGIC 0x99BE2AL #define MAGIC 0x99BE3AL
static object *modules; static object *modules;
......
...@@ -304,16 +304,23 @@ run_node(n, filename, globals, locals) ...@@ -304,16 +304,23 @@ run_node(n, filename, globals, locals)
char *filename; char *filename;
/*dict*/object *globals, *locals; /*dict*/object *globals, *locals;
{ {
object *res;
int needmerge = 0;
if (globals == NULL) { if (globals == NULL) {
globals = getglobals(); globals = getglobals();
if (locals == NULL) if (locals == NULL) {
locals = getlocals(); locals = getlocals();
needmerge = 1;
}
} }
else { else {
if (locals == NULL) if (locals == NULL)
locals = globals; locals = globals;
} }
return eval_node(n, filename, globals, locals); res = eval_node(n, filename, globals, locals);
if (needmerge)
mergelocals();
return res;
} }
object * object *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment