Skip to content

Commit

Permalink
1.0.10
Browse files Browse the repository at this point in the history
- publish latest c++ header
- guide for using mitata with engine testing runtimes directly
- fix viz breaking when high-res time is not available
- further expand engine coverage, fix for graaljs
  • Loading branch information
evanwashere committed Sep 28, 2024
1 parent cac455e commit e5a8c26
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 47 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "mitata",
"type": "module",
"license": "MIT",
"version": "1.0.9",
"version": "1.0.10",
"main": "src/main.mjs",
"types": "src/main.d.mts",
"files": ["src", "license", "readme.md"],
Expand Down
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,27 @@ On runtimes that expose gc (e.g. bun, `node --expose-gc ...`), mitata will autom

Out of box mitata can detect engine/runtime it's running on and fall back to using [alternative](https://github.com/evanwashere/mitata/blob/master/src/lib.mjs#L30) non-standard I/O functions. If your engine or runtime is missing support, open an issue or pr requesting for support.

### how to use mitata with engine CLIs like d8, jsc, graaljs, spidermonkey

```bash
$ xs bench.mjs
$ quickjs bench.mjs
$ d8 --expose-gc bench.mjs
$ spidermonkey -m bench.mjs
$ graaljs --js.timer-resolution=1 bench.mjs
$ /System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc bench.mjs
```

```js
// bench.mjs

import { print } from './src/lib.mjs';
import { run, bench } from './src/main.mjs'; // git clone
import { run, bench } from './node_modules/mitata/src/main.mjs'; // npm install

print('hello world'); // works on every engine
```

## argumentizing your benchmarks has never been so easy

With other benchmarking libraries, often it's quite hard to easily make benchmarks that go over a range or run the same function with different arguments without writing spaghetti code, but now with mitata converting your benchmark to use arguments is just a function call away.
Expand Down
4 changes: 2 additions & 2 deletions src/lib.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export const print = (() => {
export const gc = (() => {
try { return (Bun.gc(true), () => Bun.gc(true)); } catch { }
try { return (globalThis.gc(), () => globalThis.gc()); } catch { }
try { return (globalThis.std.gc(), () => globalThis.std.gc()); } catch { }
try { return (globalThis.$262.gc(), () => globalThis.$262.gc()); } catch { }

return () => new Uint8Array(2 ** 30);
if (globalThis.Graal) return () => new Uint8Array(2 ** 29); return () => new Uint8Array(2 ** 30);
})();

export const now = (() => {
Expand Down
45 changes: 21 additions & 24 deletions src/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,25 @@ function* unroll() {

function version() {
return ({
v8: () => globalThis.version?.(),
bun: () => globalThis.Bun?.version,
deno: () => globalThis.Deno?.version?.deno,
node: () => globalThis.process?.versions?.node,
graaljs: () => globalThis.Graal?.versionGraalVM,
'quickjs-ng': () => globalThis.navigator?.userAgent?.split?.('/')[1],
hermes: () => globalThis.HermesInternal?.getRuntimeProperties?.()?.['OSS Release Version'],
})[runtime()]?.() || null;
}

function runtime() {
if (globalThis.d8) return 'v8';
if (globalThis.Graal) return 'graaljs';
if (globalThis.inIon && globalThis.performance?.mozMemory) return 'spidermonkey';
if (globalThis.navigator?.userAgent?.toLowerCase?.()?.includes?.('quickjs-ng')) return 'quickjs-ng';
if (globalThis.$262 && globalThis.lockdown && globalThis.AsyncDisposableStack) return 'XS Moddable';
if (globalThis.$ && 'IsHTMLDDA' in globalThis.$ && (new Error().stack).startsWith('runtime@')) return 'jsc';

if (globalThis.os && globalThis.std) return 'quickjs';
if (globalThis.Bun) return 'bun'; if (globalThis.Deno) return 'deno'; if (globalThis.HermesInternal) return 'hermes';
if (globalThis.window && globalThis.navigator) return 'browser'; if (globalThis.process) return 'node'; else return null;
}
Expand Down Expand Up @@ -387,21 +393,21 @@ const formats = {
const poffset = (percentile * (stats.samples.length - 1)) | 0;

const min = stats.min;
const max = stats.samples[poffset];
const max = stats.samples[poffset] || stats.max || 1;

const bins = new Array(size).fill(0);
const steps = new Array(size).fill(0);
const step = (max - min) / (size - 1);

for (let o = 0; o < poffset; o++) {
const s = stats.samples[o];
bins[clamp(0, Math.round((s - min) / step), size - 1)]++;
}
for (let o = 0; o < size; o++) steps[o] = min + o * step;
for (let o = 0; o < poffset; o++) bins[Math.round((stats.samples[o] - min) / step)]++;

return {
min, max, step, bins,
min, max,
step, bins, steps,
peak: Math.max(...bins),
outliers: stats.samples.length - 1 - poffset,
avg: clamp(0, Math.round((stats.avg - min) / step), size - 1),
steps: Array.from({ length: size }, (_, o) => min + o * step),
};
},

Expand Down Expand Up @@ -580,34 +586,25 @@ const formats = {
for (const name in map) {
const stats = map[name];
if (tmin > stats.min) tmin = stats.min;
if (tmax < stats.p99) tmax = stats.p99;
if (tmax < stats.p99) tmax = stats.p99 || stats.max || 1;
}

const step = (tmax - tmin) / steps;
const step = (tmax - tmin) / (steps - 1);

for (const name in map) {
const stats = map[name];

const min = stats.min;
const avg = stats.avg;
const max = stats.p99;
const p25 = stats.p25;
const p75 = stats.p75;
const max = stats.p99 || stats.max || 1;

let min_offset = 0;
let max_offset = 0;
let avg_offset = 0;
let p25_offset = 0;
let p75_offset = 0;

for (let o = 0; o < steps; o++) {
const t = tmin + o * step;
if (t <= min) min_offset = o;
if (t <= max) max_offset = o;
if (t <= avg) avg_offset = o;
if (t <= p25) p25_offset = o;
if (t <= p75) p75_offset = o;
}
const min_offset = Math.round((min - tmin) / step);
const max_offset = Math.round((max - tmin) / step);
const avg_offset = Math.round((avg - tmin) / step);
const p25_offset = Math.round((p25 - tmin) / step);
const p75_offset = Math.round((p75 - tmin) / step);

const u = new Array(1 + max_offset).fill(' ');
const m = new Array(1 + max_offset).fill(' ');
Expand Down
31 changes: 18 additions & 13 deletions src/mitata.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ namespace mitata {
auto clamp = [](auto m, auto v, auto x) { return v < m ? m : v > x ? x : v; };

f64 min = stats.min;
f64 max = stats.samples[poffset];
f64 max = .0 != stats.samples[poffset] ? stats.samples[poffset] : (.0 == stats.max ? 1.0 : stats.max);

f64 step = (max - min) / (size - 1);
auto bins = std::vector<u64>(size, 0);
auto steps = std::vector<f64>(size, 0);
Expand Down Expand Up @@ -297,7 +298,9 @@ namespace mitata {
inline const std::string boxplot(std::map<std::string, lib::k_stats> map, u64 legend = 8, u64 width = 14, bool colors = true) {
std::string boxplot = "";
f64 tmin = std::min_element(map.begin(), map.end(), [](const auto &a, const auto &b) { return a.second.min < b.second.min; })->second.min;
f64 tmax = std::max_element(map.begin(), map.end(), [](const auto &a, const auto &b) { return a.second.p99 < b.second.p99; })->second.p99;
auto tmax_stats = std::max_element(map.begin(), map.end(), [](const auto &a, const auto &b) { return (.0 != a.second.p99 ? a.second.p99 : (.0 == a.second.max ? 1.0 : a.second.max)) < (.0 != b.second.p99 ? b.second.p99 : (.0 == b.second.max ? 1.0 : b.second.max)); })->second;

f64 tmax = .0 != tmax_stats.p99 ? tmax_stats.p99 : (.0 == tmax_stats.max ? 1.0 : tmax_stats.max);

auto steps = 2 + width;
auto step = (tmax - tmin) / (steps - 1);
Expand All @@ -306,17 +309,17 @@ namespace mitata {
boxplot += "" + std::string(width, ' ') + "" + "\n";

for (const auto &[name, stats] : map) {
auto min = stats.min;
auto max = stats.p99;
auto avg = stats.avg;
auto p25 = stats.p25;
auto p75 = stats.p75;

auto min_offset = std::round((min - tmin) / step);
auto max_offset = std::round((max - tmin) / step);
auto avg_offset = std::round((avg - tmin) / step);
auto p25_offset = std::round((p25 - tmin) / step);
auto p75_offset = std::round((p75 - tmin) / step);
f64 min = stats.min;
f64 avg = stats.avg;
f64 p25 = stats.p25;
f64 p75 = stats.p75;
f64 max = .0 != stats.p99 ? stats.p99 : (.0 == stats.max ? 1.0 : stats.max);

u64 min_offset = std::round((min - tmin) / step);
u64 max_offset = std::round((max - tmin) / step);
u64 avg_offset = std::round((avg - tmin) / step);
u64 p25_offset = std::round((p25 - tmin) / step);
u64 p75_offset = std::round((p75 - tmin) / step);

auto u = std::vector<std::string>(1 + max_offset, " ");
auto m = std::vector<std::string>(1 + max_offset, " ");
Expand Down Expand Up @@ -472,6 +475,8 @@ namespace mitata {
if ("json" == opts.format) {
std::cout << "{" << std::endl;
std::cout << "\"context\": {" << std::endl;
std::cout << "\"runtime\": \"c++\"," << std::endl;
std::cout << "\"compiler\": \"" << ctx::compiler() << "\"," << std::endl;

std::cout << "\"noop\": {" << std::endl;
std::cout << "\"min\": " << noop.min << "," << std::endl;
Expand Down
20 changes: 13 additions & 7 deletions tools/tune.mjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { print } from '../src/lib.mjs';
import { run, bench, measure } from '../src/main.mjs';
import { now, print } from '../src/lib.mjs';
import { run, bench, boxplot, barplot, lineplot, measure } from '../src/main.mjs';

bench('noop', () => { });
boxplot(() => {
// barplot(() => {
// lineplot(() => {
bench('noop', () => { });

bench('test', function* (state) {
const size = state.get(0);
yield () => size;
}).args([0]);
bench('test', function* (state) {
const size = state.get(0);
yield () => new Array(size);
}).args([0]);
});

// cpu info
await run();

// while (true) print(`${now() / 1e6}`);

while (true) {
const s = await measure(() => { });

Expand Down

0 comments on commit e5a8c26

Please sign in to comment.