From b9f2c1ac39b6ef5df3897c70dbce1756a07b86df Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 11 Jul 2014 01:59:40 -0400 Subject: [PATCH] observer benchmarks and tests --- benchmarks/observer.js | 306 +++++++++++++++++++++++++++++++++++++++++ test/unit/observer.js | 76 +++++++--- 2 files changed, 366 insertions(+), 16 deletions(-) create mode 100644 benchmarks/observer.js diff --git a/benchmarks/observer.js b/benchmarks/observer.js new file mode 100644 index 000000000..5fd2c6f6f --- /dev/null +++ b/benchmarks/observer.js @@ -0,0 +1,306 @@ +var Observer = require('../src/observer/observer') +var Emitter = require('../src/emitter') +var OldObserver = require('../../vue/src/observer') +var sideEffect = 0 +var runs = 1000 +function cb () { + sideEffect++ +} + +var loadTime = getNano() + +function getNano () { + var hr = process.hrtime() + return hr[0] * 1e9 + hr[1] +} + +function now () { + return (getNano() - loadTime) / 1e6 +} + +function bench (desc, fac, run) { + var objs = [] + for (var i = 0; i < runs; i++) { + objs.push(fac(i)) + } + var s = now() + for (var i = 0; i < runs; i++) { + run(objs[i]) + } + var passed = now() - s + console.log(desc + ' - ' + (16 / (passed / runs)).toFixed(2) + ' ops/frame') +} + +bench( + 'observe (simple object) ', + function (i) { + return {a:i} + }, + function (o) { + new Observer().observe('', o) + } +) + +bench( + 'old observe (simple object) ', + function (i) { + return {a:i} + }, + function (o) { + OldObserver.observe(o, '', new Emitter()) + } +) + +bench( + 'observe (3 nested objects) ', + function (i) { + return {a:{b:{c:i}}} + }, + function (o) { + new Observer().observe('', o) + } +) + +bench( + 'old observe (3 nested objects) ', + function (i) { + return {a:{b:{c:i}}} + }, + function (o) { + OldObserver.observe(o, '', new Emitter()) + } +) + +bench( + 'observe (array, 3 objects) ', + function (i) { + return [{a:i}, {a:i+1}, {a:i+2}] + }, + function (o) { + new Observer().observe('', o) + } +) + +bench( + 'old observe (array, 3 objects) ', + function (i) { + return [{a:i}, {a:i+1}, {a:i+2}] + }, + function (o) { + OldObserver.observe(o, '', new Emitter()) + } +) + +bench( + 'observe (array, 30 objects) ', + function () { + var a = [], i = 30 + while (i--) { + a.push({a:i}) + } + return a + }, + function (o) { + new Observer().observe('', o) + } +) + +bench( + 'old observe (array, 30 objects)', + function () { + var a = [], i = 30 + while (i--) { + a.push({a:i}) + } + return a + }, + function (o) { + OldObserver.observe(o, '', new Emitter()) + } +) + +Observer.emitGet = true +OldObserver.shouldGet = true + +bench( + 'simple get ', + function () { + var a = {a:1} + var ob = new Observer() + ob.observe('', a) + ob.on('get', cb) + return a + }, + function (o) { + var v = o.a + } +) + +bench( + 'old simple get', + function () { + var a = {a:1} + var ob = new Emitter() + OldObserver.observe(a, '', ob) + ob.on('get', cb) + return a + }, + function (o) { + var v = o.a + } +) + +bench( + 'nested get ', + function () { + var a = {a:{b:{c:1}}} + var ob = new Observer() + ob.observe('', a) + ob.on('get', cb) + return a + }, + function (o) { + var v = o.a.b.c + } +) + +bench( + 'old nested get', + function () { + var a = {a:{b:{c:1}}} + var ob = new Emitter() + OldObserver.observe(a, '', ob) + ob.on('get', cb) + return a + }, + function (o) { + var v = o.a.b.c + } +) + +Observer.emitGet = false +OldObserver.shouldGet = false + +bench( + 'simple set ', + function () { + var a = {a:1} + var ob = new Observer() + ob.observe('', a) + ob.on('set', cb) + return a + }, + function (o) { + o.a = 12345 + } +) + +bench( + 'old simple set', + function () { + var a = {a:1} + var ob = new Emitter() + OldObserver.observe(a, '', ob) + ob.on('set', cb) + return a + }, + function (o) { + o.a = 12345 + } +) + +bench( + 'nested set ', + function () { + var a = {a:{b:{c:1}}} + var ob = new Observer() + ob.observe('', a) + ob.on('set', cb) + return a + }, + function (o) { + o.a.b.c = 2 + } +) + +bench( + 'old nested set', + function () { + var a = {a:{b:{c:1}}} + var ob = new Emitter() + OldObserver.observe(a, '', ob) + ob.on('set', cb) + return a + }, + function (o) { + o.a.b.c = 2 + } +) + +bench( + 'array mutation (5 objects) ', + function () { + var a = [], i = 5 + while (i--) { + a.push({a:i}) + } + var ob = new Observer() + ob.observe('', a) + ob.on('mutation', cb) + return a + }, + function (o) { + o.reverse() + } +) + +bench( + 'old array mutation (5 objects) ', + function () { + var a = [], i = 5 + while (i--) { + a.push({a:i}) + } + var ob = new Emitter() + OldObserver.observe(a, '', ob) + ob.on('mutation', cb) + return a + }, + function (o) { + o.reverse() + } +) + +bench( + 'array mutation (50 objects) ', + function () { + var a = [], i = 50 + while (i--) { + a.push({a:i}) + } + var ob = new Observer() + ob.observe('', a) + ob.on('mutation', cb) + return a + }, + function (o) { + o.reverse() + } +) + +bench( + 'old array mutation (50 objects)', + function () { + var a = [], i = 50 + while (i--) { + a.push({a:i}) + } + var ob = new Emitter() + OldObserver.observe(a, '', ob) + ob.on('mutation', cb) + return a + }, + function (o) { + o.reverse() + } +) \ No newline at end of file diff --git a/test/unit/observer.js b/test/unit/observer.js index e94ebf472..f753e5e01 100644 --- a/test/unit/observer.js +++ b/test/unit/observer.js @@ -1,18 +1,18 @@ var Observer = require('../../src/observer/observer') -var delimiter = Observer.pathDelimiter - -function path (p) { - return p.replace(/\./g, '\b') -} +var u = undefined +Observer.pathDelimiter = '.' describe('Observer', function () { var spy beforeEach(function () { - spy = jasmine.createSpy() + spy = jasmine.createSpy('observer') }) it('get', function () { + + Observer.emitGet = true + var obj = { a: 1, b: { @@ -23,13 +23,15 @@ describe('Observer', function () { ob.on('get', spy) var t = obj.a - expect(spy).toHaveBeenCalledWith('a', undefined, undefined) + expect(spy).toHaveBeenCalledWith('a', u, u) expect(spy.callCount).toEqual(1) t = obj.b.c - expect(spy).toHaveBeenCalledWith('b', undefined, undefined) - expect(spy).toHaveBeenCalledWith(path('b.c'), undefined, undefined) + expect(spy).toHaveBeenCalledWith('b', u, u) + expect(spy).toHaveBeenCalledWith('b.c', u, u) expect(spy.callCount).toEqual(3) + + Observer.emitGet = false }) it('set', function () { @@ -43,20 +45,27 @@ describe('Observer', function () { ob.on('set', spy) obj.a = 3 - expect(spy).toHaveBeenCalledWith('a', 3, undefined) + expect(spy).toHaveBeenCalledWith('a', 3, u) expect(spy.callCount).toEqual(1) obj.b.c = 4 - expect(spy).toHaveBeenCalledWith(path('b.c'), 4, undefined) + expect(spy).toHaveBeenCalledWith('b.c', 4, u) expect(spy.callCount).toEqual(2) var newB = { c: 5 } obj.b = newB - expect(spy).toHaveBeenCalledWith('b', newB, undefined) + expect(spy).toHaveBeenCalledWith('b', newB, u) + expect(spy.callCount).toEqual(3) + + // same value set should not emit events + obj.a = 3 expect(spy.callCount).toEqual(3) }) it('array get', function () { + + Observer.emitGet = true + var obj = { arr: [{a:1}, {a:2}] } @@ -64,17 +73,52 @@ describe('Observer', function () { ob.on('get', spy) var t = obj.arr[0].a - expect(spy).toHaveBeenCalledWith(path('arr'), undefined, undefined) - expect(spy).toHaveBeenCalledWith(path('arr.0.a'), undefined, undefined) + expect(spy).toHaveBeenCalledWith('arr', u, u) + expect(spy).toHaveBeenCalledWith('arr.0.a', u, u) expect(spy.callCount).toEqual(2) + + Observer.emitGet = false }) it('array set', function () { - // body... + var obj = { + arr: [{a:1}, {a:2}] + } + var ob = Observer.create(obj) + ob.on('set', spy) + + obj.arr[0].a = 2 + expect(spy).toHaveBeenCalledWith('arr.0.a', 2, u) + + // set events after mutation + obj.arr.reverse() + obj.arr[0].a = 3 + expect(spy).toHaveBeenCalledWith('arr.0.a', 3, u) }) it('array mutate', function () { - // body... + var arr = [{a:1}, {a:2}] + var ob = Observer.create(arr) + + ob.on('mutate', spy) + arr.push({a:3}) + expect(spy.mostRecentCall.args[0]).toEqual('') + expect(spy.mostRecentCall.args[1]).toEqual(arr) + var mutation = spy.mostRecentCall.args[2] + expect(mutation).toBeDefined() + expect(mutation.method).toEqual('push') + expect(mutation.index).toEqual(2) + expect(mutation.inserted.length).toEqual(1) + expect(mutation.inserted[0]).toEqual(arr[2]) + }) + + it('array set after mutate', function () { + var arr = [{a:1}, {a:2}] + var ob = Observer.create(arr) + ob.on('set', spy) + arr.push({a:3}) + arr[2].a = 4 + expect(spy).toHaveBeenCalledWith('2.a', 4, u) }) it('object.$add', function () {