[pypy-svn] r36721 - in pypy/dist/pypy: config module/pypymagic objspace/std objspace/std/test
cfbolz at codespeak.net
cfbolz at codespeak.net
Sun Jan 14 12:10:38 CET 2007
Author: cfbolz
Date: Sun Jan 14 12:10:36 2007
New Revision: 36721
Modified:
pypy/dist/pypy/config/pypyoption.py
pypy/dist/pypy/module/pypymagic/__init__.py
pypy/dist/pypy/module/pypymagic/interp_magic.py
pypy/dist/pypy/objspace/std/objspace.py
pypy/dist/pypy/objspace/std/test/test_shadowtracking.py
pypy/dist/pypy/objspace/std/typeobject.py
Log:
(pedronis, cfbolz): try to implement method lookup caching. seems to work as
intended, but gives very little speedup so far (about 4% in richards). more
work needed.
Modified: pypy/dist/pypy/config/pypyoption.py
==============================================================================
--- pypy/dist/pypy/config/pypyoption.py (original)
+++ pypy/dist/pypy/config/pypyoption.py Sun Jan 14 12:10:36 2007
@@ -155,8 +155,18 @@
default=False,
requires=[("objspace.std.withmultidict", True),
("objspace.std.withtypeversion", True)]),
-
-
+ BoolOption("withmethodcache",
+ "try to cache methods",
+ default=False,
+ requires=[("objspace.std.withshadowtracking", True)]),
+ BoolOption("withmethodcachecounter",
+ "try to cache methods and provide a counter in pypymagic. "
+ "for testing purposes only.",
+ default=False,
+ requires=[("objspace.std.withmethodcache", True)]),
+ IntOption("methodcachesize",
+ "size of the method cache (should be a power of 2)",
+ default=2048),
BoolOption("optimized_int_add",
"special case the addition of two integers in BINARY_ADD",
default=False),
Modified: pypy/dist/pypy/module/pypymagic/__init__.py
==============================================================================
--- pypy/dist/pypy/module/pypymagic/__init__.py (original)
+++ pypy/dist/pypy/module/pypymagic/__init__.py Sun Jan 14 12:10:36 2007
@@ -10,4 +10,6 @@
'pypy_repr' : 'interp_magic.pypy_repr',
'isfake' : 'interp_magic.isfake',
'interp_pdb' : 'interp_magic.interp_pdb',
+ 'method_cache_counter' : 'interp_magic.method_cache_counter',
+ 'reset_method_cache_counter' : 'interp_magic.reset_method_cache_counter',
}
Modified: pypy/dist/pypy/module/pypymagic/interp_magic.py
==============================================================================
--- pypy/dist/pypy/module/pypymagic/interp_magic.py (original)
+++ pypy/dist/pypy/module/pypymagic/interp_magic.py Sun Jan 14 12:10:36 2007
@@ -1,4 +1,5 @@
from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import ObjSpace
from pypy.rlib.objectmodel import we_are_translated
def pypy_repr(space, w_object):
@@ -19,3 +20,21 @@
else:
import pdb
pdb.set_trace()
+
+def method_cache_counter(space, name):
+ if not space.config.objspace.std.withmethodcachecounter:
+ raise OperationError(space.w_NotImplementedError,
+ space.wrap("not implemented"))
+ ec = space.getexecutioncontext()
+ return space.newtuple([space.newint(ec.method_cache_hits.get(name, 0)),
+ space.newint(ec.method_cache_misses.get(name, 0)),])
+method_cache_counter.unwrap_spec = [ObjSpace, str]
+
+def reset_method_cache_counter(space):
+ if not space.config.objspace.std.withmethodcachecounter:
+ raise OperationError(space.w_NotImplementedError,
+ space.wrap("not implemented"))
+ ec = space.getexecutioncontext()
+ ec.method_cache_misses = {}
+ ec.method_cache_hits = {}
+
Modified: pypy/dist/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/std/objspace.py (original)
+++ pypy/dist/pypy/objspace/std/objspace.py Sun Jan 14 12:10:36 2007
@@ -275,6 +275,14 @@
# add space specific fields to execution context
ec = ObjSpace.createexecutioncontext(self)
ec._py_repr = self.newdict()
+ if self.config.objspace.std.withmethodcache:
+ SIZE = self.config.objspace.std.methodcachesize
+ ec.method_cache_versions = [None] * SIZE
+ ec.method_cache_names = [None] * SIZE
+ ec.method_cache_lookup_where = [(None, None)] * SIZE
+ if self.config.objspace.std.withmethodcachecounter:
+ ec.method_cache_hits = {}
+ ec.method_cache_misses = {}
return ec
def createframe(self, code, w_globals, closure=None):
Modified: pypy/dist/pypy/objspace/std/test/test_shadowtracking.py
==============================================================================
--- pypy/dist/pypy/objspace/std/test/test_shadowtracking.py (original)
+++ pypy/dist/pypy/objspace/std/test/test_shadowtracking.py Sun Jan 14 12:10:36 2007
@@ -87,3 +87,86 @@
assert a.f() == 42
a.f = lambda : 43
assert a.f() == 43
+
+class AppTestMethodCaching(AppTestShadowTracking):
+ def setup_class(cls):
+ cls.space = gettestobjspace(
+ **{"objspace.std.withmethodcachecounter": True})
+
+ def test_mix_classes(self):
+ import pypymagic
+ class A(object):
+ def f(self):
+ return 42
+ class B(object):
+ def f(self):
+ return 43
+ class C(object):
+ def f(self):
+ return 44
+ l = [A(), B(), C()] * 10
+ pypymagic.reset_method_cache_counter()
+ for i, a in enumerate(l):
+ assert a.f() == 42 + i % 3
+ cache_counter = pypymagic.method_cache_counter("f")
+ print cache_counter
+ assert cache_counter[1] >= 3 # should be (27, 3)
+ assert sum(cache_counter) == 30
+
+ def test_class_that_cannot_be_cached(self):
+ import pypymagic
+ class metatype(type):
+ pass
+ class A(object):
+ __metaclass__ = metatype
+ def f(self):
+ return 42
+
+ class B(object):
+ def f(self):
+ return 43
+ class C(object):
+ def f(self):
+ return 44
+ l = [A(), B(), C()] * 10
+ pypymagic.reset_method_cache_counter()
+ for i, a in enumerate(l):
+ assert a.f() == 42 + i % 3
+ cache_counter = pypymagic.method_cache_counter("f")
+ print cache_counter
+ assert cache_counter[1] >= 2 # should be (18, 2)
+ assert sum(cache_counter) == 20
+
+ def test_change_methods(self):
+ import pypymagic
+ class A(object):
+ def f(self):
+ return 42
+ l = [A()] * 10
+ pypymagic.reset_method_cache_counter()
+ for i, a in enumerate(l):
+ assert a.f() == 42 + i
+ A.f = eval("lambda self: %s" % (42 + i + 1, ))
+ cache_counter = pypymagic.method_cache_counter("f")
+ print cache_counter
+ assert cache_counter == (0, 10)
+
+ def test_subclasses(self):
+ import pypymagic
+ class A(object):
+ def f(self):
+ return 42
+ class B(object):
+ def f(self):
+ return 43
+ class C(A):
+ pass
+ l = [A(), B(), C()] * 10
+ pypymagic.reset_method_cache_counter()
+ for i, a in enumerate(l):
+ assert a.f() == 42 + (i % 3 == 1)
+ cache_counter = pypymagic.method_cache_counter("f")
+ print cache_counter
+ assert cache_counter[1] >= 3 # should be (27, 3)
+ assert sum(cache_counter) == 30
+
Modified: pypy/dist/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/dist/pypy/objspace/std/typeobject.py (original)
+++ pypy/dist/pypy/objspace/std/typeobject.py Sun Jan 14 12:10:36 2007
@@ -276,16 +276,30 @@
return w_value
return w_value
- def lookup(w_self, key):
+ def lookup(w_self, name):
# note that this doesn't call __get__ on the result at all
space = w_self.space
+ if space.config.objspace.std.withmethodcache:
+ return w_self.lookup_where_with_method_cache(name)[1]
+
+ return w_self._lookup(name)
+
+ def lookup_where(w_self, name):
+ space = w_self.space
+ if space.config.objspace.std.withmethodcache:
+ return w_self.lookup_where_with_method_cache(name)
+
+ return w_self._lookup_where(name)
+
+ def _lookup(w_self, key):
+ space = w_self.space
for w_class in w_self.mro_w:
w_value = w_class.getdictvalue_w(space, key)
if w_value is not None:
return w_value
return None
- def lookup_where(w_self, key):
+ def _lookup_where(w_self, key):
# like lookup() but also returns the parent class in which the
# attribute was found
space = w_self.space
@@ -295,6 +309,41 @@
return w_class, w_value
return None, None
+ def lookup_where_with_method_cache(w_self, name):
+ space = w_self.space
+ assert space.config.objspace.std.withmethodcache
+ ec = space.getexecutioncontext()
+ try:
+ frame = ec.framestack.top()
+ position_hash = frame.last_instr ^ id(frame.pycode)
+ except IndexError:
+ position_hash = 0
+ version_tag = w_self.version_tag
+ if version_tag is None:
+ tup = w_self._lookup_where(name)
+ return tup
+ SIZE = space.config.objspace.std.methodcachesize
+ method_hash = (id(version_tag) ^ position_hash ^ hash(name)) % SIZE
+ cached_version_tag = ec.method_cache_versions[method_hash]
+ if cached_version_tag is version_tag:
+ cached_name = ec.method_cache_names[method_hash]
+ if cached_name == name:
+ tup = ec.method_cache_lookup_where[method_hash]
+ if space.config.objspace.std.withmethodcachecounter:
+ ec.method_cache_hits[name] = \
+ ec.method_cache_hits.get(name, 0) + 1
+# print "hit", w_self, name
+ return tup
+ tup = w_self._lookup_where(name)
+ ec.method_cache_versions[method_hash] = version_tag
+ ec.method_cache_names[method_hash] = name
+ ec.method_cache_lookup_where[method_hash] = tup
+ if space.config.objspace.std.withmethodcachecounter:
+ ec.method_cache_misses[name] = \
+ ec.method_cache_misses.get(name, 0) + 1
+# print "miss", w_self, name
+ return tup
+
def check_user_subclass(w_self, w_subtype):
space = w_self.space
if not isinstance(w_subtype, W_TypeObject):
More information about the Pypy-commit
mailing list