diff options
| author | Jason Madden <jamadden@gmail.com> | 2020-01-23 10:57:00 -0600 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2020-01-27 07:40:36 -0600 |
| commit | 7f6638cf5113f9f6ecb51116bbb72fdbc3f3ce06 (patch) | |
| tree | 4efe02165739fcb1854833815c822f80712c96bb /src | |
| parent | ab0466e56c6328407c3839a3a392cb127dbdb282 (diff) | |
| download | zope-interface-7f6638cf5113f9f6ecb51116bbb72fdbc3f3ce06.tar.gz | |
Specifications with no dependents are common (4700 out of 7000 in this example), so avoid allocating a WeakKeyDictionary until needed.
This saves another 2% or so.
Diffstat (limited to 'src')
| -rw-r--r-- | src/zope/interface/_zope_interface_coptimizations.c | 8 | ||||
| -rw-r--r-- | src/zope/interface/interface.py | 34 |
2 files changed, 29 insertions, 13 deletions
diff --git a/src/zope/interface/_zope_interface_coptimizations.c b/src/zope/interface/_zope_interface_coptimizations.c index e308cbd..4794e3f 100644 --- a/src/zope/interface/_zope_interface_coptimizations.c +++ b/src/zope/interface/_zope_interface_coptimizations.c @@ -272,7 +272,7 @@ typedef struct { The remainder aren't used in C code but must be stored here to prevent instance layout conflicts. */ - PyObject* dependents; + PyObject* _dependents; PyObject* _bases; PyObject* _v_attrs; PyObject* __iro__; @@ -287,7 +287,7 @@ static int Spec_traverse(Spec* self, visitproc visit, void* arg) { Py_VISIT(self->_implied); - Py_VISIT(self->dependents); + Py_VISIT(self->_dependents); Py_VISIT(self->_v_attrs); Py_VISIT(self->__iro__); Py_VISIT(self->__sro__); @@ -298,7 +298,7 @@ static int Spec_clear(Spec* self) { Py_CLEAR(self->_implied); - Py_CLEAR(self->dependents); + Py_CLEAR(self->_dependents); Py_CLEAR(self->_v_attrs); Py_CLEAR(self->__iro__); Py_CLEAR(self->__sro__); @@ -408,7 +408,7 @@ static struct PyMethodDef Spec_methods[] = { static PyMemberDef Spec_members[] = { {"_implied", T_OBJECT_EX, offsetof(Spec, _implied), 0, ""}, - {"dependents", T_OBJECT_EX, offsetof(Spec, dependents), 0, ""}, + {"_dependents", T_OBJECT_EX, offsetof(Spec, _dependents), 0, ""}, {"_bases", T_OBJECT_EX, offsetof(Spec, _bases), 0, ""}, {"_v_attrs", T_OBJECT_EX, offsetof(Spec, _v_attrs), 0, ""}, {"__iro__", T_OBJECT_EX, offsetof(Spec, __iro__), 0, ""}, diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py index d3074c5..8317a7f 100644 --- a/src/zope/interface/interface.py +++ b/src/zope/interface/interface.py @@ -111,7 +111,7 @@ class SpecificationBase(object): # Things used here. '_implied', # Things used in Specification. - 'dependents', + '_dependents', '_bases', '_v_attrs', '__iro__', @@ -203,7 +203,15 @@ class Specification(SpecificationBase): providedBy = SpecificationBase.providedBy def __init__(self, bases=()): - self.dependents = weakref.WeakKeyDictionary() + # There are many leaf interfaces with no dependents, + # and a few with very many. It's a heavily left-skewed + # distribution. In a survey of Plone and Zope related packages + # that loaded 2245 InterfaceClass objects and 2235 ClassProvides + # instances, there were a total of 7000 Specification objects created. + # 4700 had 0 dependents, 1400 had 1, 382 had 2 and so on. Only one + # for <type> had 1664. So there's savings to be had deferring + # the creation of dependents. + self._dependents = None # type: weakref.WeakKeyDictionary self._bases = () self._implied = {} self._v_attrs = None @@ -212,17 +220,26 @@ class Specification(SpecificationBase): self.__bases__ = tuple(bases) + @property + def dependents(self): + if self._dependents is None: + self._dependents = weakref.WeakKeyDictionary() + return self._dependents + def subscribe(self, dependent): - self.dependents[dependent] = self.dependents.get(dependent, 0) + 1 + self._dependents[dependent] = self.dependents.get(dependent, 0) + 1 def unsubscribe(self, dependent): - n = self.dependents.get(dependent, 0) - 1 + try: + n = self._dependents[dependent] + except TypeError: + raise KeyError(dependent) + n -= 1 if not n: del self.dependents[dependent] - elif n > 0: - self.dependents[dependent] = n else: - raise KeyError(dependent) + assert n > 0 + self.dependents[dependent] = n def __setBases(self, bases): # Remove ourselves as a dependent of our old bases @@ -267,7 +284,7 @@ class Specification(SpecificationBase): implied[ancestor] = () # Now, advise our dependents of change: - for dependent in tuple(self.dependents.keys()): + for dependent in tuple(self._dependents.keys() if self._dependents else ()): dependent.changed(originally_changed) # Just in case something called get() at some point @@ -285,7 +302,6 @@ class Specification(SpecificationBase): seen[interface] = 1 yield interface - def extends(self, interface, strict=True): """Does the specification extend the given interface? |
