From d677480f9b9ffee39e7a15e4d58e1303b1d87fcf Mon Sep 17 00:00:00 2001 From: Leo Horie Date: Fri, 16 May 2014 09:37:03 -0400 Subject: [PATCH] add benchmarks page --- Gruntfile.js | 3 +- archive/v0.1.13/auto-redrawing.html | 1 + archive/v0.1.13/benchmarks.html | 90 +++++++++++++++++++++++ archive/v0.1.13/community.html | 1 + archive/v0.1.13/comparison.html | 1 + archive/v0.1.13/compiling-templates.html | 1 + archive/v0.1.13/components.html | 1 + archive/v0.1.13/getting-started.html | 24 ++++-- archive/v0.1.13/index.html | 2 +- archive/v0.1.13/installation.html | 1 + archive/v0.1.13/integration.html | 1 + archive/v0.1.13/mithril.min.zip | Bin 43732 -> 43732 bytes archive/v0.1.13/practices.html | 1 + archive/v0.1.13/refactoring.html | 1 + archive/v0.1.13/routing.html | 1 + archive/v0.1.13/tools.html | 1 + archive/v0.1.13/web-services.html | 1 + docs/benchmarks.md | 34 +++++++++ docs/getting-started.md | 33 +++++++-- docs/layout/guide.html | 1 + docs/layout/index.html | 2 +- 21 files changed, 186 insertions(+), 15 deletions(-) create mode 100644 archive/v0.1.13/benchmarks.html create mode 100644 docs/benchmarks.md diff --git a/Gruntfile.js b/Gruntfile.js index 2fed063a..2b708f2d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -10,6 +10,7 @@ module.exports = function(grunt) { var guideLayout = "guide" var guide = [ "auto-redrawing", + "benchmarks", "community", "compiling-templates", "comparison", @@ -21,7 +22,7 @@ module.exports = function(grunt) { "refactoring", "routing", "tools", - "web-services" + "web-services", ] var apiLayout = "api" var api = [ diff --git a/archive/v0.1.13/auto-redrawing.html b/archive/v0.1.13/auto-redrawing.html index f62f97d4..93a7fe55 100644 --- a/archive/v0.1.13/auto-redrawing.html +++ b/archive/v0.1.13/auto-redrawing.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/benchmarks.html b/archive/v0.1.13/benchmarks.html new file mode 100644 index 00000000..7052a849 --- /dev/null +++ b/archive/v0.1.13/benchmarks.html @@ -0,0 +1,90 @@ + + + + Mithril + + + + +
+ +
+
+
+
+
+ +
+

Benchmarks

+

These benchmarks were designed to measure Javascript running time. This is significant because the gzipped size of a framework can be misleading in terms of how much code actually runs on page loads. In my experience, page loads happen far more commonly than one would expect in single page applications: power users open multiple tabs, and mobile users are open and close the browser very frequently. And as far as templating engines go, the initial page load represents the worst case for the rendering algorithm since there are very little room for performance optimization tricks. It's arguably also one of the most important metric when it comes to performance.

+

The numbers shown here are best-run results for all frameworks, except for Mithril's case, for which I'm taking the worst-run result. The numbers aren't statistically rigorous (e.g. I didn't bother to calculate standard deviation), but they should be enough to give a rough idea of what is faster than what.

+

Generally speaking, these tests are making a deliberate effort to be biased in favor of other frameworks: for example, I don't load "optional-but-usually-used-in-real-life" things like the router module for Angular, or Marionette in Backbone's case, and I load the entirety of Mithril. In addition, this test deliberately avoids triggering requestAnimationFrame-based performance optimizations for Mithril, since this optimization does not exist in many frameworks and severely skews numbers in Mithril's favor in CPU-intensive situations like parallax sites. I'm also NOT using the Mithril template compiler, which would also skew the benchmark in Mithril's favor.

+

To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (Lower is better).

+
+
+

Loading

+ + + + + + +
Mithril 0.28ms
jQuery 13.11ms
Backbone 18.54ms
Angular 7.49ms
React 24.99ms
+
+
+

Rendering

+ + + + + + +
Mithril 9.44ms (uncompiled)
jQuery 40.27ms
Backbone 23.05ms
Angular 118.63ms
React 79.65ms
+
+
+ +

Feel free to implement versions of the tests above in other frameworks, if you wish. The code is very simple.

+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/archive/v0.1.13/community.html b/archive/v0.1.13/community.html index 534e0c74..ce98d3f7 100644 --- a/archive/v0.1.13/community.html +++ b/archive/v0.1.13/community.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/comparison.html b/archive/v0.1.13/comparison.html index 8cffa47a..a3f400b5 100644 --- a/archive/v0.1.13/comparison.html +++ b/archive/v0.1.13/comparison.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/compiling-templates.html b/archive/v0.1.13/compiling-templates.html index d4747b48..4d5dc799 100644 --- a/archive/v0.1.13/compiling-templates.html +++ b/archive/v0.1.13/compiling-templates.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/components.html b/archive/v0.1.13/components.html index 1efc655d..45b28f25 100644 --- a/archive/v0.1.13/components.html +++ b/archive/v0.1.13/components.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/getting-started.html b/archive/v0.1.13/getting-started.html index b72ad87f..87ebaeba 100644 --- a/archive/v0.1.13/getting-started.html +++ b/archive/v0.1.13/getting-started.html @@ -38,6 +38,7 @@

Misc

@@ -221,6 +222,17 @@ m.render(todo.view(ctrl)); // input now says "Write code"

Note that when we construct the parameterized binding, we are passing the description getter-setter by reference, and not its value. We only evaluate the getter-setter to get its value in the controller method. This is a form of lazy evaluation: it allows us to say "use this value later, when the event handler gets called".

Hopefully by now, you're starting to see why Mithril encourages the usage of m.prop: Because Mithril getter-setters are functions, they naturally compose well with functional programming tools, and allow for some very powerful idioms. In this case, we're using them in a way that resembles C pointers.

Mithril uses them in other interesting ways elsewhere.

+

As a side note, some readers have pointed out that we can refactor the add method like this:

+
this.add = function() {
+    if (this.description()) {
+        this.list.push(new todo.Todo({description: this.description()}));
+        this.description("");
+    }
+}.bind(this);
+

The difference is that add no longer takes an argument, and we call .bind(this) at the end to lock the scoping of this inside of the add method

+

Then we can make the onclick binding on the template much simpler:

+
m("button", {onclick: ctrl.add}, "Add")
+

The only reason I talked about partial application here was to make you aware of that technique, since it becomes useful when dealing with parameterized event handlers. In real life, given a choice, you should always pick the simplest idiom for your use case, as we just did here.


To implement flow control in Mithril views, we simply use Javascript:

//here's the view
@@ -243,7 +255,7 @@ m("table", [
     return m("html", [
         m("body", [
             m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}),
-            m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"),
+            m("button", {onclick: ctrl.add}, "Add"),
             m("table", [
                 ctrl.list.map(function(task, index) {
                     return m("tr", [
@@ -306,12 +318,12 @@ todo.controller = function() {
     this.list = new todo.TodoList();
     this.description = m.prop("");
 
-    this.add = function(description) {
-        if (description()) {
-            this.list.push(new todo.Todo({description: description()}));
+    this.add = function() {
+        if (this.description()) {
+            this.list.push(new todo.Todo({description: this.description()}));
             this.description("");
         }
-    };
+    }.bind(this);
 };
 
 //here's the view
@@ -319,7 +331,7 @@ todo.view = function(ctrl) {
     return m("html", [
         m("body", [
             m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}),
-            m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"),
+            m("button", {onclick: ctrl.add}, "Add"),
             m("table", [
                 ctrl.list.map(function(task, index) {
                     return m("tr", [
diff --git a/archive/v0.1.13/index.html b/archive/v0.1.13/index.html
index cc08603f..a33f82db 100644
--- a/archive/v0.1.13/index.html
+++ b/archive/v0.1.13/index.html
@@ -149,7 +149,7 @@ m.module(document.getElementById("example"), app);
 			

Performance

-

To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh. (Lower is better)

+

To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (Lower is better). Read more

Loading

diff --git a/archive/v0.1.13/installation.html b/archive/v0.1.13/installation.html index 748ed95a..5af7567c 100644 --- a/archive/v0.1.13/installation.html +++ b/archive/v0.1.13/installation.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/integration.html b/archive/v0.1.13/integration.html index e6ac72d3..94a88a4a 100644 --- a/archive/v0.1.13/integration.html +++ b/archive/v0.1.13/integration.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/mithril.min.zip b/archive/v0.1.13/mithril.min.zip index 6e911528aabe6400f08c028c06c3a970921cc7d7..d67da6ba78e3ebf403e05be4fb1b4e44ddaf84d3 100644 GIT binary patch delta 66 zcmca|mFdb=Cf)#VW)?065SZq(VIyz4Dl?GYT(0`m8qA1K+~omcOqN+?38o`fS%K-P GtLy;?@fj-s delta 66 zcmca|mFdb=Cf)#VW)?065V*8${YKt&Rc0W)xm@+BHJA~fxXS~?m@Ko(5==*|vI5gn HSJ?vqawHuI diff --git a/archive/v0.1.13/practices.html b/archive/v0.1.13/practices.html index 668de6ab..2a8a5361 100644 --- a/archive/v0.1.13/practices.html +++ b/archive/v0.1.13/practices.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/refactoring.html b/archive/v0.1.13/refactoring.html index f2fe99ca..b0bccd52 100644 --- a/archive/v0.1.13/refactoring.html +++ b/archive/v0.1.13/refactoring.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/routing.html b/archive/v0.1.13/routing.html index f9e134b3..fbff0fc3 100644 --- a/archive/v0.1.13/routing.html +++ b/archive/v0.1.13/routing.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/tools.html b/archive/v0.1.13/tools.html index 93640b6b..0565e495 100644 --- a/archive/v0.1.13/tools.html +++ b/archive/v0.1.13/tools.html @@ -38,6 +38,7 @@

Misc

diff --git a/archive/v0.1.13/web-services.html b/archive/v0.1.13/web-services.html index ea866836..15f59847 100644 --- a/archive/v0.1.13/web-services.html +++ b/archive/v0.1.13/web-services.html @@ -38,6 +38,7 @@

Misc

diff --git a/docs/benchmarks.md b/docs/benchmarks.md new file mode 100644 index 00000000..a992aef8 --- /dev/null +++ b/docs/benchmarks.md @@ -0,0 +1,34 @@ +## Benchmarks + +These benchmarks were designed to measure Javascript running time. This is significant because the gzipped size of a framework can be misleading in terms of how much code actually runs on page loads. In my experience, page loads happen far more commonly than one would expect in single page applications: power users open multiple tabs, and mobile users are open and close the browser very frequently. And as far as templating engines go, the initial page load represents the worst case for the rendering algorithm since there are very little room for performance optimization tricks. It's arguably also [one of the most important metric when it comes to performance](http://blog.kissmetrics.com/loading-time/). + +The numbers shown here are best-run results for all frameworks, except for Mithril's case, for which I'm taking the worst-run result. The numbers aren't statistically rigorous (e.g. I didn't bother to calculate standard deviation), but they should be enough to give a rough idea of what is faster than what. + +Generally speaking, these tests are making a deliberate effort to be **biased in favor of other frameworks:** for example, I don't load "optional-but-usually-used-in-real-life" things like the router module for Angular, or Marionette in Backbone's case, and I load the entirety of Mithril. In addition, this test deliberately avoids triggering `requestAnimationFrame`-based performance optimizations for Mithril, since this optimization does not exist in many frameworks and [*severely* skews numbers in Mithril's favor](http://jsperf.com/angular-vs-knockout-vs-ember/308) in CPU-intensive situations like parallax sites. I'm also NOT using the [Mithril template compiler](compiling-templates.md), which would also skew the benchmark in Mithril's favor. + +To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (Lower is better). + +
+
+

Loading

+ + + + + + +
Mithril 0.28ms
jQuery 13.11ms
Backbone 18.54ms
Angular 7.49ms
React 24.99ms
+
+
+

Rendering

+ + + + + + +
Mithril 9.44ms (uncompiled)
jQuery 40.27ms
Backbone 23.05ms
Angular 118.63ms
React 79.65ms
+
+
+ +Feel free to implement versions of the tests above in other frameworks, if you wish. The code is very simple. diff --git a/docs/getting-started.md b/docs/getting-started.md index e02a4d97..1f11a1ea 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -280,6 +280,27 @@ Hopefully by now, you're starting to see why Mithril encourages the usage of `m. Mithril uses them in other interesting ways elsewhere. +As a side note, some readers have pointed out that we can refactor the `add` method like this: + +```javascript +this.add = function() { + if (this.description()) { + this.list.push(new todo.Todo({description: this.description()})); + this.description(""); + } +}.bind(this); +``` + +The difference is that `add` no longer takes an argument, and we call `.bind(this)` at the end to lock the scoping of `this` inside of the `add` method + +Then we can make the `onclick` binding on the template much simpler: + +``` +m("button", {onclick: ctrl.add}, "Add") +``` + +The only reason I talked about partial application here was to make you aware of that technique, since it becomes useful when dealing with parameterized event handlers. In real life, given a choice, you should always pick the simplest idiom for your use case, as we just did here. + --- To implement flow control in Mithril views, we simply use Javascript: @@ -313,7 +334,7 @@ todo.view = function(ctrl) { return m("html", [ m("body", [ m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}), - m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"), + m("button", {onclick: ctrl.add}, "Add"), m("table", [ ctrl.list.map(function(task, index) { return m("tr", [ @@ -391,12 +412,12 @@ todo.controller = function() { this.list = new todo.TodoList(); this.description = m.prop(""); - this.add = function(description) { - if (description()) { - this.list.push(new todo.Todo({description: description()})); + this.add = function() { + if (this.description()) { + this.list.push(new todo.Todo({description: this.description()})); this.description(""); } - }; + }.bind(this); }; //here's the view @@ -404,7 +425,7 @@ todo.view = function(ctrl) { return m("html", [ m("body", [ m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}), - m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"), + m("button", {onclick: ctrl.add}, "Add"), m("table", [ ctrl.list.map(function(task, index) { return m("tr", [ diff --git a/docs/layout/guide.html b/docs/layout/guide.html index 16c7015b..d9742e73 100644 --- a/docs/layout/guide.html +++ b/docs/layout/guide.html @@ -38,6 +38,7 @@

Misc

diff --git a/docs/layout/index.html b/docs/layout/index.html index f7a04e0c..bfa961ff 100644 --- a/docs/layout/index.html +++ b/docs/layout/index.html @@ -149,7 +149,7 @@ m.module(document.getElementById("example"), app);

Performance

-

To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh. (Lower is better)

+

To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (Lower is better). Read more

Loading