--PEIAKu/WMn1b1Hv9 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Using dict subclasses in eval() and exec ---------------------------------------- With the attached patch, you can now use a dict subclass which implements __getitem__ as the global or builtin namespace for eval() and exec. If you do not use the feature, the performance impact is low (<2%). The fast case adds only one test+branch to each eval_code() call (to set 'fastglobals' and 'slowbuiltins'), each lookup in globals (to check 'fastglobals'), and each lookup in builtins (to check 'slowbuiltins'). If you do use the feature, the performance impact is unfortunately quite substantial (400%). If you use subclasses of dict with eval()/exec, but do not define __getitem__, the performance penalty is modest (~10%). The smaller penalty of using a dict subclass without __getitem__ could probably be erased if the 'fastglobals' test can check that __getitem__ is not defined Included are a patch and a test suite. Note that the patch causes a failure in test_descr which is testing that __getitem__ is *not* called on a dictionary subclass. Pystone timings on a 1GHz Duron system running RedHat Linux 8.0 (gcc 3.2) follow. CVS +patch +subclass +__getattr__ 1 2.74 2.76 3 14.05 2 2.74 2.78 3.02 14.07 3 2.74 2.78 3.03 14.07 4 2.76 2.78 3.04 14.08 5 2.76 2.79 3.04 14.1 avg 2.748 2.778 3.026 14.074 % 100 101.1 110.1 512.2 * 2.3a0 CVS (28 Oct 2002) Pystone(1.1) time for 50000 passes = 2.74 This machine benchmarks at 18248.2 pystones/second Pystone(1.1) time for 50000 passes = 2.74 This machine benchmarks at 18248.2 pystones/second Pystone(1.1) time for 50000 passes = 2.74 This machine benchmarks at 18248.2 pystones/second Pystone(1.1) time for 50000 passes = 2.76 This machine benchmarks at 18115.9 pystones/second Pystone(1.1) time for 50000 passes = 2.76 This machine benchmarks at 18115.9 pystones/second * 2.3a0 CVS + patch Pystone(1.1) time for 50000 passes = 2.78 This machine benchmarks at 17985.6 pystones/second Pystone(1.1) time for 50000 passes = 2.78 This machine benchmarks at 17985.6 pystones/second Pystone(1.1) time for 50000 passes = 2.79 This machine benchmarks at 17921.1 pystones/second Pystone(1.1) time for 50000 passes = 2.79 This machine benchmarks at 17921.1 pystones/second Pystone(1.1) time for 50000 passes = 2.76 This machine benchmarks at 18115.9 pystones/second * 2.3a0 CVS + patch + dict subclass w/o __getitem__ (commandline: ./python -c 'class D(dict): pass d = D(globals()); execfile("Lib/test/pystone.py", d)') Pystone(1.1) time for 50000 passes = 3.03 This machine benchmarks at 16501.7 pystones/second Pystone(1.1) time for 50000 passes = 3.04 This machine benchmarks at 16447.4 pystones/second Pystone(1.1) time for 50000 passes = 3 This machine benchmarks at 16666.7 pystones/second Pystone(1.1) time for 50000 passes = 3.04 This machine benchmarks at 16447.4 pystones/second Pystone(1.1) time for 50000 passes = 3.02 This machine benchmarks at 16556.3 pystones/second * 2.3a0 CVS + patch + dict subclass w/__getitem__ (commandline: ./python -c 'class D(dict): __getitem__ = lambda s, i: dict.__getitem__(s, i) d = D(globals()); execfile("Lib/test/pystone.py", d)') Pystone(1.1) time for 50000 passes = 14.05 This machine benchmarks at 3558.72 pystones/second Pystone(1.1) time for 50000 passes = 14.08 This machine benchmarks at 3551.14 pystones/second Pystone(1.1) time for 50000 passes = 14.07 This machine benchmarks at 3553.66 pystones/second Pystone(1.1) time for 50000 passes = 14.07 This machine benchmarks at 3553.66 pystones/second Pystone(1.1) time for 50000 passes = 14.1 This machine benchmarks at 3546.1 pystones/second --PEIAKu/WMn1b1Hv9 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ceval-userdict.patch" Index: Python/ceval.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v retrieving revision 2.338 diff -u -r2.338 ceval.c --- Python/ceval.c 3 Oct 2002 09:53:11 -0000 2.338 +++ Python/ceval.c 29 Oct 2002 00:22:42 -0000 @@ -529,6 +529,8 @@ char *filename; #endif + int fastglobals, slowbuiltins; + /* Tuple access macros */ #ifndef Py_DEBUG @@ -603,6 +605,9 @@ names = co->co_names; consts = co->co_consts; fastlocals = f->f_localsplus; +#define PyDict_CheckExact(d) ((d)->ob_type == &PyDict_Type) + fastglobals = PyDict_CheckExact(f->f_globals); + slowbuiltins = !PyDict_CheckExact(f->f_builtins); freevars = f->f_localsplus + f->f_nlocals; _PyCode_GETCODEPTR(co, &first_instr); /* An explanation is in order for the next line. @@ -1596,7 +1601,7 @@ PyObject_REPR(w)); break; } - err = PyDict_SetItem(x, w, v); + err = (fastglobals ? PyDict_SetItem : PyObject_SetItem)(x, w, v); Py_DECREF(v); break; @@ -1675,7 +1680,7 @@ case STORE_GLOBAL: w = GETITEM(names, oparg); v = POP(); - err = PyDict_SetItem(f->f_globals, w, v); + err = (fastglobals ? PyDict_SetItem : PyObject_SetItem)(f->f_globals, w, v); Py_DECREF(v); break; @@ -1696,9 +1701,10 @@ } x = PyDict_GetItem(x, w); if (x == NULL) { - x = PyDict_GetItem(f->f_globals, w); + x = (fastglobals ? PyDict_GetItem : PyObject_GetItem)(f->f_globals, w); if (x == NULL) { - x = PyDict_GetItem(f->f_builtins, w); + if (!fastglobals) PyErr_Clear(); + x = (slowbuiltins ? PyObject_GetItem : PyDict_GetItem)(f->f_builtins, w); if (x == NULL) { format_exc_check_arg( PyExc_NameError, @@ -1711,9 +1717,10 @@ PUSH(x); break; + case LOAD_GLOBAL: w = GETITEM(names, oparg); - if (PyString_CheckExact(w)) { + if (PyString_CheckExact(w) && fastglobals) { /* Inline the PyDict_GetItem() calls. WARNING: this is an extreme speed hack. Do not try this at home. */ @@ -1727,6 +1734,7 @@ PUSH(x); continue; } + if(slowbuiltins) goto load_builtins_slow; d = (PyDictObject *)(f->f_builtins); x = d->ma_lookup(d, w, hash)->me_value; if (x != NULL) { @@ -1738,9 +1746,11 @@ } } /* This is the un-inlined version of the code above */ - x = PyDict_GetItem(f->f_globals, w); + x = PyObject_GetItem(f->f_globals, w); if (x == NULL) { - x = PyDict_GetItem(f->f_builtins, w); + PyErr_Clear(); + load_builtins_slow: + x = PyObject_GetItem(f->f_builtins, w); if (x == NULL) { load_global_error: format_exc_check_arg( --PEIAKu/WMn1b1Hv9 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="test_dictsubclass.py" X='xx' class D(dict): def __getitem__(self, item): if item is X: return X return dict.__getitem__(self, item) d = D(globals()) d['zz'] = 'zz' # Make sure the dict acts like it should print d[X] == X print d['D'] == D print d['zz'] == 'zz' try: d['yy'] except KeyError, detail: print "KeyError", detail else: print "failed to get exception" # Make sure that exec and eval() in globals() works right exec "print (len.__name__, D.__name__)" in globals() exec "print (lambda: (len.__name__, D.__name__))()" in globals() print eval("len.__name__, D.__name__", globals()) print eval("(lambda: (len.__name__, D.__name__))()", globals()) try: eval(X, globals()) except NameError, detail: print detail else: print "failed to get exception" try: exec "zz" in globals() except NameError, detail: print detail else: print "failed to get exception" try: exec "yy" in globals() except NameError, detail: print detail else: print "failed to get exception" # Make sure that exec and eval() in d works right exec "print (len.__name__, D.__name__, xx, zz)" in d exec "print (lambda: (len.__name__, D.__name__, xx, zz))()" in d print eval("len.__name__, D.__name__, xx, zz", d) print eval("(lambda: (len.__name__, D.__name__, xx, zz))()", d) try: exec "yy" in d except NameError, detail: print detail else: print "failed to get exception" --PEIAKu/WMn1b1Hv9 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=test_dictsubclass test_dictsubclass True True True KeyError 'yy' ('len', 'D') ('len', 'D') ('len', 'D') ('len', 'D') name 'xx' is not defined name 'zz' is not defined name 'yy' is not defined ('len', 'D', 'xx', 'zz') ('len', 'D', 'xx', 'zz') ('len', 'D', 'xx', 'zz') ('len', 'D', 'xx', 'zz') name 'yy' is not defined --PEIAKu/WMn1b1Hv9--
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4