Movatterモバイル変換
[0]ホーム
[Python-Dev] Patch to use dict subclasses in eval(), exec
Jeff Eplerjepler@unpythonic.net
Mon, 28 Oct 2002 20:39:55 -0600
--PEIAKu/WMn1b1Hv9Content-Type: text/plain; charset=us-asciiContent-Disposition: inlineUsing 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 (toset 'fastglobals' and 'slowbuiltins'), each lookup in globals (tocheck 'fastglobals'), and each lookup in builtins (to check 'slowbuiltins').If you do use the feature, the performance impact is unfortunatelyquite 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__ couldprobably be erased if the 'fastglobals' test can check that __getitem__is not definedIncluded are a patch and a test suite. Note that the patch causes afailure in test_descr which is testing that __getitem__ is *not* called ona dictionary subclass.Pystone timings on a 1GHz Duron system running RedHat Linux 8.0 (gcc3.2) follow. CVS +patch +subclass+__getattr__1 2.74 2.76 3 14.052 2.74 2.78 3.02 14.073 2.74 2.78 3.03 14.074 2.76 2.78 3.04 14.085 2.76 2.79 3.04 14.1avg 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.74This machine benchmarks at 18248.2 pystones/secondPystone(1.1) time for 50000 passes = 2.74This machine benchmarks at 18248.2 pystones/secondPystone(1.1) time for 50000 passes = 2.74This machine benchmarks at 18248.2 pystones/secondPystone(1.1) time for 50000 passes = 2.76This machine benchmarks at 18115.9 pystones/secondPystone(1.1) time for 50000 passes = 2.76This machine benchmarks at 18115.9 pystones/second* 2.3a0 CVS + patchPystone(1.1) time for 50000 passes = 2.78This machine benchmarks at 17985.6 pystones/secondPystone(1.1) time for 50000 passes = 2.78This machine benchmarks at 17985.6 pystones/secondPystone(1.1) time for 50000 passes = 2.79This machine benchmarks at 17921.1 pystones/secondPystone(1.1) time for 50000 passes = 2.79This machine benchmarks at 17921.1 pystones/secondPystone(1.1) time for 50000 passes = 2.76This 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.03This machine benchmarks at 16501.7 pystones/secondPystone(1.1) time for 50000 passes = 3.04This machine benchmarks at 16447.4 pystones/secondPystone(1.1) time for 50000 passes = 3This machine benchmarks at 16666.7 pystones/secondPystone(1.1) time for 50000 passes = 3.04This machine benchmarks at 16447.4 pystones/secondPystone(1.1) time for 50000 passes = 3.02This 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.05This machine benchmarks at 3558.72 pystones/secondPystone(1.1) time for 50000 passes = 14.08This machine benchmarks at 3551.14 pystones/secondPystone(1.1) time for 50000 passes = 14.07This machine benchmarks at 3553.66 pystones/secondPystone(1.1) time for 50000 passes = 14.07This machine benchmarks at 3553.66 pystones/secondPystone(1.1) time for 50000 passes = 14.1This machine benchmarks at 3546.1 pystones/second--PEIAKu/WMn1b1Hv9Content-Type: text/plain; charset=us-asciiContent-Disposition: attachment; filename="ceval-userdict.patch"Index: Python/ceval.c===================================================================RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,vretrieving revision 2.338diff -u -r2.338 ceval.c--- Python/ceval.c3 Oct 2002 09:53:11 -00002.338+++ Python/ceval.c29 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/WMn1b1Hv9Content-Type: text/plain; charset=us-asciiContent-Disposition: attachment; filename="test_dictsubclass.py"X='xx'class D(dict):def __getitem__(self, item):if item is X: return Xreturn dict.__getitem__(self, item)d = D(globals())d['zz'] = 'zz'# Make sure the dict acts like it shouldprint d[X] == Xprint d['D'] == Dprint d['zz'] == 'zz'try: d['yy']except KeyError, detail: print "KeyError", detailelse: print "failed to get exception"# Make sure that exec and eval() in globals() works rightexec "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 detailelse: print "failed to get exception"try: exec "zz" in globals()except NameError, detail: print detailelse: print "failed to get exception"try: exec "yy" in globals()except NameError, detail: print detailelse: print "failed to get exception"# Make sure that exec and eval() in d works rightexec "print (len.__name__, D.__name__, xx, zz)" in dexec "print (lambda: (len.__name__, D.__name__, xx, zz))()" in dprint eval("len.__name__, D.__name__, xx, zz", d)print eval("(lambda: (len.__name__, D.__name__, xx, zz))()", d)try: exec "yy" in dexcept NameError, detail: print detailelse: print "failed to get exception"--PEIAKu/WMn1b1Hv9Content-Type: text/plain; charset=us-asciiContent-Disposition: attachment; filename=test_dictsubclasstest_dictsubclassTrueTrueTrueKeyError 'yy'('len', 'D')('len', 'D')('len', 'D')('len', 'D')name 'xx' is not definedname 'zz' is not definedname '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--
[8]ページ先頭