Skip to content

Commit

Permalink
chore(base64): Use performance.now() for benchmarking where available…
Browse files Browse the repository at this point in the history
… (e.g. Node.js)
  • Loading branch information
gibson042 committed Jan 22, 2024
1 parent aa04be5 commit c695bf8
Showing 1 changed file with 72 additions and 54 deletions.
126 changes: 72 additions & 54 deletions packages/base64/test/bench-main.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,94 @@
/* eslint-disable no-restricted-globals */
/* global print */

// This is a quick and dirty benchmark to encode and decode base64, intended to
// be run manually to measure performance progress or regressions.
// This is a hand-rolled benchmark that attempts as much as possible to avoid
// incorporating the noise of function calls and the underlying Date.now()
// syscall, by exponentially probing for the number of operations that can be
// executed within each benchmark's deadline.
// incorporating the noise of function calls and the underlying
// performance.now()/Date.now() syscall, by exponentially probing for the number
// of operations that can be executed within each benchmark's deadline.

import { encodeBase64, decodeBase64 } from '../index.js';
import { jsEncodeBase64 } from '../src/encode.js';
import { jsDecodeBase64 } from '../src/decode.js';

// eslint-disable-next-line no-undef,no-restricted-globals
const log = (typeof console !== 'undefined' && console.log) || print;
async function main() {
const log = (typeof console !== 'undefined' && console.log) || print;
/** @type {typeof Date.now} */
const now = await (async () => {
try {
const { performance } = await import('perf_hooks');
if (performance.now) {
return performance.now.bind(performance);
}
} catch (_err) {
// eslint-disable-next-line no-empty
}
return Date.now;
})();

const timeout = 1000; // ms
const timeout = 1000; // ms

const shortString =
'there once a rich man from nottingham who tried to cross the river. what a dope, he tripped on a rope. now look at him shiver.';
const string = new Array(10000).fill(shortString).join(' ');
const shortData = encodeBase64(shortString);
const data = encodeBase64(string);
const shortString =
'there once a rich man from nottingham who tried to cross the river. what a dope, he tripped on a rope. now look at him shiver.';
const string = new Array(10000).fill(shortString).join(' ');
const shortData = encodeBase64(shortString);
const data = encodeBase64(string);

/** @type {string} */
let result;
/** @type {string} */
let result;

for (const [label, fn, input] of [
['encodes', encodeBase64, string],
['JS short-string encodes', jsEncodeBase64, shortString],
]) {
for (const pass of [1, 2]) {
const start = Date.now();
const deadline = start + timeout / 2;
let operations = 0;
for (let n = 1; Date.now() < deadline; n *= 2) {
for (let i = 0; i < n; i += 1) {
result = fn(input);
for (const [label, fn, input] of [
['encodes', encodeBase64, string],
['JS short-string encodes', jsEncodeBase64, shortString],
]) {
for (const pass of [1, 2]) {
const start = now();
const deadline = start + timeout / 2;
let operations = 0;
for (let n = 1; now() < deadline; n *= 2) {
for (let i = 0; i < n; i += 1) {
result = fn(input);
}
operations += n;
}
operations += n;
const end = now();
const duration = end - start;
log(
`[pass ${pass}] ${label}`,
(operations * input.length) / duration,
'characters per millisecond',
);
}
const end = Date.now();
const duration = end - start;
log(
`[pass ${pass}] ${label}`,
(operations * input.length) / duration,
'characters per millisecond',
);
}
}

if (result.length < 100) throw Error(`unexpected result: ${result}`);
if (result.length < 100) throw Error(`unexpected result: ${result}`);

for (const [label, fn, input] of [
['decodes', decodeBase64, data],
['JS short-string decodes', jsDecodeBase64, shortData],
]) {
for (const pass of [1, 2]) {
const start = Date.now();
const deadline = start + timeout / 2;
let operations = 0;
for (let n = 1; Date.now() < deadline; n *= 2) {
for (let i = 0; i < n; i += 1) {
result = fn(input);
for (const [label, fn, input] of [
['decodes', decodeBase64, data],
['JS short-string decodes', jsDecodeBase64, shortData],
]) {
for (const pass of [1, 2]) {
const start = now();
const deadline = start + timeout / 2;
let operations = 0;
for (let n = 1; now() < deadline; n *= 2) {
for (let i = 0; i < n; i += 1) {
result = fn(input);
}
operations += n;
}
operations += n;
const end = now();
const duration = end - start;
log(
`[pass ${pass}] ${label}`,
(operations * input.length) / duration,
'bytes per millisecond',
);
}
const end = Date.now();
const duration = end - start;
log(
`[pass ${pass}] ${label}`,
(operations * input.length) / duration,
'bytes per millisecond',
);
}

if (result.length < 100) throw Error(`unexpected result: ${result}`);
}

if (result.length < 100) throw Error(`unexpected result: ${result}`);
main();

0 comments on commit c695bf8

Please sign in to comment.