var time = use('time') var math = use('math/radians') def iterations = { simple: 10000000, medium: 1000000, complex: 100000 }; function measureTime(fn) { var start = time.number(); fn(); var end = time.number(); return (end - start); } function benchPropertyAccess() { var obj = { a: 1, b: 2, c: 3, d: 4, e: 5, nested: { x: 10, y: 20, z: 30 } }; var readTime = measureTime(function() { var sum = 0; var i = 0 for (i = 0; i < iterations.simple; i++) { sum += obj.a + obj.b + obj.c + obj.d + obj.e; sum += obj.nested.x + obj.nested.y + obj.nested.z; } }); var writeTime = measureTime(function() { var i = 0 for (i = 0; i < iterations.simple; i++) { obj.a = i; obj.b = i + 1; obj.c = i + 2; obj.nested.x = i * 2; obj.nested.y = i * 3; } }); return { readTime: readTime, writeTime: writeTime }; } function benchFunctionCalls() { function add(a, b) { return a + b; } function multiply(a, b) { return a * b; } function complexCalc(a, b, c) { return (a + b) * c / 2; } var obj = { method: function(x) { return x * 2; }, nested: { deepMethod: function(x, y) { return x + y; } } }; var simpleCallTime = measureTime(function() { var result = 0; var i = 0 for (i = 0; i < iterations.simple; i++) { result = add(i, 1); result = multiply(result, 2); } }); var methodCallTime = measureTime(function() { var result = 0; var i = 0 for (i = 0; i < iterations.simple; i++) { result = obj.method(i); result = obj.nested.deepMethod(result, i); } }); var complexCallTime = measureTime(function() { var result = 0; var i = 0 for (i = 0; i < iterations.medium; i++) { result = complexCalc(i, i + 1, i + 2); } }); return { simpleCallTime: simpleCallTime, methodCallTime: methodCallTime, complexCallTime: complexCallTime }; } function benchArrayOps() { var i = 0 var pushTime = measureTime(function() { var arr = []; var j = 0 for (j = 0; j < iterations.medium; j++) { arr[] = j; } }); var arr = []; for (i = 0; i < 10000; i++) arr[] = i; var accessTime = measureTime(function() { var sum = 0; var j = 0 for (j = 0; j < iterations.medium; j++) { sum += arr[j % 10000]; } }); var iterateTime = measureTime(function() { var sum = 0; var j = 0 var k = 0 for (j = 0; j < 1000; j++) { for (k = 0; k < length(arr); k++) { sum += arr[k]; } } }); return { pushTime: pushTime, accessTime: accessTime, iterateTime: iterateTime }; } function benchObjectCreation() { var literalTime = measureTime(function() { var i = 0 var obj = null for (i = 0; i < iterations.medium; i++) { obj = { x: i, y: i * 2, z: i * 3 }; } }); function Point(x, y) { return {x,y} } var defructorTime = measureTime(function() { var i = 0 var p = null for (i = 0; i < iterations.medium; i++) { p = Point(i, i * 2); } }); var protoObj = { x: 0, y: 0, move: function(dx, dy) { this.x += dx; this.y += dy; } }; var prototypeTime = measureTime(function() { var i = 0 var obj = null for (i = 0; i < iterations.medium; i++) { obj = meme(protoObj); obj.x = i; obj.y = i * 2; } }); return { literalTime: literalTime, defructorTime: defructorTime, prototypeTime: prototypeTime }; } function benchStringOps() { var i = 0 var strings = []; var concatTime = measureTime(function() { var str = ""; var j = 0 for (j = 0; j < iterations.complex; j++) { str = "test" + j + "value"; } }); for (i = 0; i < 1000; i++) { strings[] = "string" + i; } var joinTime = measureTime(function() { var j = 0 var result = null for (j = 0; j < iterations.complex; j++) { result = text(strings, ","); } }); var splitTime = measureTime(function() { var str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p"; var j = 0 var parts = null for (j = 0; j < iterations.medium; j++) { parts = array(str, ","); } }); return { concatTime: concatTime, joinTime: joinTime, splitTime: splitTime }; } function benchArithmetic() { var intMathTime = measureTime(function() { var result = 1; var i = 0 for (i = 0; i < iterations.simple; i++) { result = ((result + i) * 2 - 1) / 3; result = result % 1000 + 1; } }); var floatMathTime = measureTime(function() { var result = 1.5; var i = 0 for (i = 0; i < iterations.simple; i++) { result = math.sine(result) + math.cosine(i * 0.01); result = math.sqrt(abs(result)) + 0.1; } }); var bitwiseTime = measureTime(function() { var result = 0; var i = 0 for (i = 0; i < iterations.simple; i++) { result = (result ^ i) & 0xFFFF; result = (result << 1) | (result >> 15); } }); return { intMathTime: intMathTime, floatMathTime: floatMathTime, bitwiseTime: bitwiseTime }; } function benchClosures() { var i = 0 function makeAdder(x) { return function(y) { return x + y; }; } var closureCreateTime = measureTime(function() { var funcs = []; var j = 0 for (j = 0; j < iterations.medium; j++) { funcs[] = makeAdder(j); } }); var adders = []; for (i = 0; i < 1000; i++) { adders[] = makeAdder(i); } var closureCallTime = measureTime(function() { var sum = 0; var j = 0 for (j = 0; j < iterations.medium; j++) { sum += adders[j % 1000](j); } }); return { closureCreateTime: closureCreateTime, closureCallTime: closureCallTime }; } log.console("JavaScript Performance Benchmark"); log.console("======================\n"); log.console("BENCHMARK: Property Access"); var propResults = benchPropertyAccess(); log.console(" Read time: " + propResults.readTime.toFixed(3) + "s => " + (iterations.simple / propResults.readTime).toFixed(1) + " reads/sec [" + (propResults.readTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(" Write time: " + propResults.writeTime.toFixed(3) + "s => " + (iterations.simple / propResults.writeTime).toFixed(1) + " writes/sec [" + (propResults.writeTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(""); log.console("BENCHMARK: Function Calls"); var funcResults = benchFunctionCalls(); log.console(" Simple calls: " + funcResults.simpleCallTime.toFixed(3) + "s => " + (iterations.simple / funcResults.simpleCallTime).toFixed(1) + " calls/sec [" + (funcResults.simpleCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(" Method calls: " + funcResults.methodCallTime.toFixed(3) + "s => " + (iterations.simple / funcResults.methodCallTime).toFixed(1) + " calls/sec [" + (funcResults.methodCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(" Complex calls: " + funcResults.complexCallTime.toFixed(3) + "s => " + (iterations.medium / funcResults.complexCallTime).toFixed(1) + " calls/sec [" + (funcResults.complexCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(""); log.console("BENCHMARK: Array Operations"); var arrayResults = benchArrayOps(); log.console(" Push: " + arrayResults.pushTime.toFixed(3) + "s => " + (iterations.medium / arrayResults.pushTime).toFixed(1) + " pushes/sec [" + (arrayResults.pushTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(" Access: " + arrayResults.accessTime.toFixed(3) + "s => " + (iterations.medium / arrayResults.accessTime).toFixed(1) + " accesses/sec [" + (arrayResults.accessTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(" Iterate: " + arrayResults.iterateTime.toFixed(3) + "s => " + (1000 / arrayResults.iterateTime).toFixed(1) + " full iterations/sec"); log.console(""); log.console("BENCHMARK: Object Creation"); var objResults = benchObjectCreation(); log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " + (iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" + (objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(" Constructor: " + objResults.defructorTime.toFixed(3) + "s => " + (iterations.medium / objResults.defructorTime).toFixed(1) + " creates/sec [" + (objResults.defructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " + (iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" + (objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(""); log.console("BENCHMARK: String Operations"); var strResults = benchStringOps(); log.console(" Concat: " + strResults.concatTime.toFixed(3) + "s => " + (iterations.complex / strResults.concatTime).toFixed(1) + " concats/sec [" + (strResults.concatTime / iterations.complex * 1e9).toFixed(1) + " ns/op]"); log.console(" Join: " + strResults.joinTime.toFixed(3) + "s => " + (iterations.complex / strResults.joinTime).toFixed(1) + " joins/sec [" + (strResults.joinTime / iterations.complex * 1e9).toFixed(1) + " ns/op]"); log.console(" Split: " + strResults.splitTime.toFixed(3) + "s => " + (iterations.medium / strResults.splitTime).toFixed(1) + " splits/sec [" + (strResults.splitTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(""); log.console("BENCHMARK: Arithmetic Operations"); var mathResults = benchArithmetic(); log.console(" Integer math: " + mathResults.intMathTime.toFixed(3) + "s => " + (iterations.simple / mathResults.intMathTime).toFixed(1) + " ops/sec [" + (mathResults.intMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(" Float math: " + mathResults.floatMathTime.toFixed(3) + "s => " + (iterations.simple / mathResults.floatMathTime).toFixed(1) + " ops/sec [" + (mathResults.floatMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(" Bitwise: " + mathResults.bitwiseTime.toFixed(3) + "s => " + (iterations.simple / mathResults.bitwiseTime).toFixed(1) + " ops/sec [" + (mathResults.bitwiseTime / iterations.simple * 1e9).toFixed(1) + " ns/op]"); log.console(""); log.console("BENCHMARK: Closures"); var closureResults = benchClosures(); log.console(" Create: " + closureResults.closureCreateTime.toFixed(3) + "s => " + (iterations.medium / closureResults.closureCreateTime).toFixed(1) + " creates/sec [" + (closureResults.closureCreateTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(" Call: " + closureResults.closureCallTime.toFixed(3) + "s => " + (iterations.medium / closureResults.closureCallTime).toFixed(1) + " calls/sec [" + (closureResults.closureCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]"); log.console(""); log.console("---------------------------------------------------------"); log.console("Benchmark complete.\n"); $stop()