[issue1518] Fast globals/builtins access (patch)
Neil Toronto added the comment: Christian: Thanks for that, I'll have to remember DEBUG_LEAK in the future. :) It looks like it may be acting correctly since there *is* now a fastglobals object that needs collecting, and also a new tuple built from co_names + "__builtins__". I don't know why it wouldn't collect those in the shell, though. The shell does create a new stack frame for every command (where a function's commands are all run in a single stack frame), but I don't know why that would matter. I'll look further into it. Chris: The build problems should be fixed in the next patch. Thanks for spending so much time on benchmarking this. Regarding PyEval_EvalCode creating a PyFastGlobalsObject: I'm not sure whether it's right thing to do, but I think it is. PyEval_EvalCode gets called for eval, exec, running an imported module's code, and everything in the interactive prompt - basically, running any code that's not in a function or generator. (I think that covers everything.) It seems like the patch has PyEval_EvalCode doing the right thing in general, but it may suffer a bit in benchmarks. __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1518> __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1560] PATCH: Attribute lookup caching
New submission from Neil Toronto: I've attached a patch to accelerate type and instance lookups using a specialized cache. It includes a benchmarking script (fastattr_test.py) that tests both successful and failing lookups on list, dict and tuple, and a simple, deep hierarchy. Benchmark results are here: http://spreadsheets.google.com/ccc?key=pHIJrYc_pnIUpTm6QSG2gZg&hl=en_US Everything tested in fastattr_test.py is faster except list.__init__ and list().__init__ (and I haven't got a clue why, so some pointers would be nice). Pybench is faster overall. TryRaiseExcept is faster for some non-obvious reason. CreateNewInstances is a little slower, which I'll discuss in a bit. Setting type attributes is slower, but I haven't benchmarked that yet. It may not happen often enough that we care as long as it's not noticeably slow in general usage. In benchmarks the patch does a little slower, it may be in part because I removed a manually inlined _PyType_Lookup from PyObject_GenericGetAttr. Something like it can be put back if it needs to be. It works in a fairly obvious way. Every type has a tp_cache, which is a custom dict type that caches the first value in a type dict in the MRO for a given name. Lazy cached lookups are done via _PyType_CachingLookup, which is a drop-in replacement for _PyType_Lookup. The only way to set an attribute on a type is via its setattr, so type's setattr notifies subclasses to invalidate specific cache entries. The cache dict is custom for two reasons: 1. The regular dict is a little slower because it's more general. The custom dict is restricted to string-exact keys (types fall back to no caching if this constraint is violated). Because it's internal to typeobject.c, it's safe for lookups to return entry pointers rather than values, so lookups only have to be done once, even on cache misses. 2. Caching missing attributes is crucial for speed on instance attribute lookups. Both type and metatype instances check all the way through the MRO for a descriptor before even trying to find an attribute. It's usually missing. Having to go through the cache machinery to find a missing attribute for every attribute lookup is expensive. However, storing all missing attribute queries in the cache is bad - it can grow unboundedly through hasattr(inst, ) calls. What's needed is a dict that knows that some of its entries are transient and doesn't copy them over on resize. It wasn't clear how to implement that efficiently with a regular dict (I suspect it's not possible), so I created a new dict type that considers entries transient (meaning the attribute is missing) when me_value == NULL. This is also very good for failing hasattr(...) calls and try...inst.method()...except style duck typing. Now, about the CreateNewInstances benchmark. It creates three classes with __init__ methods that assign a few attributes. The missing descriptors are cached, and then the cache is resized, which clears the cached missing descriptors. Increasing the minimum cache size from 4 to 8 clears up the problem. However, for any class, SOME minimum cache size will properly cache missing descriptors and some other one won't. I have some solutions for this floating around in my head, which I'll try shortly. (One idea is for missing attributes, if they're not missing on the *instance*, to be permanent in the cache.) But I'd like to get this patch off my hard drive and into other people's hands for testing, poking, prodding, and getting feedback on what's going right and what's not. -- components: Interpreter Core files: fastattr-0.patch.txt messages: 58229 nosy: ntoronto severity: normal status: open title: PATCH: Attribute lookup caching versions: Python 2.6 Added file: http://bugs.python.org/file8883/fastattr-0.patch.txt __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1560> __Index: Include/dictobject.h === --- Include/dictobject.h(revision 59362) +++ Include/dictobject.h(working copy) @@ -111,6 +111,8 @@ PyAPI_FUNC(int) PyDict_Contains(PyObject *mp, PyObject *key); PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, long hash); +PyAPI_FUNC(int) PyDict_IsStringKeysOnly(PyObject *mp); + /* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */ PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other); Index: Include/object.h === --- Include/object.h(revision 59362) +++ Include/object.h(working copy) @@ -391,6 +391,7 @@ PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); +PyAPI_F
[issue1700288] Armin's method cache optimization updated for Python 2.6
Neil Toronto added the comment: Attribute access that happens only once or very infrequently probably shouldn't go through the cache machinery - it'll only slow things down. (Updating slots for example.) Maybe _PyType_Lookup should remain the same, with _PyType_CachingLookup added for attribute access via type/instance getattr. _ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1700288> _ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1700288] Armin's method cache optimization updated for Python 2.6
Neil Toronto added the comment: I've attached the microbenchmarks I was using for my own version of attribute caching. For list, tuple, dict, and a deep hierarchy, it tests accessing type attributes (cls.__class__), class attributes (cls.__init__), class attributes via instance (inst.__class__), and instance attributes (inst.__init__), using LOAD_ATTR and hasattr. It also tests hasattr with missing attributes. -- nosy: +ntoronto Added file: http://bugs.python.org/file8887/fastattr_test.py _ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1700288> _#!/usr/bin/python import timeit, random, time, sys MULTIPLIER = 1 class A(object): def __init__(self, *args): pass class B(A): pass class C(B): pass class D(C): pass class E(D): pass class F(E): pass class G(F): pass class H(G): def __init__(self): pass class I(H): pass def test_init(tmp): tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ def test_class(tmp): tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ def test_has_init(tmp): hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__') hasattr(tmp, '__init__'); hasattr(tmp, '__init__
[issue1568] PATCH: Armin's attribute lookup caching for 3.0
New submission from Neil Toronto: This is a half-port of the patches in #1685986 and #1700288 to Python 3.0. Speedups are about the same as in those patches applied to their respective Python versions for minibenchmarks (included in the patch as fastattr_test_py3k.py): 5%-30% or more depending on how deep the class hierarchy is. Pybench takes 4% less time on my system, and pystone takes 6% less time. (Pybench would do better, but SpecialClassAttribute runs long and spends half its time setting class attributes.) The main difference between this and the previous patches is that 3.0's simplifications allow a smaller footprint and make it easier to analyze its correctness. Specifically, the fact that every object in the MRO must be a type allows us to assume that every attribute lookup is cacheable, and allows integration into the update_subclasses mechanism when attributes are set. The lack of compiled extension modules means there is no flag to check to see whether a type object supports caching. -- components: Interpreter Core files: python30-attrcache.diff messages: 58274 nosy: ntoronto severity: normal status: open title: PATCH: Armin's attribute lookup caching for 3.0 type: rfe versions: Python 3.0 Added file: http://bugs.python.org/file8889/python30-attrcache.diff __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1568> __Index: Python/pythonrun.c === --- Python/pythonrun.c (revision 59400) +++ Python/pythonrun.c (working copy) @@ -501,6 +501,9 @@ /* Cleanup Unicode implementation */ _PyUnicode_Fini(); + + /* Report counts of attribute cache hits/misses (if enabled) */ + PyType_Fini(); /* reset file system default encoding */ if (!Py_HasFileSystemDefaultEncoding) { Index: Include/object.h === --- Include/object.h (revision 59400) +++ Include/object.h (working copy) @@ -373,6 +373,9 @@ PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; + /* Unique cache ID per type, assigned when bases change (see + mro_internal) */ + PY_LONG_LONG tp_cache_id; #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ Index: Include/pythonrun.h === --- Include/pythonrun.h (revision 59400) +++ Include/pythonrun.h (working copy) @@ -141,6 +141,7 @@ PyAPI_FUNC(void) PyBytes_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); PyAPI_FUNC(void) PyOS_FiniInterrupts(void); +PyAPI_FUNC(void) PyType_Fini(void); /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); Index: fastattr_test_py3k.py === --- fastattr_test_py3k.py (revision 0) +++ fastattr_test_py3k.py (revision 0) @@ -0,0 +1,265 @@ +#!/usr/bin/python + +import timeit, random, time, sys + +MULTIPLIER = 1 + + +class A(object): + def __init__(self, *args): + pass + +class B(A): pass + +class C(B): pass + +class D(C): pass + +class E(D): pass + +class F(E): pass + +class G(F): pass + +class H(G): + def __init__(self): + pass + +class I(H): pass + + +def test_init(tmp): + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + + +def test_class(tmp): + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__c
[issue1568] PATCH: Armin's attribute lookup caching for 3.0
Neil Toronto added the comment: This patch adds the ability to invalidate all of a subclasses' cache entries upon setattr rather than updating just one cache entry. It's not clear which of these is the Right Thing To Do, so I've made it an #ifdef for now. It defaults to updating. Added file: http://bugs.python.org/file8891/python30-attrcache-1.diff __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1568> __Index: Python/pythonrun.c === --- Python/pythonrun.c (revision 59400) +++ Python/pythonrun.c (working copy) @@ -501,6 +501,9 @@ /* Cleanup Unicode implementation */ _PyUnicode_Fini(); + + /* Report counts of attribute cache hits/misses (if enabled) */ + PyType_Fini(); /* reset file system default encoding */ if (!Py_HasFileSystemDefaultEncoding) { Index: Include/object.h === --- Include/object.h (revision 59400) +++ Include/object.h (working copy) @@ -373,6 +373,9 @@ PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; + /* Unique cache ID per type, assigned when bases change (see + mro_internal) */ + PY_LONG_LONG tp_cache_id; #ifdef COUNT_ALLOCS /* these must be last and never explicitly initialized */ Index: Include/pythonrun.h === --- Include/pythonrun.h (revision 59400) +++ Include/pythonrun.h (working copy) @@ -141,6 +141,7 @@ PyAPI_FUNC(void) PyBytes_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); PyAPI_FUNC(void) PyOS_FiniInterrupts(void); +PyAPI_FUNC(void) PyType_Fini(void); /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); Index: fastattr_test_py3k.py === --- fastattr_test_py3k.py (revision 0) +++ fastattr_test_py3k.py (revision 0) @@ -0,0 +1,265 @@ +#!/usr/bin/python + +import timeit, random, time, sys + +MULTIPLIER = 1 + + +class A(object): + def __init__(self, *args): + pass + +class B(A): pass + +class C(B): pass + +class D(C): pass + +class E(D): pass + +class F(E): pass + +class G(F): pass + +class H(G): + def __init__(self): + pass + +class I(H): pass + + +def test_init(tmp): + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__ + + +def test_class(tmp): + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__ + tmp.__class__; tmp.__
[issue1560] PATCH: Attribute lookup caching
Neil Toronto added the comment: Yes, as well #1700288 (Armin's attribute lookup caching patch ported to 2.6) or #1685986 (Armin's original for 2.4), or whatever Raymond finds most convenient. __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1560> __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1560] PATCH: Attribute lookup caching
Neil Toronto added the comment: Sorry - I'll be more clear. I'd love to see 2.6 get attribute lookup caching, and Kevin's port (#1700288) of Armin's original 2.4 patch (#1685986) does that for 2.6. #1685986 (2.4) should be closed and #1700288 (2.6) should remain open. But Raymond has indicated that he's currently working on #1685986 - I think for 2.6, but it's not clear. __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1560> __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1568] PATCH: Armin's attribute lookup caching for 3.0
Neil Toronto added the comment: Well horse pucky. I plum forgot about deletes. I've attached an update that properly clears the cache entry for the deleted attribute for all non-shadowing subclasses. (It was a small change.) Undef'ing ATTRCACHE_SETATTR_INVALIDATES should work now. Re: different from 2.6: It takes advantage of the lack of "classic" classes. That makes some things a lot easier. Also, I was looking into updating cache entries to see if it would be faster than invalidating all the entries for a type, among other improvements. FWIW, updating cache entries is a little faster on my box. Also: This may have the same problem with test_ctypes' test_incomplete.py as #1700288 did, though I haven't seen any ctypes tests fail. Added file: http://bugs.python.org/file9189/python30-attrcache-2.diff __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1568> __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1568] PATCH: Armin's attribute lookup caching for 3.0
Neil Toronto added the comment: There's nothing it tests that standard unit tests don't, so it shouldn't stick around as a unit test. I used it to time different types of attribute lookups and left it in as an optimization aid. The main test groups are '.' access, successful hasattr (returns True), and failing hasattr (returns False). For each group it prints a header and then four test cases for each test class: metaclass attribute access (class.__class__), class attribute access (class.__init__), class attribute access via an instance (class().__class__), and instance attribute access (class().__init__). The test classes include a few built-ins and the classes of a made-up deep hierarchy. The main thing to notice is that, when you run this using the patched source, every number is no larger than when you run it using the trunk, and most are smaller. __ Tracker <[EMAIL PROTECTED]> <http://bugs.python.org/issue1568> __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com