Skip to content

Commit

Permalink
Optimizations for ClientCache, especially under PyPy.
Browse files Browse the repository at this point in the history
- make the locked decorator easier to inline by not creating a new closure every time.
- use builtin dicts instead of the pure-python BTrees (only under pypy)

"Transaction",                  AFTER    BEFORE
"Add 3000 Objects",             10014      8777
"Update 3000 Objects",          12199      8805
"Read 3000 Warm Objects",        4294      3699
"Read 3000 Cold Objects",        4264      3676
"Read 3000 Hot Objects",        79764     58732
"Read 3000 Steamin' Objects", 2667283   2574508
  • Loading branch information
jamadden committed May 20, 2015
1 parent 09958e6 commit 48c336d
Showing 1 changed file with 22 additions and 19 deletions.
41 changes: 22 additions & 19 deletions src/ZEO/cache.py
Expand Up @@ -36,6 +36,7 @@
import zc.lockfile
from ZODB.utils import p64, u64, z64
import six
from ._compat import PYPY

logger = logging.getLogger("ZEO.cache")

Expand Down Expand Up @@ -130,21 +131,23 @@
# to the end of the file that the new object can't fit in one
# contiguous chunk, currentofs is reset to ZEC_HEADER_SIZE first.

class locked(object):

def __init__(self, func):
self.func = func

def __get__(self, inst, class_):
if inst is None:
return self
def call(*args, **kw):
inst._lock.acquire()
try:
return self.func(inst, *args, **kw)
finally:
inst._lock.release()
return call
def locked(func):
def _locked_wrapper(inst, *args, **kwargs):
inst._lock.acquire()
try:
return func(inst, *args, **kwargs)
finally:
inst._lock.release()
return _locked_wrapper

# Under PyPy, the available dict specializations perform significantly
# better (faster) than the pure-Python BTree implementation. They may
# use less memory too. And we don't require any of the special BTree features...
_current_index_type = ZODB.fsIndex.fsIndex if not PYPY else dict
_noncurrent_index_type = BTrees.LOBTree.LOBTree if not PYPY else dict
# ...except at this leaf level
_noncurrent_bucket_type = BTrees.LLBTree.LLBucket

class ClientCache(object):
"""A simple in-memory cache."""
Expand Down Expand Up @@ -173,13 +176,13 @@ def __init__(self, path=None, size=200*1024**2, rearrange=.8):
self._len = 0

# {oid -> pos}
self.current = ZODB.fsIndex.fsIndex()
self.current = _current_index_type()

# {oid -> {tid->pos}}
# Note that caches in the wild seem to have very little non-current
# data, so this would seem to have little impact on memory consumption.
# I wonder if we even need to store non-current data in the cache.
self.noncurrent = BTrees.LOBTree.LOBTree()
self.noncurrent = _noncurrent_index_type()

# tid for the most recent transaction we know about. This is also
# stored near the start of the file.
Expand Down Expand Up @@ -276,8 +279,8 @@ def _initfile(self, fsize):
# Remember the location of the largest free block. That seems a
# decent place to start currentofs.

self.current = ZODB.fsIndex.fsIndex()
self.noncurrent = BTrees.LOBTree.LOBTree()
self.current = _current_index_type()
self.noncurrent = _noncurrent_index_type()
l = 0
last = ofs = ZEC_HEADER_SIZE
first_free_offset = 0
Expand Down Expand Up @@ -369,7 +372,7 @@ def _initfile(self, fsize):
def _set_noncurrent(self, oid, tid, ofs):
noncurrent_for_oid = self.noncurrent.get(u64(oid))
if noncurrent_for_oid is None:
noncurrent_for_oid = BTrees.LLBTree.LLBucket()
noncurrent_for_oid = _noncurrent_bucket_type()
self.noncurrent[u64(oid)] = noncurrent_for_oid
noncurrent_for_oid[u64(tid)] = ofs

Expand Down

0 comments on commit 48c336d

Please sign in to comment.