Create a gist now

Instantly share code, notes, and snippets.

@gmalysa /opt_test.js
Last active Dec 20, 2015

What would you like to do?
Test of some simple function optimizations for javascript/nodejs.
// Test 1: Do simple functions get inlined?
var iter = 10000;
var start, stop;
function inc(a) { return a+1; }
// Wrap test in a function to allow warmup of optimizer
function test1_fn(iter) {
var x;
for (var i = 0; i < iter; ++i) {
x = inc(x);
}
}
// Warmup, then run real test
test1_fn();
start = process.hrtime();
test1_fn();
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T1//with function: '+time+' ms');
function test1_inline(iter) {
var x;
for (var i = 0; i < iter; ++i) {
x = x + 1;
}
}
test1_inline(iter);
start = process.hrtime();
test1_inline(iter);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T1//hand inlined: '+time+' ms');
console.log('');
// On my machine, ~ 0.4670 ms vs. 0.0365 ms
// Therefore: the answer is no.
// Test 2: Iteration methods vs. for loops
iter = 1000;
var elems = 100, subelems = 10, sum;
var xa = [];
for (i = 0; i < elems; ++i) {
xa[i] = Math.random();
}
// 2a: array sum with reduce
function test2a_reduce() {
var sum;
sum = xa.reduce(function(memo, v) {
return memo + v;
}, 0);
return sum;
}
sum = test2a_reduce();
start = process.hrtime();
sum = test2a_reduce();
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T2A//using reduce: '+time+' ms');
function test2a_loop() {
var sum = 0;
for (var i = 0; i < xa.length; ++i) {
sum += xa[i];
}
return sum;
}
sum = test2a_loop();
start = process.hrtime();
sum = test2a_loop();
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T2A//for loop: '+time+' ms');
console.log('');
// On my machine, times fluctuate a lot, but 0.10-0.16 ms vs 0.0054-0.0078 ms
// so the for loop is faster again
// 2b: nested iteration, calling a member function for each element of an array
function t2b_class() {
this.val = Math.random();
}
var t2b_sum = 0;
t2b_class.prototype.work = function() {
t2b_sum += this.val;
};
for (i = 0; i < elems; ++i) {
xa[i] = [];
for (j = 0; j < elems; ++j) {
xa[i][j] = new t2b_class();
}
}
function test2b_nested(xa) {
xa.forEach(function(v) {
v.forEach(function(v2) {
v2.work();
});
});
}
test2b_nested(xa);
start = process.hrtime();
test2b_nested(xa);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T2B//nested iterators: '+time+' ms');
function inner_iter(v2) {
v2.work();
}
function test2b_inner(xa) {
xa.forEach(function(v) {
v.forEach(inner_iter);
});
}
test2b_inner(xa);
start = process.hrtime();
test2b_inner(xa);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T2B//nested iterators, avoids redeclaration: '+time+' ms');
function test2b_loop(xa) {
for (var i = 0; i < xa.length; ++i) {
for (var j = 0; j < xa[i].length; ++j) {
xa[i][j].work();
}
}
}
test2b_loop(xa);
start = process.hrtime();
test2b_loop(xa);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T2B//for loop: '+time+' ms');
console.log('');
// Again, some fluctuation, but roughly 1.08-1.32 ms vs 0.26-0.35 ms on my machine
// Test 3: Tail calls vs. stack walking vs. iterative method
function fact_tail(n, accumulator) {
if (n < 2)
return accumulator;
return fact_tail(n-1, accumulator*n);
}
function fact_stack(n) {
if (n < 2)
return 1;
return n * fact_stack(n-1);
}
function fact_iter(n) {
var accumulator = n;
for (n = n-1; n > 1; --n) {
accumulator = accumulator*n;
}
return accumulator;
}
iter = 1000;
var n = 30, result;
function test3_tail(iter, n) {
var result;
for (var i = 0; i < iter; ++i) {
result = fact_tail(n-1, n);
}
}
test3_tail(iter, n);
start = process.hrtime();
test3_tail(iter, n);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T3//Tail Factorial: '+time+' ms');
function test3_stack(iter, n) {
var result;
for (var i = 0; i < iter; ++i) {
result = fact_stack(n);
}
}
test3_stack(iter, n);
start = process.hrtime();
test3_stack(iter, n);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T3//Stack Factorial: '+time+' ms');
function test3_iter(iter, n) {
var result;
for (var i = 0; i < iter; ++i) {
result = fact_iter(n);
}
}
test3_iter(iter, n);
start = process.hrtime();
test3_iter(iter, n);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T3//Iterative Factorial: '+time+' ms');
console.log('');
// Times: 0.277 ms (tail) vs. 0.515 (stack) vs. 0.181 (iterative)
// Tail and iterative should be the same time if tail calls are fully optimized
// Test 4: Function table overhead
function TestClass(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
}
TestClass.prototype.hypot = function() {
return Math.sqrt(this.a*this.a + this.b*this.b + this.c*this.c);
}
iter = 10000;
var fn = TestClass.prototype.hypot;
xa = new TestClass(Math.random(), Math.random(), Math.random());
function test4_object(iter, xa) {
var result;
for (var i = 0; i < iter; ++i) {
result = xa.hypot();
}
}
test4_object(iter, xa);
start = process.hrtime();
test4_object(iter, xa);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T4//Object method lookup: '+time+' ms');
function test4_prototype(iter, xa) {
var result;
for (var i = 0; i < iter; ++i) {
result = TestClass.prototype.hypot.call(xa);
}
}
test4_prototype(iter, xa);
start = process.hrtime();
test4_prototype(iter, xa);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T4//Prototype method lookup: '+time+' ms');
function test4_pointer(iter, xa) {
var result;
for (var i = 0; i < iter; ++i) {
result = fn.call(xa);
}
}
test4_pointer(iter, xa);
start = process.hrtime();
test4_pointer(iter, xa);
stop = process.hrtime(start);
time = (stop[0]*1e9 + stop[1])/1e6;
console.log('T4//Pre-saved method pointer: '+time+' ms');
console.log('');
// 0.194 ms (lookup) vs 0.172 ms (prototype) vs 0.135 ms (presaved)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment