I'm trying to figure out why in my jsperf below, the javascript string templates are so much slower than the equivalent knockout.js templates. The goal of the test is to measure full page, and partial page changes, link in a Single Page Application.
http://jsperf.com/knockoutvjquery/6
Originally I was going to test just the speed of underscore.js templates, but now I'm just writing raw hard coded strings to the dom. It seems that when the speed tests are simple dom elements they are fast, but once I get into "real world" situations, the knockout tests are faster. What gives?
Preparation code HTML
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="//cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.js"></script>
<div id="jquery"></div>
<div id="knockout">
<div data-bind="foreach:Zones">
<div data-bind="
style:{ position: 'absolute', top: ZoneY, left: ZoneX, width: Width, height: Height },
foreach:Layouts
">
<!-- ko if: ShowText -->
<div style="position: absolute;" data-bind="
text:Value,
style:{
top: YPosition,
left: XPosition,
color: FontColor,
'font-family': FontFamilyName,
'font-size': FontSize,
'font-weight': FontWeight
}
"></div>
<!-- /ko -->
<!-- ko if: ShowHtml -->
<div style="position: absolute;" data-bind="
html:Value,
style:{
top: YPosition,
left: XPosition,
color: FontColor,
'font-family': FontFamilyName,
'font-size': FontSize,
'font-weight': FontWeight
}
"></div>
<!-- /ko -->
<!-- ko if: ShowImage -->
<!-- ko if: IsSWF -->
<embed data-bind="attr:{src:ImageURL}, style:{ width: Width, height : Height }">
<!-- /ko -->
<!-- ko ifnot: IsSWF -->
<img data-bind="attr:{src:ImageURL}, style:{ width: Width, height : Height }" />
<!-- /ko -->
<!-- /ko -->
</div>
</div>
</div>
Define Setup for all tests
var d1 = '<div style="position: absolute; top: 0px; left: 0px; width: 1360px; height: 768px; "></div>'
var d2 = '<div style="position: absolute; top: 0px; left: 0px; width: 1360px; height: 280px; "><embed src="http://ronincastx.ronincast.com/CMS/Media/54638DC3-DC3C-4EAC-8698-334EB681B6B8_Widener-Expo-1.2-1360x280.swf" style="width: 1360px; height: 275px; "></div>'
var d3 = '<div style="position: absolute; top: 275px; left: 0px; width: 1360px; height: 493px; "><img src="http://ronincastx.ronincast.com/CMS/Media/94A94FC5-AB43-4218-B5CA-15C6F37D3152_WidenerU-Expo-1.2-BKGD.png" style="width: 1360px; height: 493px; "></div>'
var d4 = '<div style="position: absolute; top: 650px; left: 0px; width: 118px; height: 118px; "><embed src="http://ronincastx.ronincast.com/CMS/Media/0C484985-6038-47B8-8A1D-BF7FB33B6B56_Be Well-Footer-140x140.swf" style="width: 140px; height: 140px; "></div>'
var d5 = '<div style="position: absolute; top: 650px; left: 160px; width: 130px; height: 118px; "><img src="http://ronincastx.ronincast.com/CMS/Media/a91e8733-d27f-4f86-81e3-df4f93efad67_01Just4U-Icons-LowerFat.png" style="width: 130px; height: 118px; "> </div>'
var d6 = '<div style="position: absolute; top: 650px; left: 310px; width: 130px; height: 118px; "></div>'
var d7 = '<div style="position: absolute; top: 650px; left: 460px; width: 130px; height: 118px; "></div>'
var d8 = '<div style="position: absolute; top: 650px; left: 610px; width: 130px; height: 118px; "></div>'
var d9 = '<div style="position: absolute; top: 650px; left: 760px; width: 130px; height: 118px; "></div>'
var d10 = '<div style="position: absolute; top: 370px; left: 0px; width: 680px; height: 126px; "> <div style="position: absolute; top: 79px; left: 84px; color: rgb(0, 0, 0); font-size: 24px; font-weight: bold; " > Create your customized omelet with your choice of: ham, turkey, spinach, green peppers, onions, and mushrooms</div><div style="position: absolute; top: 0px; left: 84px; color: rgb(0, 0, 0); font-size: 50px; font-weight: bold; " >Customized Omelet</div></div>'
var d11 = '<div style="position: absolute; top: 501px; left: 0px; width: 680px; height: 126px; "> <img src="http://ronincastx.ronincast.com/CMS/Media/2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png" style="width: 25px; height: 25px; "></div>'
var d12 = '<div style="position: absolute; top: 370px; left: 680px; width: 680px; height: 126px; "><img src="http://ronincastx.ronincast.com/CMS/Media/2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png" style="width: 25px; height: 25px; "> </div>'
var d13 = '<div style="position: absolute; top: 501px; left: 680px; width: 680px; height: 126px; "><img src="http://ronincastx.ronincast.com/CMS/Media/2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png" style="width: 25px; height: 25px; "></div>'
var d14 = '<div style="position: absolute; top: 290px; left: 0px; width: 680px; height: 77px; "><div style="position: absolute; top: 0px; left: 84px; color: rgb(0, 0, 0); font-size: 72px; font-weight: bold; "></div></div>'
var d15 = '<div style="position: absolute; top: 290px; left: 680px; width: 680px; height: 77px; "><div style="position: absolute; top: 0px; left: 84px; color: rgb(0, 0, 0); font-size: 72px; font-weight: bold; "></div></div>'
var displayViewModel = function () {
var self = this;
this.init = function () {
};
this.Events = ko.observableArray();
this.Zones = ko.computed({
read: function () {
if(self.Events().length == 0) {
return [];
}
return self.Events()[0].Presentations[0].Zones;
},
deferEvaluation: true
});
};
var events = [
{
"Id": 38312,
"Presentations": [
{
"Zones": [
{
"ZoneX": "0px",
"ZoneY": "0px",
"Layouts": [],
"Height": "768px",
"Width": "1360px"
},
{
"ZoneX": "0px",
"ZoneY": "0px",
"Layouts": [
{
"XPosition": "0px",
"YPosition": "0px",
"Width": "1360px",
"Height": "275px",
"Value": "54638dc3-dc3c-4eac-8698-334eb681b6b8",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "54638DC3-DC3C-4EAC-8698-334EB681B6B8_Widener-Expo-1.2-1360x280.swf",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": true,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/54638DC3-DC3C-4EAC-8698-334EB681B6B8_Widener-Expo-1.2-1360x280.swf"
}
],
"Height": "280px",
"Width": "1360px"
},
{
"ZoneX": "0px",
"ZoneY": "275px",
"Layouts": [
{
"XPosition": "0px",
"YPosition": "0px",
"Width": "1360px",
"Height": "493px",
"Value": "94a94fc5-ab43-4218-b5ca-15c6f37d3152",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "94A94FC5-AB43-4218-B5CA-15C6F37D3152_WidenerU-Expo-1.2-BKGD.png",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/94A94FC5-AB43-4218-B5CA-15C6F37D3152_WidenerU-Expo-1.2-BKGD.png"
}
],
"Height": "493px",
"Width": "1360px"
},
{
"ZoneX": "0px",
"ZoneY": "650px",
"Layouts": [
{
"XPosition": "0px",
"YPosition": "0px",
"Width": "140px",
"Height": "140px",
"Value": "0c484985-6038-47b8-8a1d-bf7fb33b6b56",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "0C484985-6038-47B8-8A1D-BF7FB33B6B56_Be Well-Footer-140x140.swf",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": true,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/0C484985-6038-47B8-8A1D-BF7FB33B6B56_Be Well-Footer-140x140.swf"
}
],
"Height": "118px",
"Width": "118px"
},
{
"ZoneX": "160px",
"ZoneY": "650px",
"Layouts": [
{
"XPosition": "0px",
"YPosition": "0px",
"Width": "130px",
"Height": "118px",
"Value": "a91e8733-d27f-4f86-81e3-df4f93efad67",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "a91e8733-d27f-4f86-81e3-df4f93efad67_01Just4U-Icons-LowerFat.png",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/a91e8733-d27f-4f86-81e3-df4f93efad67_01Just4U-Icons-LowerFat.png"
}
],
"Height": "118px",
"Width": "130px",
},
{
"ZoneX": "310px",
"ZoneY": "650px",
"Layouts": [],
"Height": "118px",
"Width": "130px",
},
{
"ZoneX": "460px",
"ZoneY": "650px",
"Layouts": [],
"Height": "118px",
"Width": "130px"
},
{
"ZoneX": "610px",
"ZoneY": "650px",
"Layouts": [],
"Height": "118px",
"Width": "130px"
},
{
"ZoneX": "760px",
"ZoneY": "650px",
"Layouts": [],
"Height": "118px",
"Width": "130px"
},
{
"ZoneX": "0px",
"ZoneY": "370px",
"Layouts": [
{
"XPosition": "84px",
"YPosition": "79px",
"Width": "595px",
"Height": "40px",
"Value": "Create your customized omelet with your choice of: ham, turkey, spinach, green peppers, onions, and mushrooms",
"ValueType": 3,
"FontSize": "24px",
"FontFamilyName": "",
"FontWeight": "bold",
"FontColor": "#000000",
"ShowText": true,
"ShowImage": false,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": ""
},
{
"XPosition": "84px",
"YPosition": "0px",
"Width": "595px",
"Height": "52px",
"Value": "Customized Omelet",
"ValueType": 1,
"FontSize": "50px",
"FontFamilyName": "",
"FontWeight": "bold",
"FontColor": "#000000",
"ShowText": true,
"ShowImage": false,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": ""
}
],
"Height": "126px",
"Width": "680px",
},
{
"ZoneX": "0px",
"ZoneY": "501px",
"Layouts": [
{
"XPosition": "53px",
"YPosition": "11px",
"Width": "25px",
"Height": "25px",
"Value": "2509594f-404f-4818-bb26-30c91adb57b9",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png"
}
],
"Height": "126px",
"Width": "680px",
},
{
"ZoneX": "680px",
"ZoneY": "370px",
"Layouts": [
{
"XPosition": "53px",
"YPosition": "11px",
"Width": "25px",
"Height": "25px",
"Value": "2509594f-404f-4818-bb26-30c91adb57b9",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png"
}
],
"Height": "126px",
"Width": "680px",
},
{
"ZoneX": "680px",
"ZoneY": "501px",
"Layouts": [
{
"XPosition": "53px",
"YPosition": "11px",
"Width": "25px",
"Height": "25px",
"Value": "2509594f-404f-4818-bb26-30c91adb57b9",
"ValueType": 12,
"FontSize": "20px",
"FontFamilyName": "Verlag Black",
"FontWeight": "Normal",
"FontColor": "#000000",
"FileName": "2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png",
"ShowText": false,
"ShowImage": true,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": "http://ronincastx.ronincast.com/CMS/Media/2509594F-404F-4818-BB26-30C91ADB57B9_transparent-image.png"
}
],
"Height": "126px",
"Width": "680px"
},
{
"ZoneX": "0px",
"ZoneY": "290px",
"Layouts": [
{
"XPosition": "84px",
"YPosition": "0px",
"Width": "596px",
"Height": "77px",
"Value": "",
"ValueType": 1,
"FontSize": "72px",
"FontFamilyName": "",
"FontWeight": "bold",
"FontColor": "#000000",
"ShowText": true,
"ShowImage": false,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": ""
}
],
"Height": "77px",
"Width": "680px"
},
{
"ZoneX": "680px",
"ZoneY": "290px",
"Layouts": [
{
"XPosition": "84px",
"YPosition": "0px",
"Width": "596px",
"Height": "77px",
"Value": "",
"ValueType": 1,
"FontSize": "72px",
"FontFamilyName": "",
"FontWeight": "bold",
"FontColor": "#000000",
"ShowText": true,
"ShowImage": false,
"ShowHtml": false,
"IsSWF": false,
"ImageURL": ""
}
],
"Height": "77px",
"Width": "680px"
}
]
}
]
}
];
var masterViewModel = new displayViewModel();
var knockoutElement = $('#knockout')[0];
ko.applyBindings(masterViewModel, $('#knockout')[0]);
Define teardown for all tests
$('#jquery').empty()
masterViewModel.Events([]);
ko.cleanNode(knockoutElement);
Test 1 - jquery
var tmp = [d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15];
$('#jquery')[0].innerHTML = tmp.join();
Test 2 - knockout
masterViewModel.Events.push.apply(masterViewModel.Events, events);
For anonymous templates (what is used for if
, ifnot
, with
, and foreach
), the elements are stored and cloned when the actual rendering happens. This is true for all browsers except IE < 9.
So, the Knockout test is able to do cloneNode
on existing nodes rather than parsing them from strings like the jQuery test is doing.