Skip to content

Commit ecafcce

Browse files
koresarFredyC
authored andcommitted
Obj create perf fix (#270)
* Fix property access perf. Fix some JSDoc comments. Run CI tests for node v7 too. Add benchmarking test, make it run in CI only. * Turns out Travis already declares that var by default: https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables * Minor additions to the JSDoc
1 parent eb96581 commit ecafcce

File tree

8 files changed

+129
-10
lines changed

8 files changed

+129
-10
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ node_js:
33
- "4"
44
- "5"
55
- "6"
6+
- "7"
67
script:
78
- npm run check

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"devDependencies": {
2929
"babel-cli": "^6.10.1",
3030
"babel-preset-es2015": "^6.16.0",
31+
"benchmark": "^2.1.1",
3132
"check-compose": "^3.2.0",
3233
"dependency-check": "^2.5.0",
3334
"eslint": "^3.7.0",

src/compose.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ const assign = Object.assign;
77

88
/**
99
* Creates new factory instance.
10-
* @param {object} descriptor The information about the object the factory will be creating.
10+
* @param {Descriptor} descriptor The information about the object the factory will be creating.
1111
* @returns {Function} The new factory function.
1212
*/
1313
function createFactory(descriptor) {
1414
return function Stamp(options, ...args) {
15-
const obj = Object.create(descriptor.methods || {});
15+
// Next line was optimized for most JS VMs. Please, be careful here!
16+
const obj = Object.create(descriptor.methods || null);
1617

1718
merge(obj, descriptor.deepProperties);
1819
assign(obj, descriptor.properties);
@@ -31,9 +32,9 @@ function createFactory(descriptor) {
3132

3233
/**
3334
* Returns a new stamp given a descriptor and a compose function implementation.
34-
* @param {object} [descriptor={}] The information about the object the stamp will be creating.
35-
* @param {Function} composeFunction The "compose" function implementation.
36-
* @returns {Function}
35+
* @param {Descriptor} [descriptor={}] The information about the object the stamp will be creating.
36+
* @param {Compose} composeFunction The "compose" function implementation.
37+
* @returns {Stamp}
3738
*/
3839
function createStamp(descriptor, composeFunction) {
3940
const Stamp = createFactory(descriptor);
@@ -53,9 +54,10 @@ function createStamp(descriptor, composeFunction) {
5354

5455
/**
5556
* Mutates the dstDescriptor by merging the srcComposable data into it.
56-
* @param {object} dstDescriptor The descriptor object to merge into.
57-
* @param {object} [srcComposable] The composable (either descriptor or stamp) to merge data form.
58-
* @returns {object} Returns the dstDescriptor argument.
57+
* @param {Descriptor} dstDescriptor The descriptor object to merge into.
58+
* @param {Composable} [srcComposable] The composable
59+
* (either descriptor or stamp) to merge data form.
60+
* @returns {Descriptor} Returns the dstDescriptor argument.
5961
*/
6062
function mergeComposable(dstDescriptor, srcComposable) {
6163
const srcDescriptor = (srcComposable && srcComposable.compose) || srcComposable;
@@ -91,7 +93,8 @@ function mergeComposable(dstDescriptor, srcComposable) {
9193
/**
9294
* Given the list of composables (stamp descriptors and stamps) returns
9395
* a new stamp (composable factory function).
94-
* @param {...(object|Function)} [composables] The list of composables.
96+
* @typedef {Function} Compose
97+
* @param {...(Composable)} [composables] The list of composables.
9598
* @returns {Stamp} A new stamp (aka composable factory function)
9699
*/
97100
export default function compose(...composables) {
@@ -125,3 +128,9 @@ export default function compose(...composables) {
125128
* @returns {*} Instantiated object
126129
* @property {Descriptor} compose - The Stamp descriptor and composition function
127130
*/
131+
132+
/**
133+
* A composable object - stamp or descriptor
134+
* @typedef {Stamp|Descriptor} Composable
135+
*/
136+

src/isStamp.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import isFunction from './isFunction';
22

3+
/**
4+
* Returns true if argument is a stamp.
5+
* @param {*} obj
6+
* @returns {Boolean}
7+
*/
38
export default function isStamp(obj) {
49
return isFunction(obj) && isFunction(obj.compose);
510
}

src/stampit.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ const baseStampit = compose(
9696

9797
/**
9898
* Infected compose
99+
* @param {...(Composable)} [args] The list of composables.
99100
* @return {Stamp}
100101
*/
101102
function stampit(...args) {

src/standardise-descriptor.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,28 @@ import merge from './merge';
44

55
const assign = Object.assign;
66

7+
/**
8+
* Converts stampit extended descriptor to a standard one.
9+
* @param [methods]
10+
* @param [properties]
11+
* @param [props]
12+
* @param [refs]
13+
* @param [initializers]
14+
* @param [init]
15+
* @param [deepProperties]
16+
* @param [deepProps]
17+
* @param [propertyDescriptors]
18+
* @param [staticProperties]
19+
* @param [statics]
20+
* @param [staticDeepProperties]
21+
* @param [deepStatics]
22+
* @param [staticPropertyDescriptors]
23+
* @param [configuration]
24+
* @param [conf]
25+
* @param [deepConfiguration]
26+
* @param [deepConf]
27+
* @returns {Descriptor}
28+
*/
729
export default function ({
830
methods,
931

test/benchmark/property-access.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import test from 'tape';
2+
import _ from 'lodash';
3+
4+
const Benchmark = require('benchmark');
5+
const stampit = require('../..'); // Need to test the distributable
6+
7+
8+
test('benchmarking property access', (t) => {
9+
const Position = stampit({
10+
init() {
11+
this.x = 10;
12+
this.y = 10;
13+
}
14+
});
15+
16+
const Velocity = stampit({
17+
init() {
18+
this.vx = 10;
19+
this.vy = 10;
20+
}
21+
});
22+
23+
const Entity = stampit({
24+
deepProps: {
25+
components: {}
26+
},
27+
init() {
28+
this.components.position = Position();
29+
this.components.velocity = Velocity();
30+
}
31+
});
32+
33+
const COUNT = 5000;
34+
const stampitEntities = _.range(COUNT).map(() => Entity());
35+
const normalEntities = _.range(COUNT).map(() => ({
36+
components: {
37+
position: {
38+
x: 10,
39+
y: 10,
40+
},
41+
velocity: {
42+
vy: 10,
43+
vx: 10,
44+
},
45+
},
46+
}));
47+
48+
function runArray(arr) {
49+
const DELTA = 16;
50+
for (let i = 0; i < COUNT; i += 1) {
51+
const entity = arr[i].components;
52+
53+
entity.position.x += (DELTA / 1000) * entity.velocity.vx;
54+
entity.position.y += (DELTA / 1000) * entity.velocity.vy;
55+
}
56+
}
57+
58+
const suite = new Benchmark.Suite();
59+
const results = [];
60+
suite
61+
.add('Stampit', () => {
62+
runArray(stampitEntities);
63+
})
64+
.add('Plain object', () => {
65+
runArray(normalEntities);
66+
})
67+
.on('cycle', (event) => {
68+
results.push(String(event.target));
69+
})
70+
.on('complete', function () {
71+
t.ok(this[0].hz / this[1].hz >= 0.5,
72+
'object instances property access must be as fast as plain object.');
73+
t.comment(results.join('. '));
74+
t.end();
75+
})
76+
.run();
77+
});

test/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
require('require-all')(__dirname);
1+
require('require-all')({
2+
dirname: __dirname,
3+
excludeDirs: process.env.CI ? /^\./ : /^\.|benchmark/
4+
});

0 commit comments

Comments
 (0)