Ticket #16679: fast-signals2.diff

File fast-signals2.diff, 8.5 KB (added by akaariai, 4 years ago)

sender -> receivers cache

  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index 71fd1f7..730e52f 100644
    a b from django.db.models.query_utils import DeferredAttribute 
    1818from django.db.models.deletion import Collector
    1919from django.db.models.options import Options
    2020from django.db.models import signals
     21from django.db.models.signals import pre_init, post_init
    2122from django.db.models.loading import register_models, get_model
    2223from django.utils.translation import ugettext_lazy as _
    2324from django.utils.functional import curry
    class Model(object): 
    277278    _deferred = False
    278279
    279280    def __init__(self, *args, **kwargs):
    280         signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
     281        pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
    281282
    282283        # Set up the storage for instance state
    283284        self._state = ModelState()
    class Model(object): 
    367368            if kwargs:
    368369                raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
    369370        super(Model, self).__init__()
    370         signals.post_init.send(sender=self.__class__, instance=self)
     371        post_init.send(sender=self.__class__, instance=self)
    371372
    372373    def __repr__(self):
    373374        try:
  • django/db/models/signals.py

    diff --git a/django/db/models/signals.py b/django/db/models/signals.py
    index 48872e7..65e8f23 100644
    a b from django.dispatch import Signal 
    22
    33class_prepared = Signal(providing_args=["class"])
    44
    5 pre_init = Signal(providing_args=["instance", "args", "kwargs"])
    6 post_init = Signal(providing_args=["instance"])
     5pre_init = Signal(providing_args=["instance", "args", "kwargs"], use_caching=True)
     6post_init = Signal(providing_args=["instance"], use_caching=True)
    77
    8 pre_save = Signal(providing_args=["instance", "raw", "using"])
    9 post_save = Signal(providing_args=["instance", "raw", "created", "using"])
     8pre_save = Signal(providing_args=["instance", "raw", "using"], use_caching=True)
     9post_save = Signal(providing_args=["instance", "raw", "created", "using"], use_caching=True)
    1010
    11 pre_delete = Signal(providing_args=["instance", "using"])
    12 post_delete = Signal(providing_args=["instance", "using"])
     11pre_delete = Signal(providing_args=["instance", "using"], use_caching=True)
     12post_delete = Signal(providing_args=["instance", "using"], use_caching=True)
    1313
    14 post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"])
     14post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"], use_caching=True)
    1515
    16 m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"])
     16m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True)
  • django/dispatch/dispatcher.py

    diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
    index ed9da57..bd9e461 100644
    a b def _make_id(target): 
    99    if hasattr(target, 'im_func'):
    1010        return (id(target.im_self), id(target.im_func))
    1111    return id(target)
     12NONE_ID = _make_id(None)
    1213
    1314class Signal(object):
    1415    """
    class Signal(object): 
    2021            { receriverkey (id) : weakref(receiver) }
    2122    """
    2223   
    23     def __init__(self, providing_args=None):
     24    def __init__(self, providing_args=None, use_caching=False):
    2425        """
    2526        Create a new signal.
    2627       
    class Signal(object): 
    3132        if providing_args is None:
    3233            providing_args = []
    3334        self.providing_args = set(providing_args)
     35        self.use_caching = use_caching
     36        # For convenience we create empty caches even if they are not used
     37        self.sender_no_receivers_cache = set()
     38        self.sender_receivers_cache = {}
    3439        self.lock = threading.Lock()
    3540
    3641    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
    class Signal(object): 
    107112            else:
    108113                self.receivers.append((lookup_key, receiver))
    109114        finally:
     115            if self.use_caching:
     116                self.sender_no_receivers_cache = set()
     117                self.sender_receivers_cache = {}
    110118            self.lock.release()
    111119
    112120    def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
    class Signal(object): 
    144152                    del self.receivers[index]
    145153                    break
    146154        finally:
     155            if self.use_caching:
     156               self.sender_no_receivers_cache = set()
     157               self.sender_receivers_cache = {}
    147158            self.lock.release()
    148159
    149160    def send(self, sender, **named):
    class Signal(object): 
    165176        Returns a list of tuple pairs [(receiver, response), ... ].
    166177        """
    167178        responses = []
    168         if not self.receivers:
     179        if not self.receivers or (self.use_caching and sender in self.sender_no_receivers_cache):
    169180            return responses
    170181
    171         for receiver in self._live_receivers(_make_id(sender)):
     182        for receiver in self._live_receivers(sender):
    172183            response = receiver(signal=self, sender=sender, **named)
    173184            responses.append((receiver, response))
    174185        return responses
    class Signal(object): 
    197208        receiver.
    198209        """
    199210        responses = []
    200         if not self.receivers:
     211        if not self.receivers or (self.use_caching and sender in self.sender_no_receivers_cache):
    201212            return responses
    202213
    203214        # Call each receiver with whatever arguments it can accept.
    204215        # Return a list of tuple pairs [(receiver, response), ... ].
    205         for receiver in self._live_receivers(_make_id(sender)):
     216        for receiver in self._live_receivers(sender):
    206217            try:
    207218                response = receiver(signal=self, sender=sender, **named)
    208219            except Exception, err:
    class Signal(object): 
    211222                responses.append((receiver, response))
    212223        return responses
    213224
    214     def _live_receivers(self, senderkey):
     225    def _live_receivers(self, sender):
    215226        """
    216227        Filter sequence of receivers to get resolved, live receivers.
    217228
    218229        This checks for weak references and resolves them, then returning only
    219230        live receivers.
    220231        """
    221         none_senderkey = _make_id(None)
    222         receivers = []
    223 
    224         for (receiverkey, r_senderkey), receiver in self.receivers:
    225             if r_senderkey == none_senderkey or r_senderkey == senderkey:
    226                 if isinstance(receiver, WEAKREF_TYPES):
    227                     # Dereference the weak reference.
    228                     receiver = receiver()
    229                     if receiver is not None:
    230                         receivers.append(receiver)
    231                 else:
     232        receivers = None
     233        if self.use_caching:
     234            receivers = self.sender_receivers_cache.get(sender)
     235        if receivers is None:
     236            none_senderkey = NONE_ID
     237            senderkey = _make_id(sender)
     238            receivers = []
     239            if self.use_caching:
     240                self.lock.acquire()
     241            for (receiverkey, r_senderkey), receiver in self.receivers:
     242                if r_senderkey == none_senderkey or r_senderkey == senderkey:
    232243                    receivers.append(receiver)
    233         return receivers
     244            if self.use_caching:
     245                if not receivers:
     246                    self.sender_no_receivers_cache.add(sender)
     247                # Note, we must cache the weakref versions.
     248                self.sender_receivers_cache[sender] = receivers
     249                self.lock.release()
     250        non_weak_receivers = []
     251        for receiver in receivers:
     252            if isinstance(receiver, WEAKREF_TYPES):
     253                # Dereference the weak reference.
     254                receiver = receiver()
     255                if receiver is not None:
     256                    non_weak_receivers.append(receiver)
     257            else:
     258                non_weak_receivers.append(receiver)
     259        return non_weak_receivers
    234260
    235261    def _remove_receiver(self, receiver):
    236262        """
    237263        Remove dead receivers from connections.
    238264        """
    239 
    240265        self.lock.acquire()
    241266        try:
    242267            to_remove = []
    class Signal(object): 
    251276                    if r_key == key:
    252277                        del self.receivers[last_idx-idx]
    253278        finally:
     279            if self.use_caching:
     280                self.sender_no_receivers_cache = set()
     281                self.sender_receivers_cache = {}
    254282            self.lock.release()
    255283
    256284
Back to Top