diff --git a/benchmarks/binarytree.ce b/benchmarks/binarytree.ce new file mode 100644 index 00000000..af76d9da --- /dev/null +++ b/benchmarks/binarytree.ce @@ -0,0 +1,43 @@ +function mainThread() { + var maxDepth = Math.max(6, Number(arg[0] || 16)); + + var stretchDepth = maxDepth + 1; + var check = itemCheck(bottomUpTree(stretchDepth)); + log.console(`stretch tree of depth ${stretchDepth}\t check: ${check}`); + + var longLivedTree = bottomUpTree(maxDepth); + + for (let depth = 4; depth <= maxDepth; depth += 2) { + var iterations = 1 << maxDepth - depth + 4; + work(iterations, depth); + } + + log.console(`long lived tree of depth ${maxDepth}\t check: ${itemCheck(longLivedTree)}`); +} + +function work(iterations, depth) { + let check = 0; + for (let i = 0; i < iterations; i++) + check += itemCheck(bottomUpTree(depth)); + log.console(`${iterations}\t trees of depth ${depth}\t check: ${check}`); +} + +function TreeNode(left, right) { + return {left, right}; +} + +function itemCheck(node) { + if (node.left === null) + return 1; + return 1 + itemCheck(node.left) + itemCheck(node.right); +} + +function bottomUpTree(depth) { + return depth > 0 + ? new TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1)) + : new TreeNode(null, null); +} + +mainThread() + +$_.stop() \ No newline at end of file diff --git a/benchmarks/eratosthenes.ce b/benchmarks/eratosthenes.ce new file mode 100644 index 00000000..132fae11 --- /dev/null +++ b/benchmarks/eratosthenes.ce @@ -0,0 +1,24 @@ +var blob = use('blob') + +function eratosthenes (n) { + var sieve = new blob(n, true) + var sqrtN = Math.trunc(Math.sqrt(n)); + + for (i = 2; i <= sqrtN; i++) + if (sieve.read_logical(i)) + for (j = i * i; j <= n; j += i) + sieve.write_bit(j, false); + + return sieve; +} + +var sieve = eratosthenes(10000000); +stone(sieve) + +var c = 0 +for (var i = 0; i < sieve.length; i++) + if (sieve.read_logical(i)) c++ + +log.console(c) + +$_.stop() \ No newline at end of file diff --git a/benchmarks/fannkuch.ce b/benchmarks/fannkuch.ce new file mode 100644 index 00000000..5b045662 --- /dev/null +++ b/benchmarks/fannkuch.ce @@ -0,0 +1,58 @@ +function fannkuch(n) { + var perm1 = [n] + for (let i = 0; i < n; i++) perm1[i] = i + var perm = [n] + var count = [n] + var f = 0, flips = 0, nperm = 0, checksum = 0 + var i, k, r + + r = n + while (r > 0) { + i = 0 + while (r != 1) { count[r-1] = r; r -= 1 } + while (i < n) { perm[i] = perm1[i]; i += 1 } + + // Count flips and update max and checksum + f = 0 + k = perm[0] + while (k != 0) { + i = 0 + while (2*i < k) { + let t = perm[i]; perm[i] = perm[k-i]; perm[k-i] = t + i += 1 + } + k = perm[0] + f += 1 + } + if (f > flips) flips = f + if ((nperm & 0x1) == 0) checksum += f; else checksum -= f + + // Use incremental change to generate another permutation + var more = true + while (more) { + if (r == n) { + log.console( checksum ) + return flips + } + let p0 = perm1[0] + i = 0 + while (i < r) { + let j = i + 1 + perm1[i] = perm1[j] + i = j + } + perm1[r] = p0 + + count[r] -= 1 + if (count[r] > 0) more = false; else r += 1 + } + nperm += 1 + } + return flips; +} + +var n = arg[0] || 10 + +log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`) + +$_.stop() \ No newline at end of file diff --git a/benchmarks/hyperfine_wota_nota_json.sh b/benchmarks/hyperfine_wota_nota_json.sh new file mode 100755 index 00000000..bd392054 --- /dev/null +++ b/benchmarks/hyperfine_wota_nota_json.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Run hyperfine with parameter lists +# This will create a cross-product of all libraries × all scenarios +hyperfine \ +--warmup 3 \ +--runs 20 \ +-i \ +--export-csv wota_vs_nota_vs_json.csv \ +--export-json wota_vs_nota_vs_json.json \ +--export-markdown wota_vs_nota_vs_json.md \ +--parameter-list lib wota,nota,json \ +--parameter-list scen empty,integers,floats,strings,objects,nested,large_array \ +'cell benchmarks/wota_nota_json {lib} {scen}' + + +echo "Benchmark complete! Results saved to:" +echo " - wota_vs_nota_vs_json.csv" +echo " - wota_vs_nota_vs_json.json" +echo " - wota_vs_nota_vs_json.md" \ No newline at end of file diff --git a/benchmarks/js_perf.ce b/benchmarks/js_perf.ce new file mode 100644 index 00000000..49589773 --- /dev/null +++ b/benchmarks/js_perf.ce @@ -0,0 +1,395 @@ +var time = use('time') + +//////////////////////////////////////////////////////////////////////////////// +// JavaScript Performance Benchmark Suite +// Tests core JS operations: property access, function calls, arithmetic, etc. +//////////////////////////////////////////////////////////////////////////////// + +// Test configurations +const 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 < arr.length; 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 constructorTime = 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 = Object.create(protoObj); + obj.x = i; + obj.y = i * 2; + } + }); + + return { + literalTime: literalTime, + constructorTime: constructorTime, + 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 = strings.join(","); + } + }); + + 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 = str.split(","); + } + }); + + 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.sin(result) + Math.cos(i * 0.01); + result = Math.sqrt(Math.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.constructorTime.toFixed(3) + "s => " + + (iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" + + (objResults.constructorTime / 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() diff --git a/benchmarks/mandelbrot.ce b/benchmarks/mandelbrot.ce new file mode 100644 index 00000000..546f8dd1 --- /dev/null +++ b/benchmarks/mandelbrot.ce @@ -0,0 +1,40 @@ +var blob = use('blob') + +var iter = 50, limit = 2.0; +var zr, zi, cr, ci, tr, ti; + +var h = Number(arg[0]) || 500 +var w = h + +log.console(`P4\n${w} ${h}`); + +for (let y = 0; y < h; ++y) { + // Create a blob for the row - we need w bits + var row = new blob(w); + + for (let x = 0; x < w; ++x) { + zr = zi = tr = ti = 0; + cr = 2 * x / w - 1.5; + ci = 2 * y / h - 1; + for (let i = 0; i < iter && (tr + ti <= limit * limit); ++i) { + zi = 2 * zr * zi + ci; + zr = tr - ti + cr; + tr = zr * zr; + ti = zi * zi; + } + + // Write a 1 bit if inside the set, 0 if outside + if (tr + ti <= limit * limit) + row.write_bit(1); + else + row.write_bit(0); + } + + // Convert the blob to stone (immutable) to prepare for output + stone(row) + + // Output the blob data as raw bytes + log.console(text(row, 'b')); +} + +$_.stop() \ No newline at end of file diff --git a/benchmarks/montecarlo.ce b/benchmarks/montecarlo.ce new file mode 100644 index 00000000..e4e7891d --- /dev/null +++ b/benchmarks/montecarlo.ce @@ -0,0 +1,11 @@ +var N = 10000000; +var num = 0; +for (var i = 0; i < N; i ++) { + var x = 2 * $_.random(); + var y = $_.random(); + if (y < Math.sin(x * x)) + num++; +} +log.console(2 * num / N); + +$_.stop() \ No newline at end of file diff --git a/benchmarks/nbody.ce b/benchmarks/nbody.ce new file mode 100644 index 00000000..7df0cf77 --- /dev/null +++ b/benchmarks/nbody.ce @@ -0,0 +1,161 @@ +var PI = Math.PI; +var SOLAR_MASS = 4 * PI * PI; +var DAYS_PER_YEAR = 365.24; + +function Body(x, y, z, vx, vy, vz, mass) { + this.x = x; + this.y = y; + this.z = z; + this.vx = vx; + this.vy = vy; + this.vz = vz; + this.mass = mass; +} + +function Jupiter() { + return new Body( + 4.84143144246472090e+00, + -1.16032004402742839e+00, + -1.03622044471123109e-01, + 1.66007664274403694e-03 * DAYS_PER_YEAR, + 7.69901118419740425e-03 * DAYS_PER_YEAR, + -6.90460016972063023e-05 * DAYS_PER_YEAR, + 9.54791938424326609e-04 * SOLAR_MASS + ); +} + +function Saturn() { + return new Body( + 8.34336671824457987e+00, + 4.12479856412430479e+00, + -4.03523417114321381e-01, + -2.76742510726862411e-03 * DAYS_PER_YEAR, + 4.99852801234917238e-03 * DAYS_PER_YEAR, + 2.30417297573763929e-05 * DAYS_PER_YEAR, + 2.85885980666130812e-04 * SOLAR_MASS + ); +} + +function Uranus() { + return new Body( + 1.28943695621391310e+01, + -1.51111514016986312e+01, + -2.23307578892655734e-01, + 2.96460137564761618e-03 * DAYS_PER_YEAR, + 2.37847173959480950e-03 * DAYS_PER_YEAR, + -2.96589568540237556e-05 * DAYS_PER_YEAR, + 4.36624404335156298e-05 * SOLAR_MASS + ); +} + +function Neptune() { + return new Body( + 1.53796971148509165e+01, + -2.59193146099879641e+01, + 1.79258772950371181e-01, + 2.68067772490389322e-03 * DAYS_PER_YEAR, + 1.62824170038242295e-03 * DAYS_PER_YEAR, + -9.51592254519715870e-05 * DAYS_PER_YEAR, + 5.15138902046611451e-05 * SOLAR_MASS + ); +} + +function Sun() { + return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS); +} + +var bodies = Array(Sun(), Jupiter(), Saturn(), Uranus(), Neptune()); + +function offsetMomentum() { + let px = 0; + let py = 0; + let pz = 0; + var size = bodies.length; + for (let i = 0; i < size; i++) { + var body = bodies[i]; + var mass = body.mass; + px += body.vx * mass; + py += body.vy * mass; + pz += body.vz * mass; + } + + var body = bodies[0]; + body.vx = -px / SOLAR_MASS; + body.vy = -py / SOLAR_MASS; + body.vz = -pz / SOLAR_MASS; +} + +function advance(dt) { + var size = bodies.length; + + for (let i = 0; i < size; i++) { + var bodyi = bodies[i]; + let vxi = bodyi.vx; + let vyi = bodyi.vy; + let vzi = bodyi.vz; + for (let j = i + 1; j < size; j++) { + var bodyj = bodies[j]; + var dx = bodyi.x - bodyj.x; + var dy = bodyi.y - bodyj.y; + var dz = bodyi.z - bodyj.z; + + var d2 = dx * dx + dy * dy + dz * dz; + var mag = dt / (d2 * Math.sqrt(d2)); + + var massj = bodyj.mass; + vxi -= dx * massj * mag; + vyi -= dy * massj * mag; + vzi -= dz * massj * mag; + + var massi = bodyi.mass; + bodyj.vx += dx * massi * mag; + bodyj.vy += dy * massi * mag; + bodyj.vz += dz * massi * mag; + } + bodyi.vx = vxi; + bodyi.vy = vyi; + bodyi.vz = vzi; + } + + for (let i = 0; i < size; i++) { + var body = bodies[i]; + body.x += dt * body.vx; + body.y += dt * body.vy; + body.z += dt * body.vz; + } +} + +function energy() { + let e = 0; + var size = bodies.length; + + for (let i = 0; i < size; i++) { + var bodyi = bodies[i]; + + e += 0.5 * bodyi.mass * ( bodyi.vx * bodyi.vx + + bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz ); + + for (let j = i + 1; j < size; j++) { + var bodyj = bodies[j]; + var dx = bodyi.x - bodyj.x; + var dy = bodyi.y - bodyj.y; + var dz = bodyi.z - bodyj.z; + + var distance = Math.sqrt(dx * dx + dy * dy + dz * dz); + e -= (bodyi.mass * bodyj.mass) / distance; + } + } + return e; +} + +var n = arg[0] || 1000000 + +offsetMomentum(); + +log.console(`n = ${n}`) +log.console(energy().toFixed(9)) +for (let i = 0; i < n; i++) + advance(0.01); +log.console(energy().toFixed(9)) + +$_.stop() \ No newline at end of file diff --git a/benchmarks/spectral-norm.ce b/benchmarks/spectral-norm.ce new file mode 100644 index 00000000..6ce3af4a --- /dev/null +++ b/benchmarks/spectral-norm.ce @@ -0,0 +1,50 @@ +function A(i,j) { + return 1/((i+j)*(i+j+1)/2+i+1); +} + +function Au(u,v) { + for (var i=0; i // // Ensure wota, nota, json, and os are all available, e.g.: var wota = use('wota'); @@ -12,34 +12,43 @@ var os = use('os'); // +// Parse command line arguments +if (arg.length !== 2) { + log.console('Usage: cell benchmark_wota_nota_json.ce '); + $_.stop() +} + +var lib_name = arg[0]; +var scenario_name = arg[1]; + //////////////////////////////////////////////////////////////////////////////// -// 1. Setup "libraries" array to easily switch among Wota, Nota, and JSON +// 1. Setup "libraries" array to easily switch among wota, nota, and json //////////////////////////////////////////////////////////////////////////////// const libraries = [ { - name: "Wota", + name: "wota", encode: wota.encode, decode: wota.decode, - // Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size. + // wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size. getSize(encoded) { return encoded.length; } }, { - name: "Nota", + name: "nota", encode: nota.encode, decode: nota.decode, - // Nota also produces an ArrayBuffer: + // nota also produces an ArrayBuffer: getSize(encoded) { return encoded.length; } }, { - name: "JSON", + name: "json", encode: json.encode, decode: json.decode, - // JSON produces a JS string. We'll measure its UTF-16 code unit length + // json produces a JS string. We'll measure its UTF-16 code unit length // as a rough "size". Alternatively, you could convert to UTF-8 for // a more accurate byte size. Here we just use `string.length`. getSize(encodedStr) { @@ -55,27 +64,27 @@ const libraries = [ const benchmarks = [ { - name: "Empty object", + name: "empty", data: [{}, {}, {}, {}], iterations: 10000 }, { - name: "Small Integers", + name: "integers", data: [0, 42, -1, 2023], iterations: 100000 }, { - name: "Floating point", + name: "floats", data: [0.1, 1e-50, 3.14159265359], iterations: 100000 }, { - name: "Strings (short, emoji)", - data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"], + name: "strings", + data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"], iterations: 100000 }, { - name: "Small Objects", + name: "objects", data: [ { a:1, b:2.2, c:"3", d:false }, { x:42, y:null, z:"test" } @@ -83,12 +92,12 @@ const benchmarks = [ iterations: 50000 }, { - name: "Nested Arrays", + name: "nested", data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ], iterations: 50000 }, { - name: "Large Array (1k integers)", + name: "large_array", data: [ Array.from({length:1000}, (_, i) => i) ], iterations: 1000 }, @@ -109,7 +118,7 @@ function measureTime(fn) { // 4. For each library, we run each benchmark scenario and measure: // - Encoding time (seconds) // - Decoding time (seconds) -// - Total encoded size (bytes or code units for JSON) +// - Total encoded size (bytes or code units for json) // //////////////////////////////////////////////////////////////////////////////// @@ -153,33 +162,43 @@ function runBenchmarkForLibrary(lib, bench) { } //////////////////////////////////////////////////////////////////////////////// -// 5. Main driver: run across all benchmarks, for each library. +// 5. Main driver: run only the specified library and scenario //////////////////////////////////////////////////////////////////////////////// -log.console("Benchmark: Wota vs Nota vs JSON"); -log.console("================================\n"); +// Find the requested library and scenario +var lib = libraries.find(l => l.name === lib_name); +var bench = benchmarks.find(b => b.name === scenario_name); -for (let bench of benchmarks) { - log.console(`SCENARIO: ${bench.name}`); - log.console(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`); - - for (let lib of libraries) { - let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench); - - // We'll compute total operations = bench.iterations * bench.data.length - let totalOps = bench.iterations * bench.data.length; - let encOpsPerSec = (totalOps / encodeTime).toFixed(1); - let decOpsPerSec = (totalOps / decodeTime).toFixed(1); - - log.console(` ${lib.name}:`); - log.console(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec [${(encodeTime/bench.iterations)*1000000000} ns/try]`); - log.console(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec [${(decodeTime/bench.iterations)*1000000000}/try]`); - log.console(` Total size: ${totalSize} bytes (or code units for JSON)`); - log.console(""); - } - log.console("---------------------------------------------------------\n"); +if (!lib) { + log.console('Unknown library:', lib_name); + log.console('Available libraries:', libraries.map(l => l.name).join(', ')); + $_.stop() } -log.console("Benchmark complete.\n"); +if (!bench) { + log.console('Unknown scenario:', scenario_name); + log.console('Available scenarios:', benchmarks.map(b => b.name).join(', ')); + $_.stop() +} -os.exit() +// Run the benchmark for this library/scenario combination +var { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench); + +// Output json for easy parsing by hyperfine or other tools +var totalOps = bench.iterations * bench.data.length; +var result = { + lib: lib_name, + scenario: scenario_name, + encodeTime: encodeTime, + decodeTime: decodeTime, + totalSize: totalSize, + totalOps: totalOps, + encodeOpsPerSec: totalOps / encodeTime, + decodeOpsPerSec: totalOps / decodeTime, + encodeNsPerOp: (encodeTime / totalOps) * 1e9, + decodeNsPerOp: (decodeTime / totalOps) * 1e9 +}; + +log.console(json.encode(result)); + +$_.stop() diff --git a/cell.md b/cell.md index d90be570..3b7b9715 100644 --- a/cell.md +++ b/cell.md @@ -50,7 +50,7 @@ do loop { rename instanceof to 'is' -remove undefined; all is 'null' now +remove undefined; all are 'null' now remove 'delete'; to remove a field, assign it to null