diff --git a/django/db/models/base.py b/django/db/models/base.py
index 71fd1f7..3e770fc 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -277,8 +277,8 @@ class Model(object):
     _deferred = False
 
     def __init__(self, *args, **kwargs):
-        signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
-
+        if self._meta.has_pre_init_listeners or signals.pre_init.has_global_listeners:
+            signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
         # Set up the storage for instance state
         self._state = ModelState()
 
@@ -367,7 +367,8 @@ 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)
+        if self._meta.has_post_init_listeners or signals.post_init.has_global_listeners:
+            signals.post_init.send(sender=self.__class__, instance=self)
 
     def __repr__(self):
         try:
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 6f0f406..fc13cee 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -54,6 +54,13 @@ class Options(object):
         # from *other* models. Needed for some admin checks. Internal use only.
         self.related_fkey_lookups = []
 
+        # These variables are here to make it possible to skip pre_init
+        # and post_init signals when there are no listeners for the model.
+        # This can save up to 2/3 of the time used in Model __init__.
+        # The variables are set by pre_init's and post_init's connect methods.
+        self.has_pre_init_listeners = False
+        self.has_post_init_listeners = False
+
     def contribute_to_class(self, cls, name):
         from django.db import connection
         from django.db.backends.util import truncate_name
diff --git a/django/db/models/signals.py b/django/db/models/signals.py
index 48872e7..5b9065b 100644
--- a/django/db/models/signals.py
+++ b/django/db/models/signals.py
@@ -2,8 +2,30 @@ from django.dispatch import Signal
 
 class_prepared = Signal(providing_args=["class"])
 
-pre_init = Signal(providing_args=["instance", "args", "kwargs"])
-post_init = Signal(providing_args=["instance"])
+# Pre and post init signals have subclasses with some optimizations. The subclass
+# connect methods make sure that Model __init__ knows about somebody listening. We
+# don't want to pay the overhead of sending these signals if nobody is listening, as 
+# model initialization is a common operation.
+class PreInitSignal(Signal):
+    has_global_listeners = False
+    def connect(self, *args, **kwargs):
+        if 'sender' in kwargs:
+            kwargs['sender']._meta.has_pre_init_listeners = True
+        else:
+            self.has_global_listeners = True
+        return super(PreInitSignal, self).connect(*args, **kwargs)
+    
+class PostInitSignal(Signal):
+    has_global_listeners = False
+    def connect(self, *args, **kwargs):
+        if 'sender' in kwargs:
+            kwargs['sender']._meta.has_post_init_listeners = True
+        else:
+            self.has_global_listeners = True
+        return super(PostInitSignal, self).connect(*args, **kwargs)
+
+pre_init = PreInitSignal(providing_args=["instance", "args", "kwargs"])
+post_init = PostInitSignal(providing_args=["instance"])
 
 pre_save = Signal(providing_args=["instance", "raw", "using"])
 post_save = Signal(providing_args=["instance", "raw", "created", "using"])
diff --git a/tests/modeltests/signals/tests.py b/tests/modeltests/signals/tests.py
index 9b8bce0..5503786 100644
--- a/tests/modeltests/signals/tests.py
+++ b/tests/modeltests/signals/tests.py
@@ -29,6 +29,8 @@ class SignalTests(TestCase):
         # Save up the number of connected signals so that we can check at the
         # end that all the signals we register get properly unregistered (#9989)
         pre_signals = (
+            len(signals.pre_init.receivers),
+            len(signals.post_init.receivers),
             len(signals.pre_save.receivers),
             len(signals.post_save.receivers),
             len(signals.pre_delete.receivers),
@@ -67,8 +69,21 @@ class SignalTests(TestCase):
         def pre_save_decorator_sender_test(signal, sender, instance, **kwargs):
             data.append(instance)
 
+        def pre_init_test(signal, sender, **kwargs):
+            data.append(sender)
+        signals.pre_init.connect(pre_init_test, sender=Person)
+
+        def post_init_test(signal, sender, instance, **kwargs):
+            data.append((instance, instance.pk is None))
+        signals.post_init.connect(post_init_test, sender=Person)
+
+        def pre_init_all_models_test(signal, sender, **kwargs):
+            data.append((sender, 'GLOBAL'))
+        signals.pre_init.connect(pre_init_all_models_test)
+
         p1 = Person(first_name="John", last_name="Smith")
-        self.assertEqual(data, [])
+        self.assertEqual(data, [Person, (Person, 'GLOBAL'), (p1, True)])
+        data[:] = []
         p1.save()
         self.assertEqual(data, [
             (p1, False),
@@ -88,6 +103,8 @@ class SignalTests(TestCase):
 
         # Car signal (sender defined)
         c1 = Car(make="Volkswagon", model="Passat")
+        self.assertEqual(data, [(Car, 'GLOBAL')])
+        data[:] = []
         c1.save()
         self.assertEqual(data, [
             (c1, False),
@@ -114,6 +131,8 @@ class SignalTests(TestCase):
         data[:] = []
 
         p2 = Person(first_name="James", last_name="Jones")
+        self.assertEqual(data, [Person, (Person, 'GLOBAL'), (p2, True)])
+        data[:] = []
         p2.id = 99999
         p2.save()
         self.assertEqual(data, [
@@ -137,6 +156,11 @@ class SignalTests(TestCase):
             (p2, False),
             (p2, False)
         ])
+        data[:] = []
+
+        p3 = Person.objects.all()[0]
+        self.assertEqual(data, [Person, (Person, 'GLOBAL'), (p3, False)])
+        data = data[:]
 
         self.assertQuerysetEqual(
             Person.objects.all(), [
@@ -145,6 +169,13 @@ class SignalTests(TestCase):
             unicode
         )
 
+        signals.pre_init.disconnect(pre_init_test, sender=Person)
+        signals.pre_init.disconnect(pre_init_all_models_test)
+        # We need to do a dirty trick here - connecting to a global pre or post init
+        # signal sets a state for the whole Django project - we want to get rid of 
+        # that state.
+        signals.pre_init.has_global_listeners = False
+        signals.post_init.disconnect(post_init_test, sender=Person)
         signals.post_delete.disconnect(post_delete_test)
         signals.pre_delete.disconnect(pre_delete_test)
         signals.post_save.disconnect(post_save_test)
@@ -154,6 +185,8 @@ class SignalTests(TestCase):
 
         # Check that all our signals got disconnected properly.
         post_signals = (
+            len(signals.pre_init.receivers),
+            len(signals.post_init.receivers),
             len(signals.pre_save.receivers),
             len(signals.post_save.receivers),
             len(signals.pre_delete.receivers),
