diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js
index 3173d80c7..2c10e6ffc 100644
--- a/app/assets/javascripts/discourse.js
+++ b/app/assets/javascripts/discourse.js
@@ -220,7 +220,6 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
     Discourse.KeyValueStore.init("discourse_", Discourse.MessageBus);
 
     // Developer specific functions
-    Discourse.Development.setupProbes();
     Discourse.Development.observeLiveChanges();
     Discourse.subscribeUserToNotifications();
   }
diff --git a/app/assets/javascripts/discourse/components/development.js b/app/assets/javascripts/discourse/components/development.js
index 675010e7b..4040a9da6 100644
--- a/app/assets/javascripts/discourse/components/development.js
+++ b/app/assets/javascripts/discourse/components/development.js
@@ -7,77 +7,6 @@
 **/
 Discourse.Development = {
 
-
-  /**
-    Set up probes for performance measurements.
-
-    @method setupProbes
-  **/
-  setupProbes: function() {
-
-    // Don't probe if we don't have a console
-    if (typeof console === "undefined" || console === null) return;
-
-    var topLevel = function(fn, name) {
-      return window.probes.measure(fn, {
-        name: name,
-
-        before: function(data, owner, args) {
-          if (owner) {
-            return window.probes.clear();
-          }
-        },
-
-        after: function(data, owner, args) {
-
-          if (typeof console === "undefined") return;
-          if (console === null) return;
-
-          var f, n, v;
-          if (owner && data.time > 10) {
-
-            f = function(name, data) {
-              if (data && data.count) return name + " - " + data.count + " calls " + ((data.time + 0.0).toFixed(2)) + "ms";
-            };
-
-            if (console.group) {
-              console.group(f(name, data));
-            } else {
-              console.log("");
-              console.log(f(name, data));
-            }
-
-            var ary = [];
-            for (n in window.probes) {
-              v = window.probes[n];
-              if (n === name || v.time < 1) continue;
-              ary.push({ k: n, v: v });
-            }
-            ary.sort(function(x,y) {
-              x = x.v && x.v.time ? -x.v.time : 0;
-              y = y.v && y.v.time ? -y.v.time : 0;
-              return x > y;
-            });
-            _.each(ary, function(item,idx) {
-              var output = f("" + item.k, item.v);
-              if (output) {
-                console.log(output);
-              }
-            });
-
-            if (console.group) {
-              console.groupEnd();
-            }
-            window.probes.clear();
-          }
-        }
-      });
-    };
-
-    Discourse.URL.routeTo = topLevel(Discourse.URL.routeTo, "Discourse.URL.routeTo");
-    Ember.run.backburner.end = topLevel(Ember.run.backburner.end, "Ember.run.backburner.end");
-  },
-
   /**
     Use the message bus for live reloading of components for faster development.
 
diff --git a/app/assets/javascripts/external/ember-renderspeed.js b/app/assets/javascripts/external/ember-renderspeed.js
new file mode 100644
index 000000000..8df4fa4a9
--- /dev/null
+++ b/app/assets/javascripts/external/ember-renderspeed.js
@@ -0,0 +1,103 @@
+//
+//  ember-renderspeed
+//
+//  Include this script if you want to instrument your rendering speed in your Ember
+//  applications.
+//
+//  https://github.com/eviltrout/ember-renderspeed
+//
+(function () {
+
+  /**
+    Used for assembling a tree of render calls so they can be grouped and displayed
+    nicely afterwards.
+
+    @class ProfileNode
+  **/
+  var ProfileNode = Ember.Object.extend({
+
+    init: function() {
+      this.set('children', []);
+    },
+
+    /**
+      A string description of this node. If we have a template name we display that
+      too.
+
+      @property description
+    **/
+    description: function() {
+      var result = "Rendered ";
+      if (this.get('payload.template')) {
+        result += "'" + this.get('payload.template') + "' ";
+      }
+
+      if (this.get('payload.object')) {
+        result += this.get('payload.object').toString() + " ";
+      }
+      result += (Math.round(this.get('time') * 100) / 100).toString() + "ms";
+      return result;
+    }.property('time', 'payload.template', 'payload.object'),
+
+    time: function() {
+      return this.get('end') - this.get('start');
+    }.property('start', 'end'),
+
+    /**
+      Adds a child node underneath this node. It also creates a reference between
+      the child and the parent.
+
+      @method addChild
+      @param {ProfileNode} node the node we want as a child
+    **/
+    addChild: function(node) {
+      node.set('parent', this);
+      this.get('children').pushObject(node);
+    },
+
+    /**
+      Logs this node and any children to the developer console, grouping appropriately
+      for easy drilling down.
+
+      @method log
+    **/
+    log: function() {
+      if ((!console) || (!console.groupCollapsed)) { return; }
+
+      // We don't care about really fast renders
+      if (this.get('time') < 1) { return; }
+
+      console.groupCollapsed(this.get('description'));
+      this.get('children').forEach(function (c) {
+        c.log();
+      });
+      console.groupEnd();
+    }
+  });
+
+
+  // Set up our instrumentation of Ember below
+  Ember.subscribe("render", {
+    depth: null,
+
+    before: function(name, timestamp, payload) {
+      var node = ProfileNode.create({start: timestamp, payload: payload});
+
+      if (this.depth) { this.depth.addChild(node); }
+      this.depth = node;
+
+      return node;
+    },
+
+    after: function(name, timestamp, payload, profileNode) {
+      this.depth = profileNode.get('parent');
+      profileNode.set('end', timestamp);
+
+      if (!this.depth) {
+        profileNode.log();
+      }
+    }
+  });
+
+})();
+