From 3caafb598e47fc6f81b3b8dc9fb1176a0257baba Mon Sep 17 00:00:00 2001 From: Richard Gustin Date: Sun, 12 Apr 2015 08:35:02 +1000 Subject: [PATCH 1/2] tests(class): Performance testing for classes --- test/performance/class.js | 254 ++++++++++++++++++ test/performance/class/ES5Native.js | 38 +++ test/performance/class/ES6Native.es6 | 44 +++ test/performance/class/UberClass.js | 40 +++ test/performance/class/UberProto.js | 38 +++ .../class/util/inlinePreventedFunction.js | 21 ++ 6 files changed, 435 insertions(+) create mode 100644 test/performance/class.js create mode 100644 test/performance/class/ES5Native.js create mode 100644 test/performance/class/ES6Native.es6 create mode 100644 test/performance/class/UberClass.js create mode 100644 test/performance/class/UberProto.js create mode 100644 test/performance/class/util/inlinePreventedFunction.js diff --git a/test/performance/class.js b/test/performance/class.js new file mode 100644 index 0000000..33afd8f --- /dev/null +++ b/test/performance/class.js @@ -0,0 +1,254 @@ +require('babel/register')({ + loose: ['es6.classes'], + extensions: ['.es6'] +}); + +var Benchmark = require('benchmark') + , ES5Native = require('./class/ES5Native') + , ES6Native = require('./class/ES6Native.es6') + , UberClass = require('./class/UberClass') + , UberProto = require('./class/UberProto'); + +new Benchmark +.Suite() +.add('ES5Native', function() { + for (var i = 0; i < 20000; i++) { + new ES5Native.ClassA('a'); + new ES5Native.ClassB('b'); + } +}) +.add('ES6Native', function() { + for (var i = 0; i < 20000; i++) { + new ES6Native.ClassA('a'); + new ES6Native.ClassB('b'); + } +}) +.add('UberClass', function() { + for (var i = 0; i < 20000; i++) { + UberClass.ClassA.newInstance('a'); + UberClass.ClassB.newInstance('b'); + } +}) +.add('UberProto', function() { + for (var i = 0; i < 20000; i++) { + UberProto.ClassA.create('a') + UberProto.ClassB.create('b') + } +}) +.on('cycle', function(event) { + console.log(String(event.target)); +}) +.on('complete', function() { + console.log('Fastest for construction (polymorphic) is >> ' + this.filter('fastest').pluck('name')); + + modelInstances = [ + // ES5Native: + [ + new ES5Native.ClassA('a1'), + new ES5Native.ClassB('b1'), + new ES5Native.ClassA('a2'), + new ES5Native.ClassB('b2'), + new ES5Native.ClassA('a3'), + new ES5Native.ClassB('b3'), + new ES5Native.ClassA('a4'), + new ES5Native.ClassB('b4'), + new ES5Native.ClassA('a5'), + new ES5Native.ClassB('b5') + ], + // ES6Native: + [ + new ES6Native.ClassA('a1'), + new ES6Native.ClassB('b1'), + new ES6Native.ClassA('a2'), + new ES6Native.ClassB('b2'), + new ES6Native.ClassA('a3'), + new ES6Native.ClassB('b3'), + new ES6Native.ClassA('a4'), + new ES6Native.ClassB('b4'), + new ES6Native.ClassA('a5'), + new ES6Native.ClassB('b5') + ], + // UberClass: + [ + UberClass.ClassA.newInstance('a1'), + UberClass.ClassB.newInstance('b1'), + UberClass.ClassA.newInstance('a2'), + UberClass.ClassB.newInstance('b2'), + UberClass.ClassA.newInstance('a3'), + UberClass.ClassB.newInstance('b3'), + UberClass.ClassA.newInstance('a4'), + UberClass.ClassB.newInstance('b4'), + UberClass.ClassA.newInstance('a5'), + UberClass.ClassB.newInstance('b5') + ], + // UberProto: + [ + UberProto.ClassA.create('a1'), + UberProto.ClassB.create('b1'), + UberProto.ClassA.create('a2'), + UberProto.ClassB.create('b2'), + UberProto.ClassA.create('a3'), + UberProto.ClassB.create('b3'), + UberProto.ClassA.create('a4'), + UberProto.ClassB.create('b4'), + UberProto.ClassA.create('a5'), + UberProto.ClassB.create('b5') + ] + ]; + + // warm-up loop + for (var dummy = 0; dummy < 4; dummy++) { + for (var c = 0; c < 10; c++) { + modelInstances[dummy][c].method(); + if (modelInstances[dummy][c].method.ownMethod) { + modelInstances[dummy][c].method.ownMethod(); + } + if (modelInstances[dummy][c].method.parentMethod) { + modelInstances[dummy][c].method.parentMethod(); + } + } + } + new Benchmark + .Suite() + .add('ES5Native#method', function() { + var instances = modelInstances[0]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].method(); + } + } + }) + .add('ES6Native#method', function() { + var instances = modelInstances[1]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].method(); + } + } + }) + .add('UberClass#method', function() { + var instances = modelInstances[2]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].method(); + } + } + }) + .add('UberProto#method', function() { + var instances = modelInstances[3]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].method(); + } + } + }) + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('complete', function() { + console.log('Fastest method call is ' + this.filter('fastest').pluck('name')); + + new Benchmark + .Suite() + .add('ES5Native#parentMethod', function() { + var instances = modelInstances[0]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].parentMethod(); + } + } + }) + .add('ES6Native#parentMethod', function() { + var instances = modelInstances[1]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].parentMethod(); + } + } + }) + .add('UberClass#parentMethod', function() { + var instances = modelInstances[2]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].parentMethod(); + } + } + }) + .add('UberProto#parentMethod', function() { + var instances = modelInstances[3]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].parentMethod(); + } + } + }) + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('complete', function() { + console.log('Fastest parentMethod call is ' + this.filter('fastest').pluck('name')); + + + new Benchmark + .Suite() + .add('ES5Native#ownMethod', function() { + var instances = modelInstances[0]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].ownMethod(); + } + } + }) + .add('ES6Native#ownMethod', function() { + var instances = modelInstances[1]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].ownMethod(); + } + } + }) + .add('UberClass#ownMethod', function() { + var instances = modelInstances[2]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].ownMethod(); + } + } + }) + .add('UberProto#ownMethod', function() { + var instances = modelInstances[3]; + for (var i = 0; i < 50000; i++) { + for (var c = 0; c < 10; c++) { + instances[c].ownMethod(); + } + } + }) + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('complete', function() { + console.log('Fastest ownMethod call is ' + this.filter('fastest').pluck('name')); + }) + .run({ + name : 'Method Call', + async : false + }); + + + }) + .run({ + name : 'Parent Method Call', + async : false + }); + + }) + .run({ + name : 'Overloaded Method Call', + async : false + }); + +}) +.run({ + name : 'Instantiation', + async : false +}); diff --git a/test/performance/class/ES5Native.js b/test/performance/class/ES5Native.js new file mode 100644 index 0000000..352d97e --- /dev/null +++ b/test/performance/class/ES5Native.js @@ -0,0 +1,38 @@ +var util = require('util') + , inlinePreventedFunction = require('./util/inlinePreventedFunction'); + +function ES5Class(instanceString) { + this.instanceString = instanceString; +} +ES5Class.prototype.counter = 0; +ES5Class.prototype.instanceArray = []; +ES5Class.prototype.instanceString = ''; +ES5Class.prototype.method = inlinePreventedFunction; +ES5Class.prototype.parentMethod = inlinePreventedFunction; + + +function ClassA(instanceString) { + ClassA.super_.call(this, instanceString); +} +ClassA.prototype.memberA = 1; +ClassA.prototype.method = function() { + this.memberA =- this.memberA; + ES5Class.prototype.method.call(this, false); +}; +ClassA.prototype.ownMethod = inlinePreventedFunction; +util.inherits(ClassA, ES5Class); +module.exports.ClassA = ClassA; + + + +function ClassB(instanceString) { + ClassB.super_.call(this, instanceString); +} +ClassB.prototype.memberB = 1; +ClassB.prototype.method = function() { + this.memberB =- this.memberB; + ES5Class.prototype.method.call(this, false); +}; +ClassB.prototype.ownMethod = inlinePreventedFunction; +util.inherits(ClassB, ES5Class); +module.exports.ClassB = ClassB; diff --git a/test/performance/class/ES6Native.es6 b/test/performance/class/ES6Native.es6 new file mode 100644 index 0000000..2d4de64 --- /dev/null +++ b/test/performance/class/ES6Native.es6 @@ -0,0 +1,44 @@ +var inlinePreventedFunction = require('./util/inlinePreventedFunction'); + +class ES6Class { + constructor(instanceString) { + this.instanceString = instanceString; + } +} + +ES6Class.prototype.counter = 0; +ES6Class.prototype.instanceArray = []; +ES6Class.prototype.instanceString = ''; +ES6Class.prototype.method = inlinePreventedFunction; +ES6Class.prototype.parentMethod = inlinePreventedFunction; + + + +class ClassA extends ES6Class { + constructor(instanceString) { + super(instanceString); + } + method() { + this.memberA =- this.memberA; + super.method(false); + } +} +ClassA.prototype.ownMethod = inlinePreventedFunction; +ClassA.prototype.memberA = 1; +module.exports.ClassA = ClassA; + + + + +class ClassB extends ES6Class { + constructor(instanceString) { + super(instanceString); + } + method() { + this.memberB =- this.memberB; + super.method(false); + } +} +ClassB.prototype.ownMethod = inlinePreventedFunction; +ClassB.prototype.memberB = 1; +module.exports.ClassB = ClassB; diff --git a/test/performance/class/UberClass.js b/test/performance/class/UberClass.js new file mode 100644 index 0000000..fd8b2dc --- /dev/null +++ b/test/performance/class/UberClass.js @@ -0,0 +1,40 @@ +var inlinePreventedFunction = require('./util/inlinePreventedFunction'); + +var UberClass = require('uberclass').extend({ + counter : 0, + instanceArray : [], + instanceString : '', + init: function(instanceString) { + this.instanceString = instanceString; + }, + method: inlinePreventedFunction, + parentMethod: inlinePreventedFunction +}); + +var ClassA = UberClass.extend({ + memberA: 1, + init: function(instanceString) { + this._super(instanceString); + }, + method: function() { + this.memberA =- this.memberA; + this._super(false); + }, + ownMethod: inlinePreventedFunction +}); +module.exports.ClassA = ClassA; + + + +var ClassB = UberClass.extend({ + memberB: 1, + init: function(instanceString) { + this._super(instanceString); + }, + method: function() { + this.memberB =- this.memberB; + this._super(false); + }, + ownMethod: inlinePreventedFunction +}); +module.exports.ClassB = ClassB; diff --git a/test/performance/class/UberProto.js b/test/performance/class/UberProto.js new file mode 100644 index 0000000..3ccf9b9 --- /dev/null +++ b/test/performance/class/UberProto.js @@ -0,0 +1,38 @@ +var inlinePreventedFunction = require('./util/inlinePreventedFunction'); + +var UberProto = require('uberproto').extend({ + counter : 0, + instanceArray : [], + instanceString : '', + init: function(instanceString) { + this.instanceString = instanceString; + }, + method: inlinePreventedFunction, + parentMethod: inlinePreventedFunction +}); + +var ClassA = UberProto.extend({ + memberA: 1, + init: function(instanceString) { + this._super(instanceString); + }, + method: function() { + this.memberA =- this.memberA; + this._super(false); + }, + ownMethod: inlinePreventedFunction +}); +module.exports.ClassA = ClassA; + +var ClassB = UberProto.extend({ + memberB: 1, + init: function(instanceString) { + this._super(instanceString); + }, + method: function() { + this.memberB =- this.memberB; + this._super(false); + }, + ownMethod: inlinePreventedFunction +}); +module.exports.ClassB = ClassB; diff --git a/test/performance/class/util/inlinePreventedFunction.js b/test/performance/class/util/inlinePreventedFunction.js new file mode 100644 index 0000000..b9f5db6 --- /dev/null +++ b/test/performance/class/util/inlinePreventedFunction.js @@ -0,0 +1,21 @@ +module.exports = function(preventInline) { + if (this.counter > 99) { + this.counter = this.counter / 2; + } else { + this.counter++; + } + + if (preventInline) { + var i = 0; + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + for (i = 0; i < 1; i++) dummy.method(); + } +}; From 59f6e0b807fcdcd8da6db385e44505f353cef764 Mon Sep 17 00:00:00 2001 From: Richard Gustin Date: Mon, 13 Apr 2015 02:23:21 +1000 Subject: [PATCH 2/2] feat(tests): Performance tests WIP --- test/performance/class/Controller.es6 | 59 +++++++++++++++++++++++++++ test/performance/class/UberClass.js | 3 +- test/performance/master.js | 2 +- test/performance/server-express.js | 4 +- 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 test/performance/class/Controller.es6 diff --git a/test/performance/class/Controller.es6 b/test/performance/class/Controller.es6 new file mode 100644 index 0000000..ec0808b --- /dev/null +++ b/test/performance/class/Controller.es6 @@ -0,0 +1,59 @@ +import { EventEmitter } from 'events'; + +class Class extends EventEmitter { + constructor() { + super(); + this.emit('init', this); + } +} + +class Model extends Class { + constructor() { + super(); + Object.keys(this.fields).forEach(function(field) { + console.dir(field); + }); + } + + save() { + console.log('save model'); + } + + destroy() { + console.log('destroy model'); + } + + toObject() { + return JSON.parse(this.toJSON()); + } + + toJSON() { + return JSON.stringify(this); + } + + inspect() { + return this.toObject(); + } +} + +class ExampleModel extends Model { + static driver = 'ORM'; + static timeStampable = true; + static fields = { + id: { + type : Number, + primaryKey : true, + autoIncrement : true + }, + name: { + type : String, + allowNull : false, + required : true + } + } +} + +// console.log('ExampleModel:'); +// console.dir(ExampleModel); +// console.log('\n\n New ExampleModel'); +// console.dir(new ExampleModel()); diff --git a/test/performance/class/UberClass.js b/test/performance/class/UberClass.js index fd8b2dc..94dfe40 100644 --- a/test/performance/class/UberClass.js +++ b/test/performance/class/UberClass.js @@ -8,7 +8,8 @@ var UberClass = require('uberclass').extend({ this.instanceString = instanceString; }, method: inlinePreventedFunction, - parentMethod: inlinePreventedFunction + parentMethod: inlinePreventedFunction, + inlinePreventedMethod: inlinePreventedFunction }); var ClassA = UberClass.extend({ diff --git a/test/performance/master.js b/test/performance/master.js index eb1e02b..6f8454c 100644 --- a/test/performance/master.js +++ b/test/performance/master.js @@ -65,7 +65,7 @@ if (cluster.isMaster) { }]; var maxClientProcessCount = 1; - var maxServerProcessCount = require('os').cpus().length; + var maxServerProcessCount = 2;//require('os').cpus().length; async.forEachSeries(jobs, function (job, cb) { async.timesSeries(maxClientProcessCount, function (clientIdx, cb) { diff --git a/test/performance/server-express.js b/test/performance/server-express.js index a562c58..e89c392 100644 --- a/test/performance/server-express.js +++ b/test/performance/server-express.js @@ -1,6 +1,6 @@ -var express = require('express'); +var restify = require('restify'); -var app = express(); +var app = restify(); app.use(app.router); app.all('/example/:action/:id?', function (req, res, next) {