summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-01-23 10:57:00 -0600
committerJason Madden <jamadden@gmail.com>2020-01-27 07:40:36 -0600
commit7f6638cf5113f9f6ecb51116bbb72fdbc3f3ce06 (patch)
tree4efe02165739fcb1854833815c822f80712c96bb /src
parentab0466e56c6328407c3839a3a392cb127dbdb282 (diff)
downloadzope-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.c8
-rw-r--r--src/zope/interface/interface.py34
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?