data-bind cache in Knockout.js

JavaScript performance comparison

Revision 18 of this test case created by

Preparation code

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script><script src="http://github.com/downloads/SteveSanderson/knockout/jquery.tmpl.js"></script><script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-1.3.0beta.debug.js"></script>

<div style="display: none">
<table>
<tbody id="main" data-bind="template: { name: 'repeat', foreach: rows }"></tbody>
</table>
</div>

<script id="repeat" type="text/html">
        <tr>
           <td data-bind="text: id, title: id"></td>
<td data-bind="text: name, title: name"></td>
        </tr>
</script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.debug.js">
</script>
<script type='text/javascript' src="https://raw.github.com/mbest/knockout-repeat/master/knockout-repeat.js"></script>


<script>

function Row(id) {
    this.id = id;
    this.name = "Row_" + id;
}

var viewModel = {
    rows: ko.observableArray()
};

for (var i = 0; i < 2400; i++) {
    var row = new Row(i);
    viewModel.rows.push(row);
}

ko.utils.buildEvalFunction = function (expression, scopeLevels) {
	// Build the source for a function that evaluates "expression"
	// For each scope variable, add an extra level of "with" nesting
	// Example result: with(sc[1]) { with(sc[0]) { return (expression) } }
	var functionBody = "return (" + expression + ")";
	for (var i = 0; i < scopeLevels; i++) {
		functionBody = "with(sc[" + i + "]) { " + functionBody + " } ";
	}
	return new Function("sc", functionBody);
};


ko.bindingProvider['instance'].bindingCache = {};
var withoutcache = ko.bindingProvider['instance'].parseBindingsString;
var withcache = function(bindingsString, bindingContext) {
	try {
		var viewModel = bindingContext['$data'];
		var scopes = (typeof viewModel == 'object' && viewModel != null) ? [viewModel, bindingContext] : [bindingContext];
		var cacheKey = scopes.length + '_' + bindingsString;
		var bindingFunction;
		if (cacheKey in this.bindingCache) {
			bindingFunction = this.bindingCache[cacheKey];
		} else {
			var rewrittenBindings = " { " + ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson(bindingsString) + " } ";
			bindingFunction = this.bindingCache[cacheKey] = ko.utils.buildEvalFunction(rewrittenBindings, scopes.length);
		}
		return bindingFunction(scopes);
	} catch (ex) {
		throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
	}           
};

</script>
      
<script>
Benchmark.prototype.setup = function() {
  ko.utils.emptyDomNode($("#main")[0]);
  ko.bindingProvider['instance'].bindingCache = {};
  

};
</script>

Preparation code output

<script src="http://github.com/downloads/SteveSanderson/knockout/jquery.tmpl.js"></script><script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-1.3.0beta.debug.js"></script> <div style="display: none"> <table> <tbody id="main" data-bind="template: { name: 'repeat', foreach: rows }"></tbody> </table> </div> <script id="repeat" type="text/html"> <tr> <td data-bind="text: id, title: id"></td> <td data-bind="text: name, title: name"></td> </tr> </script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.debug.js"> </script> <script type='text/javascript' src="https://raw.github.com/mbest/knockout-repeat/master/knockout-repeat.js"></script> <script> function Row(id) { this.id = id; this.name = "Row_" + id; } var viewModel = { rows: ko.observableArray() }; for (var i = 0; i < 2400; i++) { var row = new Row(i); viewModel.rows.push(row); } ko.utils.buildEvalFunction = function (expression, scopeLevels) { // Build the source for a function that evaluates "expression" // For each scope variable, add an extra level of "with" nesting // Example result: with(sc[1]) { with(sc[0]) { return (expression) } } var functionBody = "return (" + expression + ")"; for (var i = 0; i < scopeLevels; i++) { functionBody = "with(sc[" + i + "]) { " + functionBody + " } "; } return new Function("sc", functionBody); }; ko.bindingProvider['instance'].bindingCache = {}; var withoutcache = ko.bindingProvider['instance'].parseBindingsString; var withcache = function(bindingsString, bindingContext) { try { var viewModel = bindingContext['$data']; var scopes = (typeof viewModel == 'object' && viewModel != null) ? [viewModel, bindingContext] : [bindingContext]; var cacheKey = scopes.length + '_' + bindingsString; var bindingFunction; if (cacheKey in this.bindingCache) { bindingFunction = this.bindingCache[cacheKey]; } else { var rewrittenBindings = " { " + ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson(bindingsString) + " } "; bindingFunction = this.bindingCache[cacheKey] = ko.utils.buildEvalFunction(rewrittenBindings, scopes.length); } return bindingFunction(scopes); } catch (ex) { throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString); } }; </script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
with cache
ko.bindingProvider['instance'].parseBindingsString = withcache;

var main = $("#main");
ko.applyBindings(viewModel, main[0]);
pending…
without cache
ko.bindingProvider['instance'].parseBindingsString = withoutcache;

var main = $("#main");
ko.applyBindings(viewModel, main[0]);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments