Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 59259) +++ Python/ceval.c (working copy) @@ -492,12 +492,27 @@ PyObject * PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals) { - return PyEval_EvalCodeEx(co, - globals, locals, + PyObject *fastglobals; + PyObject *res; + + if (globals == NULL) { + PyErr_SetString(PyExc_SystemError, + "PyEval_EvalCode: NULL globals"); + return NULL; + } + + fastglobals = PyFastGlobals_New(globals, co->co_names); + if (fastglobals == NULL) + return NULL; + + res = PyEval_EvalCodeEx(co, + fastglobals, locals, (PyObject **)NULL, 0, (PyObject **)NULL, 0, (PyObject **)NULL, 0, NULL); + Py_DECREF(fastglobals); + return res; } @@ -762,7 +777,8 @@ f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ #ifdef LLTRACE - lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; + lltrace = PyDict_GetItemString( + PyFrame_GET_GLOBALS(f), "__lltrace__") != NULL; #endif #if defined(Py_DEBUG) || defined(LLTRACE) filename = PyString_AsString(co->co_filename); @@ -1814,16 +1830,17 @@ break; case STORE_GLOBAL: - w = GETITEM(names, oparg); v = POP(); - err = PyDict_SetItem(f->f_globals, w, v); - Py_DECREF(v); + /* steals a reference */ + err = PyFastGlobals_SetItem( + f->f_fastglobals, oparg, v); if (err == 0) continue; break; case DELETE_GLOBAL: w = GETITEM(names, oparg); - if ((err = PyDict_DelItem(f->f_globals, w)) != 0) + if ((err = PyDict_DelItem( + PyFrame_GET_GLOBALS(f), w)) != 0) format_exc_check_arg( PyExc_NameError, GLOBAL_NAME_ERROR_MSG, w); break; @@ -1850,15 +1867,13 @@ } } if (x == NULL) { - x = PyDict_GetItem(f->f_globals, w); + PyFastGlobals_GET_ITEM_INPLACE( + x, f->f_fastglobals, oparg); if (x == NULL) { - x = PyDict_GetItem(f->f_builtins, w); - if (x == NULL) { - format_exc_check_arg( - PyExc_NameError, - NAME_ERROR_MSG, w); - break; - } + format_exc_check_arg( + PyExc_NameError, + NAME_ERROR_MSG, w); + break; } Py_INCREF(x); } @@ -1866,53 +1881,14 @@ continue; case LOAD_GLOBAL: - w = GETITEM(names, oparg); - if (PyString_CheckExact(w)) { - /* Inline the PyDict_GetItem() calls. - WARNING: this is an extreme speed hack. - Do not try this at home. */ - long hash = ((PyStringObject *)w)->ob_shash; - if (hash != -1) { - PyDictObject *d; - PyDictEntry *e; - d = (PyDictObject *)(f->f_globals); - e = d->ma_lookup(d, w, hash); - if (e == NULL) { - x = NULL; - break; - } - x = e->me_value; - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - continue; - } - d = (PyDictObject *)(f->f_builtins); - e = d->ma_lookup(d, w, hash); - if (e == NULL) { - x = NULL; - break; - } - x = e->me_value; - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - continue; - } - goto load_global_error; - } - } - /* This is the un-inlined version of the code above */ - x = PyDict_GetItem(f->f_globals, w); + PyFastGlobals_GET_ITEM_INPLACE( + x, f->f_fastglobals, oparg); if (x == NULL) { - x = PyDict_GetItem(f->f_builtins, w); - if (x == NULL) { - load_global_error: - format_exc_check_arg( - PyExc_NameError, - GLOBAL_NAME_ERROR_MSG, w); - break; - } + w = GETITEM(names, oparg); + format_exc_check_arg( + PyExc_NameError, + GLOBAL_NAME_ERROR_MSG, w); + break; } Py_INCREF(x); PUSH(x); @@ -2047,7 +2023,8 @@ case IMPORT_NAME: w = GETITEM(names, oparg); - x = PyDict_GetItemString(f->f_builtins, "__import__"); + x = PyDict_GetItemString(PyFrame_GET_BUILTINS(f), + "__import__"); if (x == NULL) { PyErr_SetString(PyExc_ImportError, "__import__ not found"); @@ -2058,7 +2035,7 @@ if (PyInt_AsLong(u) != -1 || PyErr_Occurred()) w = PyTuple_Pack(5, w, - f->f_globals, + PyFrame_GET_GLOBALS(f), f->f_locals == NULL ? Py_None : f->f_locals, v, @@ -2066,7 +2043,7 @@ else w = PyTuple_Pack(4, w, - f->f_globals, + PyFrame_GET_GLOBALS(f), f->f_locals == NULL ? Py_None : f->f_locals, v); @@ -2353,7 +2330,7 @@ case MAKE_FUNCTION: v = POP(); /* code object */ - x = PyFunction_New(v, f->f_globals); + x = PyFunction_New(v, PyFrame_GET_GLOBALS(f)); Py_DECREF(v); /* XXX Maybe this should be a separate opcode? */ if (x != NULL && oparg > 0) { @@ -2376,7 +2353,7 @@ case MAKE_CLOSURE: { v = POP(); /* code object */ - x = PyFunction_New(v, f->f_globals); + x = PyFunction_New(v, PyFrame_GET_GLOBALS(f)); Py_DECREF(v); if (x != NULL) { v = POP(); @@ -2637,7 +2614,7 @@ the test in the if statements in Misc/gdbinit (pystack and pystackv). */ PyObject * -PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, +PyEval_EvalCodeEx(PyCodeObject *co, PyObject *fastglobals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *closure) { @@ -2647,15 +2624,14 @@ PyThreadState *tstate = PyThreadState_GET(); PyObject *x, *u; - if (globals == NULL) { + if (fastglobals == NULL) { PyErr_SetString(PyExc_SystemError, - "PyEval_EvalCodeEx: NULL globals"); + "PyEval_EvalCodeEx: NULL fastglobals"); return NULL; } assert(tstate != NULL); - assert(globals != NULL); - f = PyFrame_New(tstate, co, globals, locals); + f = PyFrame_New(tstate, co, fastglobals, locals); if (f == NULL) return NULL; @@ -3346,7 +3322,7 @@ if (current_frame == NULL) return PyThreadState_GET()->interp->builtins; else - return current_frame->f_builtins; + return PyFrame_GET_BUILTINS(current_frame); } PyObject * @@ -3366,7 +3342,7 @@ if (current_frame == NULL) return NULL; else - return current_frame->f_globals; + return PyFrame_GET_GLOBALS(current_frame); } PyFrameObject * @@ -3641,7 +3617,7 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); - PyObject *globals = PyFunction_GET_GLOBALS(func); + PyObject *fastglobals = PyFunction_GET_FASTGLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject **d = NULL; int nd = 0; @@ -3657,13 +3633,13 @@ int i; PCALL(PCALL_FASTER_FUNCTION); - assert(globals != NULL); + assert(fastglobals != NULL); /* XXX Perhaps we should create a specialized PyFrame_New() that doesn't take locals, but does take builtins without sanity checking them. */ assert(tstate != NULL); - f = PyFrame_New(tstate, co, globals, NULL); + f = PyFrame_New(tstate, co, fastglobals, NULL); if (f == NULL) return NULL; @@ -3684,7 +3660,7 @@ d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_Size(argdefs); } - return PyEval_EvalCodeEx(co, globals, + return PyEval_EvalCodeEx(co, fastglobals, (PyObject *)NULL, (*pp_stack)-n, na, (*pp_stack)-2*nk, nk, d, nd, PyFunction_GET_CLOSURE(func)); @@ -4233,7 +4209,8 @@ return -1; } if (PyDict_GetItemString(globals, "__builtins__") == NULL) - PyDict_SetItemString(globals, "__builtins__", f->f_builtins); + PyDict_SetItemString(globals, "__builtins__", + PyFrame_GET_BUILTINS(f)); if (PyCode_Check(prog)) { if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) { PyErr_SetString(PyExc_TypeError, Index: PCbuild/pythoncore.vcproj =================================================================== --- PCbuild/pythoncore.vcproj (revision 59259) +++ PCbuild/pythoncore.vcproj (working copy) @@ -785,6 +785,9 @@ RelativePath="..\Objects\weakrefobject.c"> + + ma_entryversion) +PyAPI_FUNC(PyDictEntry *) PyDict_GetEntry(PyObject *op, PyObject *key); + /* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */ PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other); Index: Include/Python.h =================================================================== --- Include/Python.h (revision 59259) +++ Include/Python.h (working copy) @@ -109,6 +109,7 @@ #include "genobject.h" #include "descrobject.h" #include "weakrefobject.h" +#include "fastglobals.h" #include "codecs.h" #include "pyerrors.h" Index: Include/funcobject.h =================================================================== --- Include/funcobject.h (revision 59259) +++ Include/funcobject.h (working copy) @@ -21,7 +21,7 @@ typedef struct { PyObject_HEAD PyObject *func_code; /* A code object */ - PyObject *func_globals; /* A dictionary (other mappings won't do) */ + PyObject *func_fastglobals; /* A PyFastGlobalsObject */ PyObject *func_defaults; /* NULL or a tuple */ PyObject *func_closure; /* NULL or a tuple of cell objects */ PyObject *func_doc; /* The __doc__ attribute, can be anything */ @@ -54,8 +54,10 @@ done, so use with care. */ #define PyFunction_GET_CODE(func) \ (((PyFunctionObject *)func) -> func_code) +#define PyFunction_GET_FASTGLOBALS(func) \ + (((PyFunctionObject *)func) -> func_fastglobals) #define PyFunction_GET_GLOBALS(func) \ - (((PyFunctionObject *)func) -> func_globals) + (PyFastGlobals_GLOBALS(PyFunction_GET_FASTGLOBALS(func))) #define PyFunction_GET_MODULE(func) \ (((PyFunctionObject *)func) -> func_module) #define PyFunction_GET_DEFAULTS(func) \ Index: Include/fastglobals.h =================================================================== --- Include/fastglobals.h (revision 0) +++ Include/fastglobals.h (revision 0) @@ -0,0 +1,109 @@ + +/* Fast globals dictionary adapter interface */ + +#ifndef Py_FASTGLOBALS_H +#define Py_FASTGLOBALS_H +#ifdef __cplusplus +extern "C" { +#endif + +/* +PyFastGlobalsObject + +These objects hold pointers directly into globals and builtins dict tables +for very fast access. Every function has one of these adapters, which is +passed to the function's frame when it's called. It enables ceval.c to get +globals and builtins almost as fast as it gets locals. + +Changes to globals and builtins DO NOT have to be reported to an object +of this type in any way. PyDictObject keeps a "version" that is +incremented whenever at least one entry becomes invalid (not safe to +keep a pointer to). PyFastGlobalObject uses this to make sure its entry +pointers are valid before dereferencing them. The rest of the codebase +can happily believe that nothing magical is going on and interact with +globals and builtins dicts as they always have. + +The dictionary versioning mechanism might be used to accelerate dictionary +access in other contexts. It could be appropriate any time get/set (non- +inserting) with a static set of keys dominates access. +*/ + +typedef struct { + PyObject_HEAD + PyObject *names; /* Tuple of names from co->co_code */ + Py_ssize_t num_entries; /* == len(names) */ + + PyObject *globals; /* PyDictObject only */ + PyObject *builtins; /* PyDictObject only */ + + PyDictEntry **entries; /* PyDictEntry*'s into ma_table */ + int *isbuiltin; /* For each pointer, whether it points + into the builtins dict (boolean) */ + /* We don't keep track of two sets of entries (one for globals and + one for builtins) because we want to make the common case, a GET, + as fast as possible. (See PyFastGlobals_GET_ITEM_INPLACE.) */ + PY_LONG_LONG entryversion; /* == globals + builtins after update */ + PyDictEntry *builtins_entry; /* entry for __builtins__ */ + PyObject *old_builtins; /* used to detect __builtins__ changes */ +} PyFastGlobalsObject; + +PyAPI_DATA(PyTypeObject) PyFastGlobals_Type; + +#define PyFastGlobals_Check(op) (Py_Type(op) == &PyFastGlobals_Type) + +#define PyFastGlobals_NAMES(op) \ + (((PyFastGlobalsObject *)(op))->names) + +#define PyFastGlobals_SIZE(op) \ + (((PyFastGlobalsObject *)(op))->num_entries) + +#define PyFastGlobals_GLOBALS(op) \ + (((PyFastGlobalsObject *)(op))->globals) + +#define PyFastGlobals_BUILTINS(op) \ + (((PyFastGlobalsObject *)(op))->builtins) + +#define PyFastGlobals_ENTRY(op, index) \ + (((PyFastGlobalsObject *)(op))->entries[index]) + +#define PyFastGlobals_IS_BUILTIN(op, index) \ + (((PyFastGlobalsObject *)(op))->isbuiltin[index]) + +#define PyFastGlobals_ENTRYVERSION(op) \ + (((PyFastGlobalsObject *)(op))->entryversion) + +#define PyFastGlobals_IS_UPDATED(op)\ + (PyFastGlobals_ENTRYVERSION(op) ==\ + PyDict_ENTRYVERSION(PyFastGlobals_GLOBALS(op)) +\ + PyDict_ENTRYVERSION(PyFastGlobals_BUILTINS(op))) + +#define PyFastGlobals_BUILTINS_CHANGED(op) \ + (((PyFastGlobalsObject *)(op))->builtins_entry == NULL ||\ + ((PyFastGlobalsObject *)(op))->builtins_entry->me_value !=\ + ((PyFastGlobalsObject *)(op))->old_builtins) + +PyAPI_FUNC(PyObject *) PyFastGlobals_New(PyObject *globals, PyObject *names); +/* The following do no run-time error checking except asserts */ +PyAPI_FUNC(int) PyFastGlobals_Update(PyObject *op); +PyAPI_FUNC(int) PyFastGlobals_CheckBuiltins(PyObject *op); +PyAPI_FUNC(PyObject *) PyFastGlobals_GetItem(PyObject *op, Py_ssize_t index); +PyAPI_FUNC(int) PyFastGlobals_SetItem( + PyObject *op, Py_ssize_t index, PyObject *item); + +/* The following returns NULL on errors and suppresses exceptions just +like PyDict_GetItem - it's intended as a replacement */ +#define PyFastGlobals_GET_ITEM_INPLACE(x, op, index) do {\ + if (!PyFastGlobals_IS_UPDATED(op) &&\ + PyFastGlobals_Update(op) < 0) {\ + PyErr_Clear();\ + (x) = NULL;\ + }\ + else\ + (x) = PyFastGlobals_ENTRY(op, index) == NULL ? NULL :\ + PyFastGlobals_ENTRY(op, index)->me_value;\ +} while (0) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FASTGLOBALS_H */ Index: Include/pythonrun.h =================================================================== --- Include/pythonrun.h (revision 59259) +++ Include/pythonrun.h (working copy) @@ -137,6 +137,7 @@ PyAPI_FUNC(void) PyInt_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); PyAPI_FUNC(void) PyOS_FiniInterrupts(void); +PyAPI_FUNC(void) PyFastGlobals_Fini(void); /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); Index: Include/frameobject.h =================================================================== --- Include/frameobject.h (revision 59259) +++ Include/frameobject.h (working copy) @@ -17,8 +17,8 @@ PyObject_VAR_HEAD struct _frame *f_back; /* previous frame, or NULL */ PyCodeObject *f_code; /* code segment */ - PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ - PyObject *f_globals; /* global symbol table (PyDictObject) */ + PyObject *f_fastglobals; /* globals and builtins symbol tables + (PyFastGlobalsObject) */ PyObject *f_locals; /* local symbol table (any mapping) */ PyObject **f_valuestack; /* points after the last local */ /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. @@ -52,8 +52,12 @@ PyAPI_DATA(PyTypeObject) PyFrame_Type; #define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type) -#define PyFrame_IsRestricted(f) \ - ((f)->f_builtins != (f)->f_tstate->interp->builtins) +#define PyFrame_GET_GLOBALS(op) \ + (((PyFastGlobalsObject *)(op)->f_fastglobals)->globals) +#define PyFrame_GET_BUILTINS(op) \ + (((PyFastGlobalsObject *)(op)->f_fastglobals)->builtins) +#define PyFrame_IsRestricted(op) \ + (PyFrame_GET_BUILTINS(op) != (op)->f_tstate->interp->builtins) PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, PyObject *, PyObject *); Index: Include/eval.h =================================================================== --- Include/eval.h (revision 59259) +++ Include/eval.h (working copy) @@ -10,7 +10,7 @@ PyAPI_FUNC(PyObject *) PyEval_EvalCode(PyCodeObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyCodeObject *co, - PyObject *globals, + PyObject *fastglobals, PyObject *locals, PyObject **args, int argc, PyObject **kwds, int kwdc, Index: fastglobals_test.py =================================================================== --- fastglobals_test.py (revision 0) +++ fastglobals_test.py (revision 0) @@ -0,0 +1,416 @@ +#!/usr/bin/python + +import timeit, random, time + + +MULTIPLIER = 1 + + +def test_local_get(): + x = 0 + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + + +def test_local_set(): + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + + +x = 0 +def test_global_get(): + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x; x + + +def test_global_set(): + global x + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0; x = 0 + + +def test_builtin_get(): + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + len; len; len; len; len; len; len; len; len; len; + + +lists = [[x for x in xrange(y)] for y in xrange(1, 1000)] +def test_listcomp(): + [set(sub) for sub in lists if len(sub) <= 5] + + +def test_func_call(): + def inner_function(): + pass + for i in xrange(1000): + inner_function(); + + +dict_range = ['%i' % i for i in range(1000)] +def test_dict_insert_del(): + d = {} + for i in dict_range: + d[i] = i + for i in dict_range: + del d[i] + +test_dict = dict((i, i) for i in dict_range) +def test_dict_set(): + d = test_dict + for i in dict_range: + d[i] = i + +def test_dict_get(): + d = test_dict + for i in dict_range: + d[i] + + +cfg = { + 'S': ['NP VP'], + 'NP': ['CN', 'CN PP'], + 'VP': ['CV', 'CV PP'], + 'PP': ['P CN'], + 'CN': ['A N'], + 'CV': ['V', 'V NP'], + 'A': ['a', 'the'], + 'N': ['knight', 'king', 'shrubbery', 'castle', 'python', 'trojan rabbit'], + 'V': ['eats', 'likes', 'sees', 'decapitates', 'smells', 'throws'], + 'P': ['with', 'without', 'behind', 'above', 'below', 'on top of', 'under'], +} + +cfg = dict( + (rule, [string.split() for string in stringlist]) + for rule, stringlist in cfg.items()) + +def produce(rule): + try: + rulelist = random.choice(cfg[rule]) + except KeyError: + return [rule] # a terminal + return sum([produce(rule) for rule in rulelist], []) + +def produce2(rule): + d = [] + d.append(rule) + result = [] + while len(d) > 0: + rule = d.pop() + try: + rulelist = random.choice(cfg[rule]) + except KeyError: + result.append(rule) + else: + d += rulelist + result.reverse() + return result + +def matches_rule(rule, wordlist): + '''A very naive top-down parser.''' + try: + rulelists = cfg[rule] + except KeyError: + return False # a terminal + if wordlist in rulelists: + return True # word list matches the rule exactly + for rulelist in rulelists: + if matches_rulelist(rulelist, wordlist): + return True + return False + +def matches_rulelist(rulelist, wordlist): + if len(rulelist) == 0: + if len(wordlist) == 0: + return True + return False + for i in xrange(1, len(wordlist) + 1): + if matches_rule(rulelist[0], wordlist[:i]): + if matches_rulelist(rulelist[1:], wordlist[i:]): + return True + return False + +def test_grammar(): + produce('S') + +def test_grammar2(): + produce2('S') + +cfg_strings = [ + 'the trojan rabbit without the castle eats the knight on top of a python', + 'a knight under the python decapitates on top of the trojan rabbit', + 'the shrubbery on top of a castle smells without the king', + 'a shrubbery decapitates', + 'the python below a castle throws the shrubbery', + 'the python decapitates below a knight', + 'a trojan rabbit under a shrubbery sees behind a python', + 'a castle smells', + 'the castle with a knight eats the shrubbery on top of a knight below the castle', + 'a python behind a king likes the knight on top of a trojan rabbit', + 'the shrubbery with a shrubbery sees', + 'a castle behind a python likes a python on top of a castle without a trojan rabbit', + 'a shrubbery below the trojan rabbit decapitates the king without a castle above the castle', + 'a shrubbery above the knight sees the castle with the python', + 'a knight eats the shrubbery behind a shrubbery', + 'a trojan rabbit decapitates the python on top of a knight', + 'a castle behind the king smells', + 'a castle throws without a trojan rabbit', + 'a shrubbery under a king throws with a trojan rabbit', + 'a knight decapitates', + 'a knight eats below the shrubbery', + 'a castle throws above a castle', + 'the trojan rabbit with the castle likes', + 'a trojan rabbit above a shrubbery smells the king', + 'the castle below a trojan rabbit throws', + 'a castle smells a trojan rabbit without the shrubbery', + 'the trojan rabbit above a castle smells a king behind the trojan rabbit', + 'a trojan rabbit sees', + 'the castle eats', + 'the castle throws', + 'a python throws a castle with a castle below a trojan rabbit', + 'a king eats a knight without the python', + 'a python below a shrubbery likes a castle with the shrubbery', + 'the trojan rabbit eats the shrubbery behind the king below the python', + 'the python likes', + 'a knight behind the king decapitates the knight on top of the python', + 'the castle decapitates the knight under the trojan rabbit', + 'a shrubbery below a shrubbery sees', + 'a shrubbery on top of a trojan rabbit likes', + 'the python behind a trojan rabbit decapitates behind a castle', + 'a castle under a castle smells the trojan rabbit below a trojan rabbit with the king', + 'the knight likes', + 'the king without a shrubbery eats with a python', + 'a king smells', + 'the python throws a castle with a shrubbery on top of the python', + 'the castle under a knight likes a trojan rabbit under the king', + 'a trojan rabbit decapitates on top of a shrubbery', + 'the python decapitates the shrubbery behind the shrubbery', + 'the shrubbery likes the knight with the castle', + 'the shrubbery likes the python without the king' +] + +cfg_wordlists = [string.split() for string in cfg_strings] + +def test_grammar3(): + for wordlist in cfg_wordlists: + assert matches_rule('S', wordlist) + + +def qsort(lst): + if len(lst) <= 1: + return lst + else: + pivot = lst[0] + lesser = qsort([l for l in lst[1:] if l < pivot]) + greater = qsort([l for l in lst[1:] if l >= pivot]) + return lesser + [pivot] + greater + +def test_qsort(): + lst = range(500) + random.shuffle(lst) + qsort(lst) + + +def fib(n): + if (n < 2): + return 1 + return fib(n - 1) + fib(n - 2) + +def test_fib(): + fib(10) + + +if __name__ == '__main__': + print 'Recursion:', timeit.Timer('test_fib()', + 'from __main__ import test_fib').timeit(number=20000 * MULTIPLIER) + + print 'Grammar:', timeit.Timer('test_grammar()', + 'from __main__ import test_grammar').timeit(number=20000 * MULTIPLIER) + + print 'Grammar (stack):', timeit.Timer('test_grammar2()', + 'from __main__ import test_grammar2').timeit(number=20000 * MULTIPLIER) + + print 'Grammar (parse):', timeit.Timer('test_grammar3()', + 'from __main__ import test_grammar3').timeit(number=1 * MULTIPLIER) + + print 'Quicksort:', timeit.Timer('test_qsort()', + 'from __main__ import test_qsort').timeit(number=500 * MULTIPLIER) + + print 'Dict insert/del:', timeit.Timer('test_dict_insert_del()', + 'from __main__ import test_dict_insert_del').timeit(number=10000 * MULTIPLIER) + + print 'Dict get:', timeit.Timer('test_dict_get()', + 'from __main__ import test_dict_get').timeit(number=10000 * MULTIPLIER) + + print 'Dict set:', timeit.Timer('test_dict_set()', + 'from __main__ import test_dict_set').timeit(number=10000 * MULTIPLIER) + + print 'Local get:', timeit.Timer('test_local_get()', + 'from __main__ import test_local_get').timeit(number=100000 * MULTIPLIER) + + print 'Local set:', timeit.Timer('test_local_set()', + 'from __main__ import test_local_set').timeit(number=100000 * MULTIPLIER) + + print 'Global get:', timeit.Timer('test_global_get()', + 'from __main__ import test_global_get').timeit(number=100000 * MULTIPLIER) + + print 'Global set:', timeit.Timer('test_global_set()', + 'from __main__ import test_global_set').timeit(number=100000 * MULTIPLIER) + + print 'Builtin get:', timeit.Timer('test_builtin_get()', + 'from __main__ import test_builtin_get').timeit(number=100000 * MULTIPLIER) + + print 'Function call:', timeit.Timer('test_func_call()', + 'from __main__ import test_func_call').timeit(number=10000 * MULTIPLIER) + + print 'List comp:', timeit.Timer('test_listcomp()', + 'from __main__ import test_listcomp').timeit(number=10000 * MULTIPLIER) Property changes on: fastglobals_test.py ___________________________________________________________________ Name: svn:executable + * Index: Objects/fastglobals.c =================================================================== --- Objects/fastglobals.c (revision 0) +++ Objects/fastglobals.c (revision 0) @@ -0,0 +1,376 @@ + +/* Fast globals dictionary adapter implementation */ + +#include "Python.h" +#include "structmember.h" + +#define PyFastGlobals_VERSION_SET(op) \ + (PyFastGlobals_ENTRYVERSION(op) =\ + PyDict_ENTRYVERSION(PyFastGlobals_GLOBALS(op)) +\ + PyDict_ENTRYVERSION(PyFastGlobals_BUILTINS(op))) + +static PyObject *__builtins__ = NULL; + +/* Request entry pointers from globals and builtins for every name if one +of them has invalidated any entries. Returns 0 on success, -1 on failure +with an exception set. */ +static int +fg_update(PyFastGlobalsObject *fg) +{ + Py_ssize_t i; + PyObject *key; + PyDictEntry *entry; + /* This loop SHOULD only get executed once, though it may loop if a + key comparison alters the underlying dictionary. */ + while (!PyFastGlobals_IS_UPDATED(fg)) { + PyFastGlobals_VERSION_SET(fg); + /* Store the entries in the same order as the names */ + for (i = 0; i < fg->num_entries; i++) { + key = PyTuple_GET_ITEM(fg->names, i); + entry = PyDict_GetEntry(fg->globals, key); + if (entry != NULL) { + fg->entries[i] = entry; + fg->isbuiltin[i] = 0; + } + else { + if (PyErr_Occurred()) return -1; + entry = PyDict_GetEntry(fg->builtins, key); + if (entry != NULL) { + fg->entries[i] = entry; + fg->isbuiltin[i] = 1; + } + else { + if (PyErr_Occurred()) return -1; + fg->entries[i] = NULL; + fg->isbuiltin[i] = 0; + } + } + } + } + /* XXX A clever adversary could prevent this from terminating */ + + /* For convenience later */ + fg->builtins_entry = fg->entries[fg->num_entries - 1]; + return 0; +} + +/* Public wrapper for fg_update with minimal error checking. */ +int +PyFastGlobals_Update(PyObject *op) +{ + assert(op != NULL && PyFastGlobals_Check(op)); + return fg_update((PyFastGlobalsObject *)op); +} + +/* This finds the __builtins__ of a global dict and tracks its value. +It also detects when __builtins__ changes. If __builtins__ does not exist +in globals, it creates one consisting of just {'None': None}. Unlike +the the previous code in PyFrame_New, this actually inserts __builtins__ +into the globals dictionary. Returns 0 on success, -1 on failure with an +exception set. */ +static int +fg_check_builtins(PyFastGlobalsObject *fg) +{ + PyObject *builtins, *old_builtins; + /* This loop SHOULD only get executed once, though it may loop if a + key comparison alters the underlying dictionary. */ + while (PyFastGlobals_BUILTINS_CHANGED(fg)) { + builtins = PyDict_GetItem(fg->globals, __builtins__); + if (builtins != NULL) { + /* Get the actual dict if it's a module */ + if (PyModule_Check(builtins)) { + builtins = PyModule_GetDict(builtins); + assert(!builtins || PyDict_Check(builtins)); + } + else if (!PyDict_Check(builtins)) + builtins = NULL; + } + + if (builtins != NULL) + Py_INCREF(builtins); + else { + /* No builtins! Create a minimal one */ + builtins = PyDict_New(); + if (builtins == NULL) + return -1; + if (PyDict_SetItemString( + builtins, "None", Py_None) < 0) { + Py_DECREF(builtins); + return -1; + } + /* Make it available */ + if (PyDict_SetItem(fg->globals, + __builtins__, builtins) < 0) { + Py_DECREF(builtins); + return -1; + } + } + + old_builtins = fg->builtins; + fg->builtins = builtins; + Py_XDECREF(old_builtins); + + if (fg_update(fg) < 0) /* Get fresh entries */ + return -1; + + if (fg->builtins_entry != NULL) + fg->old_builtins = fg->builtins_entry->me_value; + else + fg->old_builtins = NULL; + } + return 0; +} + +/* Public wrapper for fg_check_builtins with minimal error checking. */ +int +PyFastGlobals_CheckBuiltins(PyObject *op) +{ + PyFastGlobalsObject *fg; + assert(op != NULL && PyFastGlobals_Check(op)); + fg = (PyFastGlobalsObject *)op; + + /* Make sure fg->builtins_entry is updated first */ + if (fg_update(fg) < 0) + return -1; + + return fg_check_builtins(fg); +} + +PyObject * +PyFastGlobals_New(PyObject *globals, PyObject *names) +{ + PyObject *newnames; + Py_ssize_t i, num_entries; + PyDictEntry **entries; + int *isbuiltin; + PyFastGlobalsObject *fg; + + if (globals == NULL || !PyDict_Check(globals)) { + PyErr_BadInternalCall(); + return NULL; + } + if (names == NULL || !PyTuple_Check(names)) { + PyErr_BadInternalCall(); + return NULL; + } + + if (__builtins__ == NULL) { + __builtins__ = PyString_InternFromString("__builtins__"); + if (__builtins__ == NULL) + return NULL; /* shouldn't happen */ + } + + /* Append '__builtins__' to names */ + num_entries = PyTuple_GET_SIZE(names); + newnames = PyTuple_New(num_entries + 1); + if (newnames == NULL) + return NULL; + for (i = 0; i < num_entries; i++) { + PyObject *name = PyTuple_GET_ITEM(names, i); + Py_INCREF(name); + PyTuple_SET_ITEM(newnames, i, name); + } + Py_INCREF(__builtins__); + PyTuple_SET_ITEM(newnames, i, __builtins__); + num_entries++; + + entries = PyMem_NEW(PyDictEntry *, num_entries); + if (entries == NULL) { + PyErr_NoMemory(); + Py_DECREF(newnames); + return NULL; + } + + isbuiltin = PyMem_NEW(int, num_entries); + if (isbuiltin == NULL) { + PyErr_NoMemory(); + Py_DECREF(newnames); + PyMem_FREE(isbuiltin); + return NULL; + } + + fg = PyObject_GC_New(PyFastGlobalsObject, &PyFastGlobals_Type); + if (fg == NULL) { + Py_DECREF(newnames); + PyMem_FREE(entries); + PyMem_FREE(isbuiltin); + return NULL; + } + + fg->names = newnames; + fg->num_entries = num_entries; + /* The following will be initialized in fg_check_builtins */ + fg->entries = entries; + fg->isbuiltin = isbuiltin; + + Py_INCREF(globals); + fg->globals = globals; + /* Force fg_update to update entries */ + fg->entryversion = PyDict_ENTRYVERSION(fg->globals) - 1; + + fg->builtins = NULL; + /* Force fg_check_builtins to set up builtins */ + fg->builtins_entry = NULL; + if (fg_check_builtins(fg) == -1) { + _PyObject_GC_TRACK(fg); + Py_DECREF(fg); + return NULL; + } + + _PyObject_GC_TRACK(fg); + return (PyObject *)fg; +} + +/* Returns a borrowed reference to the item at index. Returns NULL on +failure or missing key, with an exception set. */ +PyObject * +PyFastGlobals_GetItem(PyObject *op, register Py_ssize_t index) +{ + register PyFastGlobalsObject *fg; + register PyDictEntry *entry; + + assert(op != NULL && PyFastGlobals_Check(op)); + fg = (PyFastGlobalsObject *)op; + assert(index >= 0 && index < fg->num_entries); + + /* Version check here almost always saves a function call */ + if (!PyFastGlobals_IS_UPDATED(fg)) + if (fg_update(fg) < 0) { + return NULL; + } + + entry = fg->entries[index]; + if (entry == NULL) { + PyErr_Format(PyExc_KeyError, + "fastglobals: no value for index %d", index); + return NULL; + } + assert(entry->me_value != NULL); + return entry->me_value; +} + +/* Sets the item at index to item. This "steals" a reference to item and +discards a reference to the item already at index, if any. Returns 0 on +success, -1 on failure with an exception. (This only happens when the item +must be inserted but PyDict_SetItem fails.) */ +int +PyFastGlobals_SetItem(PyObject *op, Py_ssize_t index, PyObject *item) +{ + PyObject *old_value; + PyDictEntry *entry; + PyFastGlobalsObject *fg; + + assert(item != NULL); + assert(op != NULL && PyFastGlobals_Check(op)); + fg = (PyFastGlobalsObject *)op; + assert(index >= 0 && index < fg->num_entries); + + /* Version check here almost always saves a function call */ + if (!PyFastGlobals_IS_UPDATED(fg)) + if (fg_update(fg) < 0) { + return -1; + } + + entry = fg->entries[index]; + if (entry == NULL || fg->isbuiltin[index]) { + /* Not already there or a builtin - insert it into globals */ + PyObject *key = PyTuple_GET_ITEM(fg->names, index); + int ret = PyDict_SetItem(fg->globals, key, item); + /* Can't guarantee that only the missing entry will be changed, + so we wait for the next get/set to update. */ + Py_DECREF(item); /* steal a reference */ + return ret; + } + + old_value = entry->me_value; + entry->me_value = item; /* steal a reference */ + assert(old_value != NULL); /* dict enforces this */ + Py_DECREF(old_value); + return 0; +} + +static int +fg_clear(PyFastGlobalsObject *fg) +{ + Py_CLEAR(fg->globals); + Py_CLEAR(fg->builtins); + Py_CLEAR(fg->names); + if (fg->entries != NULL) { + PyMem_FREE(fg->entries); + fg->entries = NULL; + } + if (fg->isbuiltin != NULL) { + PyMem_FREE(fg->isbuiltin); + fg->isbuiltin = NULL; + } + return 0; +} + +static void +fg_dealloc(PyFastGlobalsObject *fg) +{ + _PyObject_GC_UNTRACK(fg); + fg_clear(fg); + PyObject_GC_Del(fg); +} + +static int +fg_traverse(PyFastGlobalsObject *fg, visitproc visit, void *arg) +{ + Py_VISIT(fg->globals); + Py_VISIT(fg->builtins); + Py_VISIT(fg->names); + /* Traversing entries will be done in dict traversal */ + return 0; +} + +PyTypeObject PyFastGlobals_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "fastglobals", + sizeof(PyFastGlobalsObject), + 0, + (destructor)fg_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)fg_traverse, /* tp_traverse */ + (inquiry)fg_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ +}; + + +void +PyFastGlobals_Fini(void) +{ + Py_XDECREF(__builtins__); +} Index: Objects/dictobject.c =================================================================== --- Objects/dictobject.c (revision 59259) +++ Objects/dictobject.c (working copy) @@ -207,6 +207,7 @@ if (mp->ma_fill) { EMPTY_TO_MINSIZE(mp); } + mp->ma_entryversion = 0; assert (mp->ma_used == 0); assert (mp->ma_table == mp->ma_smalltable); assert (mp->ma_mask == PyDict_MINSIZE - 1); @@ -215,6 +216,7 @@ if (mp == NULL) return NULL; EMPTY_TO_MINSIZE(mp); + mp->ma_entryversion = 0; } mp->ma_lookup = lookdict_string; #ifdef SHOW_CONVERSION_COUNTS @@ -425,6 +427,7 @@ ep->me_hash = (Py_ssize_t)hash; ep->me_value = value; mp->ma_used++; + mp->ma_entryversion++; /* key insert invalidates */ } return 0; } @@ -546,6 +549,7 @@ if (is_oldtable_malloced) PyMem_DEL(oldtable); + mp->ma_entryversion++; /* table resize invalidates */ return 0; } @@ -602,6 +606,42 @@ return ep->me_value; } +/* Like PyDict_GetItem, but retrieves a PyDictEntry pointer. This is NOT +safe to use unless you VERY carefully keep track of the dictionary's +entries version and update your entries pointers when it changes. This is +a little tricker than it sounds because key comparisons can change a +dictionary's entries. See fastglobals.h/fastglobals.c for examples of how +to use it safely. + +Returns NULL with an exception set if an error occurred, or NULL without +an exception set if the key is not in the dictionary. ??? Is there a +better way to do this? */ +PyDictEntry * +PyDict_GetEntry(PyObject *op, PyObject *key) +{ + PyDictObject *mp; + long hash; + PyDictEntry *ep; + + if (!PyDict_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + assert(key != NULL); + mp = (PyDictObject *)op; + + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *)key)->ob_shash) == -1) { + if ((hash = PyObject_Hash(key)) == -1) + return NULL; + } + + ep = (mp->ma_lookup)(mp, key, hash); + if (ep == NULL || ep->me_value == NULL) + return NULL; + return ep; +} + /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() @@ -690,6 +730,7 @@ old_value = ep->me_value; ep->me_value = NULL; mp->ma_used--; + mp->ma_entryversion++; /* deleting invalidates */ Py_DECREF(old_value); Py_DECREF(old_key); return 0; @@ -762,6 +803,7 @@ if (table_is_malloced) PyMem_DEL(table); + mp->ma_entryversion++; /* clear invalidates */ } /* @@ -1862,6 +1904,7 @@ old_value = ep->me_value; ep->me_value = NULL; mp->ma_used--; + mp->ma_entryversion++; /* deleting invalidates */ Py_DECREF(old_key); return old_value; } @@ -1921,6 +1964,7 @@ mp->ma_used--; assert(mp->ma_table[0].me_value == NULL); mp->ma_table[0].me_hash = i + 1; /* next place to start */ + mp->ma_entryversion++; /* deleting invalidates */ return res; } Index: Objects/frameobject.c =================================================================== --- Objects/frameobject.c (revision 59259) +++ Objects/frameobject.c (working copy) @@ -17,8 +17,6 @@ static PyMemberDef frame_memberlist[] = { {"f_back", T_OBJECT, OFF(f_back), RO}, {"f_code", T_OBJECT, OFF(f_code), RO}, - {"f_builtins", T_OBJECT, OFF(f_builtins),RO}, - {"f_globals", T_OBJECT, OFF(f_globals), RO}, {"f_lasti", T_INT, OFF(f_lasti), RO}, {"f_exc_type", T_OBJECT, OFF(f_exc_type)}, {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, @@ -35,6 +33,22 @@ } static PyObject * +frame_getglobals(PyFrameObject *f, void *closure) +{ + PyObject *globals = PyFrame_GET_GLOBALS(f); + Py_INCREF(globals); + return globals; +} + +static PyObject * +frame_getbuiltins(PyFrameObject *f, void *closure) +{ + PyObject *builtins = PyFrame_GET_BUILTINS(f); + Py_INCREF(builtins); + return builtins; +} + +static PyObject * frame_getlineno(PyFrameObject *f, void *closure) { int lineno; @@ -348,6 +362,8 @@ static PyGetSetDef frame_getsetlist[] = { {"f_locals", (getter)frame_getlocals, NULL, NULL}, + {"f_globals", (getter)frame_getglobals, NULL, NULL}, + {"f_builtins", (getter)frame_getbuiltins, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, (setter)frame_setlineno, NULL}, {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, @@ -422,8 +438,7 @@ } Py_XDECREF(f->f_back); - Py_DECREF(f->f_builtins); - Py_DECREF(f->f_globals); + Py_CLEAR(f->f_fastglobals); Py_CLEAR(f->f_locals); Py_CLEAR(f->f_trace); Py_CLEAR(f->f_exc_type); @@ -453,8 +468,14 @@ Py_VISIT(f->f_back); Py_VISIT(f->f_code); - Py_VISIT(f->f_builtins); - Py_VISIT(f->f_globals); + /* This is a bad idea now: if gc breaks a cycle by clearing + f_fastglobals, when a generator is finally collected it does this + sequence of calls: gen_del->gen_close->gen_send_ex-> + PyEval_EvalFrameEx. But PyEval_EvalFrameEx references + f_fastglobals->globals, which will be cleared by then, resuling + in a segfault. + ??? Is there a way to preserve this traversal? */ + /* Py_VISIT(f->f_fastglobals); */ Py_VISIT(f->f_locals); Py_VISIT(f->f_trace); Py_VISIT(f->f_exc_type); @@ -543,60 +564,33 @@ 0, /* tp_dict */ }; -static PyObject *builtin_object; - int _PyFrame_Init() { - builtin_object = PyString_InternFromString("__builtins__"); - return (builtin_object != NULL); + return 1; } PyFrameObject * -PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, - PyObject *locals) +PyFrame_New(PyThreadState *tstate, PyCodeObject *code, + PyObject *fastglobals, PyObject *locals) { PyFrameObject *back = tstate->frame; PyFrameObject *f; - PyObject *builtins; Py_ssize_t i; #ifdef Py_DEBUG - if (code == NULL || globals == NULL || !PyDict_Check(globals) || + if (code == NULL || fastglobals == NULL || + !PyFastGlobals_Check(fastglobals) || (locals != NULL && !PyMapping_Check(locals))) { PyErr_BadInternalCall(); return NULL; } #endif - if (back == NULL || back->f_globals != globals) { - builtins = PyDict_GetItem(globals, builtin_object); - if (builtins) { - if (PyModule_Check(builtins)) { - builtins = PyModule_GetDict(builtins); - assert(!builtins || PyDict_Check(builtins)); - } - else if (!PyDict_Check(builtins)) - builtins = NULL; - } - if (builtins == NULL) { - /* No builtins! Make up a minimal one - Give them 'None', at least. */ - builtins = PyDict_New(); - if (builtins == NULL || - PyDict_SetItemString( - builtins, "None", Py_None) < 0) - return NULL; - } - else - Py_INCREF(builtins); - - } - else { - /* If we share the globals, we share the builtins. - Save a lookup and a call. */ - builtins = back->f_builtins; - assert(builtins != NULL && PyDict_Check(builtins)); - Py_INCREF(builtins); - } + /* Superfluous checks here almost always save a function call */ + if (!PyFastGlobals_IS_UPDATED(fastglobals) || + PyFastGlobals_BUILTINS_CHANGED(fastglobals)) + if (PyFastGlobals_CheckBuiltins(fastglobals) < 0) + return NULL; + if (code->co_zombieframe != NULL) { f = code->co_zombieframe; code->co_zombieframe = NULL; @@ -613,7 +607,6 @@ f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras); if (f == NULL) { - Py_DECREF(builtins); return NULL; } } @@ -625,7 +618,6 @@ if (Py_Size(f) < extras) { f = PyObject_GC_Resize(PyFrameObject, f, extras); if (f == NULL) { - Py_DECREF(builtins); return NULL; } } @@ -642,12 +634,11 @@ f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; } f->f_stacktop = f->f_valuestack; - f->f_builtins = builtins; Py_XINCREF(back); f->f_back = back; Py_INCREF(code); - Py_INCREF(globals); - f->f_globals = globals; + Py_INCREF(fastglobals); + f->f_fastglobals = fastglobals; /* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */ if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == (CO_NEWLOCALS | CO_OPTIMIZED)) @@ -662,7 +653,7 @@ } else { if (locals == NULL) - locals = globals; + locals = PyFastGlobals_GLOBALS(fastglobals); Py_INCREF(locals); f->f_locals = locals; } @@ -899,6 +890,4 @@ --numfree; } assert(numfree == 0); - Py_XDECREF(builtin_object); - builtin_object = NULL; } Index: Objects/funcobject.c =================================================================== --- Objects/funcobject.c (revision 59259) +++ Objects/funcobject.c (working copy) @@ -9,9 +9,16 @@ PyObject * PyFunction_New(PyObject *code, PyObject *globals) { - PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, - &PyFunction_Type); static PyObject *__name__ = 0; + PyFunctionObject *op; + PyObject *fastglobals; + + fastglobals = PyFastGlobals_New(globals, + ((PyCodeObject*)code)->co_names); + if (fastglobals == NULL) + return NULL; + + op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); if (op != NULL) { PyObject *doc; PyObject *consts; @@ -19,8 +26,7 @@ op->func_weakreflist = NULL; Py_INCREF(code); op->func_code = code; - Py_INCREF(globals); - op->func_globals = globals; + op->func_fastglobals = fastglobals; op->func_name = ((PyCodeObject *)code)->co_name; Py_INCREF(op->func_name); op->func_defaults = NULL; /* No default arguments */ @@ -54,8 +60,10 @@ op->func_module = module; } } - else + else { + Py_DECREF(fastglobals); return NULL; + } _PyObject_GC_TRACK(op); return (PyObject *)op; } @@ -77,7 +85,7 @@ PyErr_BadInternalCall(); return NULL; } - return ((PyFunctionObject *) op) -> func_globals; + return PyFunction_GET_GLOBALS(op); } PyObject * @@ -165,10 +173,6 @@ RESTRICTED|READONLY}, {"func_doc", T_OBJECT, OFF(func_doc), PY_WRITE_RESTRICTED}, {"__doc__", T_OBJECT, OFF(func_doc), PY_WRITE_RESTRICTED}, - {"func_globals", T_OBJECT, OFF(func_globals), - RESTRICTED|READONLY}, - {"__globals__", T_OBJECT, OFF(func_globals), - RESTRICTED|READONLY}, {"__module__", T_OBJECT, OFF(func_module), PY_WRITE_RESTRICTED}, {NULL} /* Sentinel */ }; @@ -329,6 +333,15 @@ return 0; } +static PyObject * +func_get_globals(PyFunctionObject *op) +{ + if (restricted()) + return NULL; + Py_INCREF(PyFunction_GET_GLOBALS(op)); + return PyFunction_GET_GLOBALS(op); +} + static PyGetSetDef func_getsetlist[] = { {"func_code", (getter)func_get_code, (setter)func_set_code}, {"__code__", (getter)func_get_code, (setter)func_set_code}, @@ -340,6 +353,8 @@ {"__dict__", (getter)func_get_dict, (setter)func_set_dict}, {"func_name", (getter)func_get_name, (setter)func_set_name}, {"__name__", (getter)func_get_name, (setter)func_set_name}, + {"func_globals", (getter)func_get_globals, NULL}, + {"__globals__", (getter)func_get_globals, NULL}, {NULL} /* Sentinel */ }; @@ -452,7 +467,7 @@ if (op->func_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) op); Py_DECREF(op->func_code); - Py_DECREF(op->func_globals); + Py_DECREF(op->func_fastglobals); Py_XDECREF(op->func_module); Py_DECREF(op->func_name); Py_XDECREF(op->func_defaults); @@ -474,7 +489,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg) { Py_VISIT(f->func_code); - Py_VISIT(f->func_globals); + Py_VISIT(f->func_fastglobals); Py_VISIT(f->func_module); Py_VISIT(f->func_defaults); Py_VISIT(f->func_doc); @@ -523,7 +538,7 @@ result = PyEval_EvalCodeEx( (PyCodeObject *)PyFunction_GET_CODE(func), - PyFunction_GET_GLOBALS(func), (PyObject *)NULL, + PyFunction_GET_FASTGLOBALS(func), (PyObject *)NULL, &PyTuple_GET_ITEM(arg, 0), PyTuple_Size(arg), k, nk, d, nd, PyFunction_GET_CLOSURE(func)); Index: PC/os2emx/Makefile =================================================================== --- PC/os2emx/Makefile (revision 59259) +++ PC/os2emx/Makefile (working copy) @@ -405,7 +405,8 @@ Objects/typeobject.c \ Objects/unicodeobject.c \ Objects/unicodectype.c \ - Objects/weakrefobject.c) + Objects/weakrefobject.c \ + Objects/fastglobals.c) SRC.LIB= $(SRC.OS2EMX) \ $(SRC.MAIN) \ Index: PC/VC6/pythoncore.dsp =================================================================== --- PC/VC6/pythoncore.dsp (revision 59259) +++ PC/VC6/pythoncore.dsp (working copy) @@ -687,6 +687,10 @@ # End Source File # Begin Source File +SOURCE=..\..\Objects\fastglobals.c +# End Source File +# Begin Source File + SOURCE=..\..\Modules\xxsubtype.c # End Source File # Begin Source File Index: Lib/test/test_gc.py =================================================================== --- Lib/test/test_gc.py (revision 59259) +++ Lib/test/test_gc.py (working copy) @@ -173,7 +173,7 @@ exec("def f(): pass\n") in d gc.collect() del d - self.assertEqual(gc.collect(), 2) + self.assertEqual(gc.collect(), 4) def test_frame(self): def f(): Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (revision 59259) +++ Makefile.pre.in (working copy) @@ -306,6 +306,7 @@ Objects/listobject.o \ Objects/longobject.o \ Objects/dictobject.o \ + Objects/fastglobals.o \ Objects/methodobject.o \ Objects/moduleobject.o \ Objects/object.o \ @@ -529,6 +530,7 @@ Include/complexobject.h \ Include/descrobject.h \ Include/dictobject.h \ + Include/fastglobals.h \ Include/enumobject.h \ Include/genobject.h \ Include/fileobject.h \ Index: Modules/_ctypes/callbacks.c =================================================================== --- Modules/_ctypes/callbacks.c (revision 59259) +++ Modules/_ctypes/callbacks.c (working copy) @@ -34,6 +34,7 @@ PyObject *py_srcfile = 0; PyObject *py_funcname = 0; PyObject *py_globals = 0; + PyObject *py_fastglobals = 0; PyObject *empty_tuple = 0; PyObject *empty_string = 0; PyCodeObject *py_code = 0; @@ -47,6 +48,8 @@ if (!py_globals) goto bad; empty_tuple = PyTuple_New(0); if (!empty_tuple) goto bad; + py_fastglobals = PyFastGlobals_New(py_globals, empty_tuple); + if (!py_fastglobals) goto bad; empty_string = PyString_FromString(""); if (!empty_string) goto bad; py_code = PyCode_New( @@ -69,7 +72,7 @@ py_frame = PyFrame_New( PyThreadState_Get(), /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ - py_globals, /*PyObject *globals,*/ + py_fastglobals, /*PyObject *fastglobals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; @@ -77,6 +80,7 @@ PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_globals); + Py_XDECREF(py_fastglobals); Py_XDECREF(py_srcfile); Py_XDECREF(py_funcname); Py_XDECREF(empty_tuple); Index: Modules/pyexpat.c =================================================================== --- Modules/pyexpat.c (revision 59259) +++ Modules/pyexpat.c (working copy) @@ -380,11 +380,16 @@ PyThreadState *tstate = PyThreadState_GET(); PyFrameObject *f; PyObject *res; + PyObject *fastglobals; if (c == NULL) return NULL; - f = PyFrame_New(tstate, c, PyEval_GetGlobals(), NULL); + fastglobals = PyFastGlobals_New(PyEval_GetGlobals(), c->co_names); + if (fastglobals == NULL) + return NULL; + f = PyFrame_New(tstate, c, fastglobals, NULL); + Py_CLEAR(fastglobals); /* Don't need this anymore */ if (f == NULL) return NULL; tstate->frame = f; Index: PCbuild8/pythoncore/pythoncore.vcproj =================================================================== --- PCbuild8/pythoncore/pythoncore.vcproj (revision 59259) +++ PCbuild8/pythoncore/pythoncore.vcproj (working copy) @@ -935,6 +935,10 @@ RelativePath="..\..\Objects\weakrefobject.c" > + + + + + +