From adb6af4f6b46a6ff041183ff5f1425c2674f3398 Mon Sep 17 00:00:00 2001 From: Justin Meyer Date: Wed, 18 Dec 2013 23:16:19 -0600 Subject: [PATCH] added benchmark.js to measure performance improvements while investigating #595 --- bower.json | 3 +- lib/stealconfig.js | 6 ++-- list/list.js | 7 +++++ map/benchmark.html | 18 +++++++++++ map/map.js | 73 +++++++++++++++++++++++-------------------- map/map_benchmark.js | 18 +++++++++++ test/benchmarks.js | 33 +++++++++++++++++++ util/jquery/jquery.js | 5 +++ 8 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 map/benchmark.html create mode 100644 map/map_benchmark.js create mode 100644 test/benchmarks.js diff --git a/bower.json b/bower.json index 2186e53884f..0e50ae2de9c 100644 --- a/bower.json +++ b/bower.json @@ -11,6 +11,7 @@ "license": "MIT", "devDependencies": { "qunit": "~1.12.0", - "zepto": "~1.0.0" + "zepto": "~1.0.0", + "benchmark": "~1.0.0" } } diff --git a/lib/stealconfig.js b/lib/stealconfig.js index e721889a195..891aad726a6 100644 --- a/lib/stealconfig.js +++ b/lib/stealconfig.js @@ -28,7 +28,8 @@ map: { "*": { "jquery/jquery.js": "jquery", - "can/util/util.js": "can/util/jquery/jquery.js" + "can/util/util.js": "can/util/jquery/jquery.js", + "benchmark/benchmark.js":"benchmark" } }, paths: { @@ -38,7 +39,8 @@ "yui/yui.js": "lib/yui-3.7.3.js", "zepto/zepto.js": "lib/zepto.1.0.js", "can/": "", - "jquerypp/": "http://jquerypp.com/release/1.0.1/steal/" + "jquerypp/": "http://jquerypp.com/release/1.0.1/steal/", + "benchmark": "bower_components/benchmark/benchmark.js" }, shim: { jquery: { diff --git a/list/list.js b/list/list.js index 009393bf7ef..2c407e4236b 100644 --- a/list/list.js +++ b/list/list.js @@ -79,11 +79,18 @@ steal("can/util","can/map", function(can, Map){ this.length = 0; can.cid(this, ".map") this._init = 1; + instances = instances || []; + + if( can.isDeferred(instances) ) { this.replace(instances) } else { + var teardownMapping = instances.length && can.Map.helpers.addToMap(instances, this); this.push.apply(this, can.makeArray(instances || [])); } + + teardownMapping && teardownMapping(); + // this change needs to be ignored this.bind('change',can.proxy(this._changes,this)); can.simpleExtend(this, options); diff --git a/map/benchmark.html b/map/benchmark.html new file mode 100644 index 00000000000..90b21300be9 --- /dev/null +++ b/map/benchmark.html @@ -0,0 +1,18 @@ + + + + + +

can.Map Test Suite

+ +

+ +
+

+
    +
    + + + + + \ No newline at end of file diff --git a/map/map.js b/map/map.js index 5c9a663b968..0dbb95396ff 100644 --- a/map/map.js +++ b/map/map.js @@ -50,27 +50,6 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, // A map that temporarily houses a reference // to maps that have already been made for a plain ole JS object madeMap = null, - addToMap = function(obj, instance){ - var teardown = false; - if(!madeMap){ - teardown = true; - madeMap = {} - } - // record if it has a Cid before we add one - var hasCid = obj._cid; - var cid = can.cid(obj); - - // only update if there already isn't one - if( !madeMap[cid] ){ - - madeMap[cid] = { - obj: obj, - instance: instance, - added: !hasCid - } - } - return teardown; - }, teardownMap = function(){ for(var cid in madeMap){ if(madeMap[cid].added) { @@ -82,6 +61,7 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, getMapFromObject = function(obj){ return madeMap && madeMap[obj._cid] && madeMap[obj._cid].instance }; + /** * @add can.Map */ @@ -99,9 +79,13 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, if(!this.defaults){ this.defaults = {}; } + // a list of the compute properties + this._computes = []; for(var prop in this.prototype){ if(typeof this.prototype[prop] !== "function"){ this.defaults[prop] = this.prototype[prop]; + } else if(this.prototype[prop].isComputed) { + this._computes.push(prop) } } } @@ -111,6 +95,7 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, } }, + _computes: [], // keep so it can be overwritten bind: can.bindAndSetup, on: can.bindAndSetup, @@ -118,6 +103,28 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, off: can.unbindAndTeardown, id: "id", helpers: { + addToMap: function(obj, instance){ + var teardown; + if(!madeMap){ + teardown = teardownMap; + madeMap = {} + } + // record if it has a Cid before we add one + var hasCid = obj._cid; + var cid = can.cid(obj); + + // only update if there already isn't one + if( !madeMap[cid] ){ + + madeMap[cid] = { + obj: obj, + instance: instance, + added: !hasCid + } + } + return teardown; + }, + canMakeObserve : function( obj ) { return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject( obj ) || ( obj instanceof can.Map )); }, @@ -228,7 +235,7 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, // Sets all `attrs`. this._init = 1; this._setupComputes(); - var teardownMapping = obj && addToMap(obj, this); + var teardownMapping = obj && can.Map.helpers.addToMap(obj, this); /** * @property {*} can.Map.prototype.DEFAULT-ATTR * @@ -263,9 +270,9 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, */ var data = can.extend( can.extend(true,{},this.constructor.defaults || {}), obj ) this.attr(data); - if(teardownMapping){ - teardownMap() - } + + teardownMapping && teardownMapping() + this.bind('change',can.proxy(this._changes,this)); delete this._init; @@ -358,17 +365,15 @@ steal('can/util','can/util/bind','can/construct', 'can/util/batch',function(can, * Getter/Setter computes. */ _setupComputes: function(){ - var prototype = this.constructor.prototype; - this._computedBindings = {} - for(var prop in prototype){ - if(prototype[prop] && prototype[prop].isComputed){ - this[prop] = prototype[prop].clone(this); - this._computedBindings[prop] = { - count: 0 - } + var computes = this.constructor._computes; + this._computedBindings = {}; + for(var i = 0, len = computes.length, prop; i< len; i++) { + prop = computes[i]; + this[prop] = this[prop].clone(this); + this._computedBindings[prop] = { + count: 0 } } - }, _bindsetup: makeBindSetup(), _bindteardown: function(){ diff --git a/map/map_benchmark.js b/map/map_benchmark.js new file mode 100644 index 00000000000..2a9d302175b --- /dev/null +++ b/map/map_benchmark.js @@ -0,0 +1,18 @@ +steal('can/map', 'can/list','can/test/benchmarks.js',function(Map, List, benchmarks){ + + benchmarks.add( + "Adding a big array to an object", + function(){ + var map = new can.Map(), + objects = []; + + for (var i = 0; i < 10; i++){ + objects.push({prop: 'prop', nest: {prop: 'prop', nest: {prop: 'prop'}}}) + } + }, + function(){ + map.attr('obj', objects) + }); + + +}) diff --git a/test/benchmarks.js b/test/benchmarks.js new file mode 100644 index 00000000000..4de96a2690d --- /dev/null +++ b/test/benchmarks.js @@ -0,0 +1,33 @@ +steal("steal","benchmark", function(steal){ + + var suite = new Benchmark.Suite; + + suite.on('cycle', function(event) { + console.log(String(event.target)); + }) + + var benchmarks = { + add: function(name, setup, benchmark){ + if(!benchmark){ + benchmark = setup; + setup = undefined + } + suite.add(name, benchmark, { + setup: setup + }); + return this; + }, + run: function(){ + suite.run({ 'async': true, 'queued': true }); + }, + suite: suite, + on: function(){ + return suite.on.apply(this, arguments) + } + } + steal.bind("done", function(){ + benchmarks.run(); + }) + + return benchmarks; +}) diff --git a/util/jquery/jquery.js b/util/jquery/jquery.js index 3fc3de0eeaa..acadf8386cb 100644 --- a/util/jquery/jquery.js +++ b/util/jquery/jquery.js @@ -84,6 +84,11 @@ steal('jquery', 'can/util/can.js', 'can/util/array/each.js', "can/util/inserted" } return this; + }, + proxy: function(fn, context){ + return function(){ + return fn.apply(context, arguments) + } } });