var time = use('time') var math = use('math/radians') //////////////////////////////////////////////////////////////////////////////// // JavaScript Performance Benchmark Suite // Tests core JS operations: property access, function calls, arithmetic, etc. //////////////////////////////////////////////////////////////////////////////// // Test configurations def iterations = { simple: 10000000, medium: 1000000, complex: 100000 }; //////////////////////////////////////////////////////////////////////////////// // Utility: measureTime(fn) => how long fn() takes in seconds //////////////////////////////////////////////////////////////////////////////// function measureTime(fn) { var start = time.number(); fn(); var end = time.number(); return (end - start); } //////////////////////////////////////////////////////////////////////////////// // Benchmark: Property Access //////////////////////////////////////////////////////////////////////////////// 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; for (var 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() { for (var 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 }; } //////////////////////////////////////////////////////////////////////////////// // Benchmark: Function Calls //////////////////////////////////////////////////////////////////////////////// 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; for (var i = 0; i < iterations.simple; i++) { result = add(i, 1); result = multiply(result, 2); } }); var methodCallTime = measureTime(function() { var result = 0; for (var i = 0; i < iterations.simple; i++) { result = obj.method(i); result = obj.nested.deepMethod(result, i); } }); var complexCallTime = measureTime(function() { var result = 0; for (var i = 0; i < iterations.medium; i++) { result = complexCalc(i, i + 1, i + 2); } }); return { simpleCallTime: simpleCallTime, methodCallTime: methodCallTime, complexCallTime: complexCallTime }; } //////////////////////////////////////////////////////////////////////////////// // Benchmark: Array Operations //////////////////////////////////////////////////////////////////////////////// function benchArrayOps() { var pushTime = measureTime(function() { var arr = []; for (var i = 0; i < iterations.medium; i++) { arr.push(i); } }); var arr = []; for (var i = 0; i < 10000; i++) arr.push(i); var accessTime = measureTime(function() { var sum = 0; for (var i = 0; i < iterations.medium; i++) { sum += arr[i % 10000]; } }); var iterateTime = measureTime(function() { var sum = 0; for (var j = 0; j < 1000; j++) { for (var i = 0; i < length(arr); i++) { sum += arr[i]; } } }); return { pushTime: pushTime, accessTime: accessTime, iterateTime: iterateTime }; } //////////////////////////////////////////////////////////////////////////////// // Benchmark: Object Creation //////////////////////////////////////////////////////////////////////////////// function benchObjectCreation() { var literalTime = measureTime(function() { for (var i = 0; i < iterations.medium; i++) { var obj = { x: i, y: i * 2, z: i * 3 }; } }); function Point(x, y) { this.x = x; this.y = y; } var defructorTime = measureTime(function() { for (var i = 0; i < iterations.medium; i++) { var p = new Point(i, i * 2); } }); var protoObj = { x: 0, y: 0, move: function(dx, dy) { this.x += dx; this.y += dy; } }; var prototypeTime = measureTime(function() { for (var i = 0; i < iterations.medium; i++) { var obj = meme(protoObj); obj.x = i; obj.y = i * 2; } }); return { literalTime: literalTime, defructorTime: defructorTime, prototypeTime: prototypeTime }; } //////////////////////////////////////////////////////////////////////////////// // Benchmark: String Operations //////////////////////////////////////////////////////////////////////////////// function benchStringOps() { var concatTime = measureTime(function() { var str = ""; for (var i = 0; i < iterations.complex; i++) { str = "test" + i + "value"; } }); var strings = []; for (var i = 0; i < 1000; i++) { strings.push("string" + i); } var joinTime = measureTime(function() { for (var i = 0; i < iterations.complex; i++) { var 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"; for (var i = 0; i < iterations.medium; i++) { var parts = array(str, ","); } }); return { concatTime: concatTime, joinTime: joinTime, splitTime: splitTime }; } //////////////////////////////////////////////////////////////////////////////// // Benchmark: Arithmetic Operations //////////////////////////////////////////////////////////////////////////////// function benchArithmetic() { var intMathTime = measureTime(function() { var result = 1; for (var i = 0; i < iterations.simple; i++) { result = ((result + i) * 2 - 1) / 3; result = result % 1000 + 1; } }); var floatMathTime = measureTime(function() { var result = 1.5; for (var 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; for (var i = 0; i < iterations.simple; i++) { result = (result ^ i) & 0xFFFF; result = (result << 1) | (result >> 15); } }); return { intMathTime: intMathTime, floatMathTime: floatMathTime, bitwiseTime: bitwiseTime }; } //////////////////////////////////////////////////////////////////////////////// // Benchmark: Closure Operations //////////////////////////////////////////////////////////////////////////////// function benchClosures() { function makeAdder(x) { return function(y) { return x + y; }; } var closureCreateTime = measureTime(function() { var funcs = []; for (var i = 0; i < iterations.medium; i++) { funcs.push(makeAdder(i)); } }); var adders = []; for (var i = 0; i < 1000; i++) { adders.push(makeAdder(i)); } var closureCallTime = measureTime(function() { var sum = 0; for (var i = 0; i < iterations.medium; i++) { sum += adders[i % 1000](i); } }); return { closureCreateTime: closureCreateTime, closureCallTime: closureCallTime }; } //////////////////////////////////////////////////////////////////////////////// // Main benchmark runner //////////////////////////////////////////////////////////////////////////////// log.console("JavaScript Performance Benchmark"); log.console("======================\n"); // Property Access 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(""); // Function Calls 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(""); // Array Operations 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(""); // Object Creation 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(""); // String Operations 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(""); // Arithmetic Operations 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(""); // Closures 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()