Commit 38d86ef7 authored by Jason Madden's avatar Jason Madden

All tests pass. Lifetime issues worked out, though probably not optimally.

Performance is way up again, beating CPython on half of the tests:

"Transaction",                mysql
"Add 3000 Objects",              8259
"Update 3000 Objects",           9454
"Read 3000 Warm Objects",        5460
"Read 3000 Cold Objects",        5454
"Read 3000 Hot Objects",        24943
"Read 3000 Steamin' Objects", 1099616

Still needs some GC work to get all the ZODB tests to pass like they do under zopefoundation/persistent#20
parent c0ef0335
...@@ -487,9 +487,12 @@ class Persistent(object): ...@@ -487,9 +487,12 @@ class Persistent(object):
jar = oga(self, '_Persistent__jar') jar = oga(self, '_Persistent__jar')
if jar is None: if jar is None:
return return
myring = oga(self, '_Persistent__ring') # XXX: If we bail here, a number of persistence tests
if ring is None: # fail. However, the C implementation only does this if
return # it's already in the ring (maybe somebody does that earlier?)
#myring = oga(self, '_Persistent__ring')
#if myring is None or not myring.r_next:
# return
oid = oga(self, '_Persistent__oid') oid = oga(self, '_Persistent__oid')
if oid is None: if oid is None:
return return
...@@ -505,7 +508,7 @@ class Persistent(object): ...@@ -505,7 +508,7 @@ class Persistent(object):
# that at this level, all we can do is catch it. # that at this level, all we can do is catch it.
# The AttributeError arises in ZODB test cases # The AttributeError arises in ZODB test cases
try: try:
ring.move_to_head(jar._cache.ring_home, myring) jar._cache.mru(oid)
except (AttributeError,KeyError): except (AttributeError,KeyError):
pass pass
...@@ -520,10 +523,6 @@ class Persistent(object): ...@@ -520,10 +523,6 @@ class Persistent(object):
if cache is not None: if cache is not None:
return cache.get(oid) is self return cache.get(oid) is self
def __del__(self):
if self._p_is_in_cache():
ring.del_(self._Persistent__ring)
def _estimated_size_in_24_bits(value): def _estimated_size_in_24_bits(value):
if value > 1073741696: if value > 1073741696:
return 16777215 return 16777215
......
...@@ -103,6 +103,7 @@ class PickleCache(object): ...@@ -103,6 +103,7 @@ class PickleCache(object):
self.persistent_classes = {} self.persistent_classes = {}
self.data = weakref.WeakValueDictionary() self.data = weakref.WeakValueDictionary()
self.ring_home = ring.CPersistentRingHead() self.ring_home = ring.CPersistentRingHead()
self.ring_nodes = set()
self.cache_size_bytes = cache_size_bytes self.cache_size_bytes = cache_size_bytes
# IPickleCache API # IPickleCache API
...@@ -163,9 +164,10 @@ class PickleCache(object): ...@@ -163,9 +164,10 @@ class PickleCache(object):
self.data[oid] = value self.data[oid] = value
_gc_monitor(value) _gc_monitor(value)
node = ring.CPersistentRing(value) node = ring.CPersistentRing(value)
value._Persistent__ring = node object.__setattr__(value, '_Persistent__ring', node)
if _OGA(value, '_p_state') != GHOST: if _OGA(value, '_p_state') != GHOST:
ring.add(self.ring_home, node) ring.add(self.ring_home, node)
self.ring_nodes.add(node)
self.non_ghost_count += 1 self.non_ghost_count += 1
def __delitem__(self, oid): def __delitem__(self, oid):
...@@ -198,12 +200,18 @@ class PickleCache(object): ...@@ -198,12 +200,18 @@ class PickleCache(object):
return False # marker return for tests return False # marker return for tests
value = self.data[oid] value = self.data[oid]
id_value = id(value) node = _OGA(value, '_Persistent__ring')
if node is None: # tests
return
was_in_ring = bool(value._Persistent__ring.r_next) was_in_ring = bool(node.r_next)
ring.move_to_head(self.ring_home, value._Persistent__ring) if not was_in_ring:
if not was_in_ring and _OGA(value, '_p_state') != GHOST: if _OGA(value, '_p_state') != GHOST:
self.non_ghost_count += 1 ring.add(self.ring_home, node)
self.ring_nodes.add(node)
self.non_ghost_count += 1
else:
ring.move_to_head(self.ring_home, node)
def ringlen(self): def ringlen(self):
""" See IPickleCache. """ See IPickleCache.
...@@ -350,14 +358,12 @@ class PickleCache(object): ...@@ -350,14 +358,12 @@ class PickleCache(object):
def _sweep(self, target, target_size_bytes=0): def _sweep(self, target, target_size_bytes=0):
# lock # lock
ejected = 0 ejected = 0
for here in ring.iteritems(self.ring_home): for here in list(ring.iteritems(self.ring_home)):
value = ring.get_object(here)
if value is None:
continue
here = here.r_next
if self.non_ghost_count <= target and (self.total_estimated_size <= target_size_bytes or not target_size_bytes): if self.non_ghost_count <= target and (self.total_estimated_size <= target_size_bytes or not target_size_bytes):
break break
value = ring.get_object(here)
here = here.r_next
if value._p_state == UPTODATE: if value._p_state == UPTODATE:
# The C implementation will only evict things that are specifically # The C implementation will only evict things that are specifically
...@@ -410,3 +416,4 @@ class PickleCache(object): ...@@ -410,3 +416,4 @@ class PickleCache(object):
def _remove_from_ring(self, value): def _remove_from_ring(self, value):
if value._Persistent__ring.r_next: if value._Persistent__ring.r_next:
ring.del_(value._Persistent__ring) ring.del_(value._Persistent__ring)
self.ring_nodes.discard(value._Persistent__ring)
...@@ -45,6 +45,9 @@ class CPersistentRing(object): ...@@ -45,6 +45,9 @@ class CPersistentRing(object):
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.node, name) return getattr(self.node, name)
def get_object(self):
return get_object(self.node)
def CPersistentRingHead(): def CPersistentRingHead():
head = CPersistentRing() head = CPersistentRing()
head.node.r_next = head.node head.node.r_next = head.node
......
...@@ -979,8 +979,9 @@ class PickleCacheTests(unittest.TestCase): ...@@ -979,8 +979,9 @@ class PickleCacheTests(unittest.TestCase):
cache[p._p_oid] = p cache[p._p_oid] = p
self.assertEqual(cache.cache_non_ghost_count, 1) self.assertEqual(cache.cache_non_ghost_count, 1)
self.assertEqual(cache.ring[0], p) from ..ring import get_object,ffi
cache.ring[0] = None self.assertEqual(get_object(cache.ring_home.r_next), p)
cache.ring_home.r_next.object = ffi.NULL
# Nothing to test, just that it doesn't break # Nothing to test, just that it doesn't break
cache._invalidate(p._p_oid) cache._invalidate(p._p_oid)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment