Skip to content

Commit

Permalink
New: adds time prettifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
pustovitDmytro committed Nov 22, 2023
1 parent 341b3b2 commit f03429c
Show file tree
Hide file tree
Showing 17 changed files with 259 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .nycrc.json
Expand Up @@ -14,7 +14,7 @@
"report-dir": "./reports/coverage",
"temp-dir": "./reports/coverage",
"lines": 99,
"statements": 97,
"statements": 98,
"functions": 98,
"branches": 93,
"check-coverage": true
Expand Down
46 changes: 44 additions & 2 deletions README.md
Expand Up @@ -38,7 +38,7 @@ We fight for democratic values, freedom, for our future! Once again Ukrainians h
- [Minimal Configuration](#minimal-configuration)
- [Sequences](#sequences)
- [Timers](#timers)
- [Reporters](#reporters)
- [Reports](#reports)
- [Customization](#customization)
- [Custom mertics](#custom-mertics)
- [Memory](#memory)
Expand Down Expand Up @@ -141,7 +141,9 @@ const bench = new BenchMark({

to implement own timer it is recomended to extends `Timer` and follow it's interfece.

### Reporters
### Reports

There are two reports available:
* `JSONReporter` - report in json format, usefull for external export.
* `PlainReporter` - used by default.

Expand All @@ -156,6 +158,45 @@ const report = JSON.parse(bench.report(new JSONReporter()));

```

to pretty print numbers in report use:
```javascript
import BenchMark, { PlainReporter } from 'vesta';

const bench = new BenchMark();

bench.report(new PlainReporter(), { pretty: true });
```

or pass `pretty` as configuration object:

```javascript
import BenchMark, { JSONReporter } from 'vesta';

const bench = new BenchMark({});

bench.sequence('before');
await pause(2500);
bench.sequence('after');

console.log(
bench.report(new JSONReporter(), {
pretty : {
exclude : [ 'total' ],
include : [ 'mean', 'benchmark' ]
}
})
);

// [
// { label: 'before', benchmark: 0 },
// { label: 'after', benchmark: '2s 504ms' }
// ];

```
where
* `include` - array with metric names, for which prettifier will be added.
* `exclude` - array with metric names, for which prettifier won't be added.

### Customization

pass next options to BenchMark properties:
Expand Down Expand Up @@ -200,6 +241,7 @@ bench.calculate({
These additions allow you to tailor the benchmarks and generate more comprehensive reports tailored to your specific needs.



### Memory

Use the same API to benchmark memory usage:
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -88,6 +88,6 @@
"uuid": "^9.0.0"
},
"dependencies": {
"myrmidon": "1.8.0"
"myrmidon": "^1.9.0"
}
}
65 changes: 45 additions & 20 deletions src/BaseBenchMark.js
@@ -1,5 +1,5 @@
/* eslint-disable no-param-reassign */
import { mean, isValue, isObject } from 'myrmidon';
import { mean, isValue, isObject, InclusiveFilter } from 'myrmidon';
import PlainReporter from './reporters/PlainReporter';

export default class BaseBenchMark {
Expand Down Expand Up @@ -104,26 +104,15 @@ export default class BaseBenchMark {
return report;
}


const mergedMetrics = {
...this.constructor.metrics,
...metrics
};

if (multiObservation) {
this._prepareMultiObservationReport(bench, mergedMetrics, report);
this._prepareMultiObservationReport(bench, metrics, report);
} else {
this._prepareSingleObservationReport(bench, mergedMetrics, report);
this._prepareSingleObservationReport(bench, metrics, report);
}

const mergedItems = {
...this.constructor.items,
...items
};

for (const itemLabel of Object.keys(mergedItems)) {
if (!mergedItems[itemLabel]) continue;
report[itemLabel] = mergedItems[itemLabel](
for (const itemLabel of Object.keys(items)) {
if (!items[itemLabel]) continue;
report[itemLabel] = items[itemLabel](
filtered,
report
);
Expand Down Expand Up @@ -152,19 +141,55 @@ export default class BaseBenchMark {
this.calculateIntervals();
this._iterations.forEach(b => b.calculateIntervals());


const mergedMetrics = { ...this.constructor.metrics, ...metrics };
const mergedItems = { ...this.constructor.items, ...items };
const metricsList = Object.keys(mergedMetrics);
const itemsList = Object.keys(mergedItems);

for (const label of labelsList) {
const report = this.prepareReport(label, labels, { metrics, items });
const report = this.prepareReport(label, labels, { metrics: mergedMetrics, items: mergedItems });

if (!report) continue;
report._meta = { metricsList, itemsList };
this._reports.push(report);
}
}

report(reporter = new PlainReporter(), { pretty = false } = {}) {
report(reporter = new PlainReporter(), {
pretty = null
} = {}) {
this.calculate();

return reporter.run(this._reports, {
prettify : pretty && this.counter.constructor.prettify
prettify : pretty && this.counter.constructor.prettify && this.prettify.bind(
this,
this.counter.constructor.prettify,
isObject(pretty) ? pretty : {}
)
});
}

prettify(prettifier, config, obj, meta) {
const res = {};
const {
exclude,
include = [ ...meta.metricsList, 'benchmark' ]
} = config;

const filter = new InclusiveFilter({ include, exclude });

for (const key of Object.keys(obj)) {
if (isObject(obj[key])) {
res[key] = this.prettify(prettifier, config, obj[key], meta);
continue;
}

res[key] = filter.run(key)
? prettifier(obj[key])
: obj[key];
}

return res;
}
}
4 changes: 2 additions & 2 deletions src/counters/ProcessMemory.js
@@ -1,3 +1,4 @@
import { formatBytes } from '../utils/formatters';
import Counter from './Counter';

function safeGetKey(report, key) {
Expand All @@ -10,8 +11,7 @@ export default class ProcessMemoryCountrer extends Counter {
}

static prettify(data) {
// eslint-disable-next-line no-magic-numbers
return `${Math.round(data / 1024 / 1024 * 100) / 100}MB`;
return formatBytes(data);
}

diff(start, end) {
Expand Down
5 changes: 5 additions & 0 deletions src/counters/Timer.js
@@ -1,10 +1,15 @@
import { formatTime } from '../utils/formatters';
import Counter from './Counter';

export default class Timer extends Counter {
bench() {
return new Date();
}

static prettify(data) {
return formatTime(data);
}

diff(start, end) {
return end - start;
}
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Expand Up @@ -6,6 +6,8 @@ import BaseReporter from './reporters/Reporter';
import BaseTimer from './counters/Timer';
import PerformanceNow from './counters/PerformanceNow';
import ProcessHrtime from './counters/ProcessHrtime';
import ProcessMemory from './counters/ProcessMemory';

import Memory from './Memory';

export default BenchMark;
Expand All @@ -18,5 +20,6 @@ export {
PlainReporter,
JSONReporter,
BaseReporter,
ProcessMemory,
Memory
};
9 changes: 8 additions & 1 deletion src/reporters/PlainReporter.js
@@ -1,3 +1,5 @@
import { inspect } from 'util';
import { isObject } from 'myrmidon';
import Reporter from './Reporter';

function capitilize(str) {
Expand All @@ -9,7 +11,12 @@ export default class PlainReporter extends Reporter {
return [
'------------------------',
...Object.keys(report)
.map(key => `${capitilize(key)}: ${report[key]}`)
.map(key => {
const title = capitilize(key);
const payload = isObject(report[key]) ? inspect(report[key]) : report[key];

return `${title}: ${payload}`;
})
].join('\n');
}

Expand Down
16 changes: 2 additions & 14 deletions src/reporters/Reporter.js
Expand Up @@ -9,10 +9,8 @@ export default class Reporter {

run(reports, { prettify }) {
return this.merge(
reports.map(({ label, ...r }) => {
const metrics = prettify
? this.prettify(prettify, r)
: r;
reports.map(({ label, _meta, ...r }) => {
const metrics = prettify ? prettify(r, _meta) : r;

return this.render({
label,
Expand All @@ -21,14 +19,4 @@ export default class Reporter {
})
);
}

prettify(prettifier, obj) {
const res = {};

for (const key of Object.keys(obj)) {
res[key] = prettifier(obj[key]);
}

return res;
}
}
46 changes: 46 additions & 0 deletions src/utils/formatters.js
@@ -0,0 +1,46 @@
/* eslint-disable no-magic-numbers */

const timeUnits = [
{ label: 'ms', frack: 1000 },
{ label: 's', frack: 60 },
{ label: 'min', frack: 60 },
{ label: 'h', frack: 24 },
{ label: 'd', frack: 24 }
];

export function formatTime(duration, depth = 2) {
if (!+duration) return 0;
let s = duration;
const values = [];

for (const unit of timeUnits) {
const value = s % unit.frack;

s = (s - value) / unit.frack;
values.push({ value, unit: unit.label });
}

const result = [];

let left = depth;

for (const { value, unit } of values.reverse()) {
if (value > 0) result.push(`${value}${unit}`);
if (result.length > 0 && !left--) break;
}

return result.join(' ');
}

const k = 1024;
const sizes = [ 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ];

export function formatBytes(bytes, decimals = 2) {
if (!+bytes) return 0;

const dm = decimals < 0 ? 0 : decimals;

const i = Math.floor(Math.log(bytes) / Math.log(k));

return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
}

0 comments on commit f03429c

Please sign in to comment.