diff --git a/django/db/models/base.py b/django/db/models/base.py
index 71fd1f7..730e52f 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -18,6 +18,7 @@ from django.db.models.query_utils import DeferredAttribute
 from django.db.models.deletion import Collector
 from django.db.models.options import Options
 from django.db.models import signals
+from django.db.models.signals import pre_init, post_init
 from django.db.models.loading import register_models, get_model
 from django.utils.translation import ugettext_lazy as _
 from django.utils.functional import curry
@@ -277,7 +278,7 @@ class Model(object):
     _deferred = False
 
     def __init__(self, *args, **kwargs):
-        signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
+        pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
 
         # Set up the storage for instance state
         self._state = ModelState()
@@ -367,7 +368,7 @@ class Model(object):
             if kwargs:
                 raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
         super(Model, self).__init__()
-        signals.post_init.send(sender=self.__class__, instance=self)
+        post_init.send(sender=self.__class__, instance=self)
 
     def __repr__(self):
         try:
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index ed9da57..989d86d 100644
--- a/django/dispatch/dispatcher.py
+++ b/django/dispatch/dispatcher.py
@@ -31,6 +31,7 @@ class Signal(object):
         if providing_args is None:
             providing_args = []
         self.providing_args = set(providing_args)
+        self.sender_no_receivers_cache = set()
         self.lock = threading.Lock()
 
     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
@@ -107,6 +108,7 @@ class Signal(object):
             else:
                 self.receivers.append((lookup_key, receiver))
         finally:
+            self.sender_no_receivers_cache = set()
             self.lock.release()
 
     def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
@@ -144,6 +146,7 @@ class Signal(object):
                     del self.receivers[index]
                     break
         finally:
+            self.sender_no_receivers_cache = set()
             self.lock.release()
 
     def send(self, sender, **named):
@@ -165,10 +168,10 @@ class Signal(object):
         Returns a list of tuple pairs [(receiver, response), ... ].
         """
         responses = []
-        if not self.receivers:
+        if sender in self.sender_no_receivers_cache:
             return responses
 
-        for receiver in self._live_receivers(_make_id(sender)):
+        for receiver in self._live_receivers(sender):
             response = receiver(signal=self, sender=sender, **named)
             responses.append((receiver, response))
         return responses
@@ -197,12 +200,12 @@ class Signal(object):
         receiver.
         """
         responses = []
-        if not self.receivers:
+        if sender in self.sender_no_receivers_cache:
             return responses
 
         # Call each receiver with whatever arguments it can accept.
         # Return a list of tuple pairs [(receiver, response), ... ].
-        for receiver in self._live_receivers(_make_id(sender)):
+        for receiver in self._live_receivers(sender):
             try:
                 response = receiver(signal=self, sender=sender, **named)
             except Exception, err:
@@ -211,7 +214,7 @@ class Signal(object):
                 responses.append((receiver, response))
         return responses
 
-    def _live_receivers(self, senderkey):
+    def _live_receivers(self, sender):
         """
         Filter sequence of receivers to get resolved, live receivers.
 
@@ -219,8 +222,17 @@ class Signal(object):
         live receivers.
         """
         none_senderkey = _make_id(None)
+        senderkey = _make_id(sender)
         receivers = []
-
+        # Here is a trick which I believe is safe, but I haven't actually
+        # proved it. We store a reference to current sender_no_receivers_cache
+        # If there are modifications to self.receivers, then the
+        # modifier will create a new object for self.sender_no_receivers.
+        # Thus, if we decide to store something in local_sender_no_receivers
+        # cache using stale self.receivers info, there is no harm, because
+        # the reference itself is stale! Of course, this needs a more 
+        # convincing comment, or maybe a proof... 
+        local_sender_no_receivers = self.sender_no_receivers_cache
         for (receiverkey, r_senderkey), receiver in self.receivers:
             if r_senderkey == none_senderkey or r_senderkey == senderkey:
                 if isinstance(receiver, WEAKREF_TYPES):
@@ -230,13 +242,14 @@ class Signal(object):
                         receivers.append(receiver)
                 else:
                     receivers.append(receiver)
+        if not receivers:
+            local_sender_no_receivers.add(sender)
         return receivers
 
     def _remove_receiver(self, receiver):
         """
         Remove dead receivers from connections.
         """
-
         self.lock.acquire()
         try:
             to_remove = []
@@ -251,6 +264,7 @@ class Signal(object):
                     if r_key == key:
                         del self.receivers[last_idx-idx]
         finally:
+            self.sender_no_receivers_cache = set()
             self.lock.release()
 
 
