200 lines
5.5 KiB
Plaintext
200 lines
5.5 KiB
Plaintext
//
|
|
// benchmark_wota_nota_json.js
|
|
//
|
|
// Usage in QuickJS:
|
|
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
|
|
//
|
|
// Ensure wota, nota, json, and os are all available, e.g.:
|
|
var wota = use('wota');
|
|
var nota = use('nota');
|
|
var json = use('json');
|
|
var jswota = use('jswota')
|
|
var os = use('os');
|
|
//
|
|
|
|
// Parse command line arguments
|
|
if (length(arg) != 2) {
|
|
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
|
|
$stop()
|
|
}
|
|
|
|
var lib_name = arg[0];
|
|
var scenario_name = arg[1];
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 1. Setup "libraries" array to easily switch among wota, nota, and json
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def libraries = [
|
|
{
|
|
name: "wota",
|
|
encode: wota.encode,
|
|
decode: wota.decode,
|
|
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
|
getSize(encoded) {
|
|
return length(encoded);
|
|
}
|
|
},
|
|
{
|
|
name: "nota",
|
|
encode: nota.encode,
|
|
decode: nota.decode,
|
|
// nota also produces an ArrayBuffer:
|
|
getSize(encoded) {
|
|
return length(encoded);
|
|
}
|
|
},
|
|
{
|
|
name: "json",
|
|
encode: json.encode,
|
|
decode: json.decode,
|
|
// 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
|
|
getSize(encodedStr) {
|
|
return length(encodedStr);
|
|
}
|
|
}
|
|
];
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 2. Test data sets (similar to wota benchmarks).
|
|
// Each scenario has { name, data, iterations }
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def benchmarks = [
|
|
{
|
|
name: "empty",
|
|
data: [{}, {}, {}, {}],
|
|
iterations: 10000
|
|
},
|
|
{
|
|
name: "integers",
|
|
data: [0, 42, -1, 2023],
|
|
iterations: 100000
|
|
},
|
|
{
|
|
name: "floats",
|
|
data: [0.1, 1e-50, 3.14159265359],
|
|
iterations: 100000
|
|
},
|
|
{
|
|
name: "strings",
|
|
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
|
iterations: 100000
|
|
},
|
|
{
|
|
name: "objects",
|
|
data: [
|
|
{ a:1, b:2.2, c:"3", d:false },
|
|
{ x:42, y:null, z:"test" }
|
|
],
|
|
iterations: 50000
|
|
},
|
|
{
|
|
name: "nested",
|
|
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
|
iterations: 50000
|
|
},
|
|
{
|
|
name: "large_array",
|
|
data: [ array(1000, i => i) ],
|
|
iterations: 1000
|
|
},
|
|
];
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 3. Utility: measureTime(fn) => how long fn() takes in seconds.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function measureTime(fn) {
|
|
var start = os.now();
|
|
fn();
|
|
var end = os.now();
|
|
return (end - start); // in seconds
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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)
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function runBenchmarkForLibrary(lib, bench) {
|
|
// We'll encode and decode each item in `bench.data`.
|
|
// We do 'bench.iterations' times. Then sum up total time.
|
|
|
|
// Pre-store the encoded results for all items so we can measure decode time
|
|
// in a separate pass. Also measure total size once.
|
|
var encodedList = [];
|
|
var totalSize = 0;
|
|
|
|
// 1) Measure ENCODING
|
|
var encodeTime = measureTime(() => {
|
|
for (var i = 0; i < bench.iterations; i++) {
|
|
// For each data item, encode it
|
|
for (var j = 0; j < length(bench.data); j++) {
|
|
var e = lib.encode(bench.data[j]);
|
|
// store only in the very first iteration, so we can decode them later
|
|
// but do not store them every iteration or we blow up memory.
|
|
if (i == 0) {
|
|
push(encodedList, e);
|
|
totalSize += lib.getSize(e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// 2) Measure DECODING
|
|
var decodeTime = measureTime(() => {
|
|
for (var i = 0; i < bench.iterations; i++) {
|
|
arrfor(encodedList, lib.decode)
|
|
}
|
|
});
|
|
|
|
return { encodeTime, decodeTime, totalSize };
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 5. Main driver: run only the specified library and scenario
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Find the requested library and scenario
|
|
var lib = libraries[find(libraries, l => l.name == lib_name)];
|
|
var bench = benchmarks[find(benchmarks, b => b.name == scenario_name)];
|
|
|
|
if (!lib) {
|
|
log.console('Unknown library:', lib_name);
|
|
log.console('Available libraries:', text(array(libraries, l => l.name), ', '));
|
|
$stop()
|
|
}
|
|
|
|
if (!bench) {
|
|
log.console('Unknown scenario:', scenario_name);
|
|
log.console('Available scenarios:', text(array(benchmarks, b => b.name), ', '));
|
|
$stop()
|
|
}
|
|
|
|
// 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 * length(bench.data);
|
|
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(result);
|
|
|
|
$stop()
|