Cross-runtime benchmarking lib and cli

Overview

mitata

cross-runtime benchmarking lib

Install

bun add mitata

npm install mitata

Examples

import { run, bench, group, baseline } from 'mitata';

// deno
// import { ... } from 'https://esm.sh/mitata';

bench('noop', () => {});
bench('noop2', () => {});

group('group', () => {
  baseline('baseline', () => {});
  bench('Date.now()', () => Date.now());
  bench('performance.now()', () => performance.now());
});

group({ name: 'group2', summary: false }, () => {
  bench('new Array(0)', () => new Array(0));
  bench('new Array(1024)', () => new Array(1024));
});

await run({
  avg: true, // enable/disable avg column (default: true)
  json: false, // enable/disable json output (default: false)
  colors: true, // enable/disable colors (default: true)
  min_max: true, // enable/disable min/max column (default: true)
  collect: false, // enable/disable collecting returned values into an array during the benchmark (default: false)
  percentiles: false, // enable/disable percentiles column (default: true)
});

output:

terminal screenshot

preview.png

cpu: unknown
runtime: bun 0.0.79 (arm64-darwin) 

benchmark              time (avg)             (min  max)
---------------------------------------------------------
noop               321.41 ps/iter    (304.1 ps  8.55 ns)
noop2              389.13 ps/iter   (304.1 ps  11.13 ns)

baseline           788.15 ps/iter    (304.1 ps  6.29 ns)
Date.now()          31.12 ns/iter    (30.94 ns  34.7 ns)
performance.now()   98.82 ns/iter   (98.03 ns  106.8 ns)

summary
  baseline
   39.49x faster than Date.now()
   125.38x faster than performance.now()

new Array(0)         7.67 ns/iter   (6.53 ns  181.62 ns)
new Array(1024)    295.78 ns/iter (240.04 ns  528.28 ns)

JIT bias

If you run benchmarks like this, you might notice that they get slower (only few nanoseconds) after the first few runs.

bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});
bench('noop', () => {});

I call this behavior "JIT bias". In simple words, v8 and JSC JIT expect us to pass the same function, so they optimize for it, but we break that promise and get deoptimized.

License

MIT © Evan

Comments
  • Bun crashes (Segmentation Fault) when running benchmarks against certain template engines

    Bun crashes (Segmentation Fault) when running benchmarks against certain template engines

    I want to create a set of performance comparisons of template engines running on Node or Bun, and decided to use Mitata as the benchmark framework because that's what the Bun project uses.

    I have found several template engines -- EJS, Nunjucks, MarkdownIT so far -- that cause Bun to segfault when running the benchmark using Mitata. Full details are in the Bun issue queue: https://github.com/oven-sh/bun/issues/811

    Reporting here because you may have an idea.

    Simplest example is:

    import { bench, run } from "mitata";
    
    import * as ejs from 'ejs';
    
    let people = ['geddy', 'neil', 'alex'];
    
    bench('literal', () => { return `${people.join(', ')}`; });
    
    bench('ejs-join', () => {
        ejs.render('<%= people.join(", "); %>', { people: people });
    });
    
    try {
        await run();
    } catch (err) { console.error(err); }
    

    If I run this code standalone (without Mitata) it executes perfectly on Bun. If I comment out the ejs-join stanza, it still segfaults, and if I comment out the import for ejs then Bun does not segfault.

    opened by robogeek 2
  • Compare two benchmark outputs

    Compare two benchmark outputs

    It would be useful if it could be set to automatically save the output of the benchmark (e.g. to .json) and then the two could be automatically compared to produce something like this: image

    I made simple script:

    import { summary } from 'mitata/reporter/table.mjs';
    import { join } from 'node:path';
    
    const __dirname = new URL('.', import.meta.url).pathname;
    
    const outputBunUtilities = JSON.parse(await (await Bun.file(join(__dirname, 'outputs', 'bun-utilities.json'))).text()).benchmarks.map(b => {
        return { ...b, name: `[bun-utilities] ${b.name}`, id: b.name };
    });
    const outputNodeChildProcess = JSON.parse(await (await Bun.file(join(__dirname, 'outputs', 'node-fs.json'))).text()).benchmarks.map(b => {
        return { ...b, name: `[node-fs] ${b.name}`, id: b.name };
    });
    
    const benchmarks = [].concat(outputBunUtilities, outputNodeChildProcess);
    const summaries = [
        'copydir empty',
        'copydir files',
        'rmdir empty',
        'rmdir files'
    ];
    
    for (const summaryName of summaries) {
        const filtered = benchmarks.filter(b => b.id === summaryName);
        console.log(summary(filtered));
    }
    

    image

    opened by xHyroM 1
  • Add meta information to package.json

    Add meta information to package.json

    tldr; If you add the information regarding the repo to the package.json,it would be propagated to npm.

    Currently at vitest it is discussed if we should tinybench/benchmark.js for automated benchmarking. It was recommended to consider mitata. I looked at npm, and there was no link to the repo. So I thought, that the code is not published anywhere. Then somebody posted the link to this repo, and then I could finally look into the code.

    This PR adds the necessary information, to find the repo coming from npm.

    https://github.com/vitest-dev/vitest/pull/1029

    opened by Uzlopak 0
  • [Feature request] logging the group name / adding a divider between groups

    [Feature request] logging the group name / adding a divider between groups

    Hey ! First of all I wanted to thank you for this package, the output looks very nice, it's super-intuitive and runs fast !

    This is how I use it: Screenshot 2022-06-03 at 09 59 09

    I thought that the group name could be in the output, and maybe adding a line of ----- between groups would add some clarity, what do you think ?

    opened by astahmer 0
  • Typo in summary output

    Typo in summary output

    Hi, thanks for this great crate!

    I noticed there is a small typo in the output when making the summary. It will print for example 2x times faster, but '2x' already reads as '2 times', so 2x times reads as '2 times times'. The right output would be either 2x faster or 2 times faster.

    See https://github.com/evanwashere/mitata/blob/master/src/lib.rs#L255

    opened by GJZwiers 0
  • feat: add prepare hook

    feat: add prepare hook

    This adds a prepare hook, and fixes #12 . I'm not sure about the name, alternatives I've considered were setup and hook.

    It allows people to run code between benchmarks. Think of use cases where the file system needs to be cleaned up, or to create a database index, or other expensive code that the test depends on, but is not part of the test itself.

    This change makes the following possible:

    import { baseline, bench, group, prepare, run } from 'mitata';
    
    group('select 10 records using different index strategies', () => {
      prepare('cleanup', async () => {
        await dropAllIndexes();
      })
      
      baseline('baseline, no index', async () => select(10));
    
      prepare('create index 1', async () => {
        await dropAllIndexes();
        await createIndexOne();
      })
      
      bench('query using index 1',  async() => select(10));
    
      prepare('create index 2', async () => { 
        await dropAllIndexes();
        await createIndexTwo();
      });
      
      bench('query using index 2',  async() => select(10));
    });
    
    await run();
    
    opened by smeijer 1
  • Feature: before / after bench hooks?

    Feature: before / after bench hooks?

    Sometimes when we bench some code, we need to prepare the environment. Think of a scenario like when we try to compare the performance of two different database indexes. The baseline can be a query without index, and two bench statements both with their own unique index. Pseudo:

    group('select 10 users', () => {
      baseline('no index', async () => {
        await client.query('select * from users order by created_at asc, id asc limit 10');
      });
    
      bench('with created_at index', async() => {
        await client.query(`select * from users order by created_at asc, id asc limit 10`);
      }, { 
        before: async () => dropIndexes().then(() => createFirstIndex()),
        after: async () => dropIndexes(),
      });
    
      bench('with created_at and id index', async() => {
        await client.query(`select * from users order by created_at asc, id asc limit 10`);
      }, { 
        before: async () => dropIndexes().then(() => createAnotherIndex()),
        after: async () => dropIndexes(),
      });
    });
    

    I think it would be useful to have it both on the group level, as well as on the bench level. The group could in this case for example be used to connect/disconnect to the database, or to support multiple query tests for a single created index, where another group would test another index.

    opened by smeijer 1
  • Feature: write outputs to file and convert to markdown for tracking purpose

    Feature: write outputs to file and convert to markdown for tracking purpose

    Thanks for a fantastic tool!

    Is there a way to write the output to a file and convert it to a table in markdown with the performance measurements and dates executed? That would be helpful for app developers who are keen to track performance over time and conveniently be able to share the results across the team.

    opened by jastraberg 0
  • Executing bench blocks when external objects asynchronously initialized

    Executing bench blocks when external objects asynchronously initialized

    I have a complex case to get working, and I think I understand what's going on. The test is below.

    The issue is akasha.filecache.documents. This object is initialized asynchronously, and the code here is how I've successfully used this object in many other files. The cacheSetup and fileCachesReady functions are where this object is set up, and calling isReady ensures that it is set up. THen calling console.log(documents) prints out the object.

    Hence - the object is set up before the bench blocks are executed. Unless run executes the bench blocks right away somehow.

    In the plugin.findBlogDoc function, akasha.filecache.documents is found to be undefined. But, as just said, the code which executes before the bench blocks has ensured that this object is set up.

    Is run executing in-line with the other code? That is, the condition of akasha.filecache.documents being undefined could happen if run were to magically execute right away, before fileCachesReady exits. But that should not be happening because Node.js handles top-level async/await correctly, I believe.

    import { bench, run } from "mitata";
    import { createRequire } from 'module';
    import * as akasha from 'akasharender';
    const require = createRequire(import.meta.url);
    const config = require('../config.js');
    // const akasha = config.akasha;
    
    await akasha.cacheSetup(config);
    await akasha.fileCachesReady(config);
    
    const documents = (await akasha.filecache).documents;
    await documents.isReady();
    
    const plugin = config.plugin("@akashacms/plugins-blog-podcast");
    
    const info1 = documents.find('blog/2017/11/test-post-2.html.md');
    
    bench('find-blog-vpath', () => {
        plugin.findBlogForVPInfo(info1);
    });
    
    bench('find-blog-docs', async () => {
        await plugin.findBlogDocs(config, plugin.blogcfg('news'), 'news');
    });
    
    // await filecache.close();
    await akasha.closeCaches();
    
    try {
        await run({
            percentiles: false
        });
    } catch (err) { console.error(err); }
    
    opened by robogeek 1
  • Clarify node versions support? And Node 14?

    Clarify node versions support? And Node 14?

    file:///home/runner/work/yocto-ttl-cache/yocto-ttl-cache/node_modules/.pnpm/[email protected]/node_modules/mitata/src/cli.mjs:178
      const colors = opts.colors ??= true;
                                 ^^^
    SyntaxError: Unexpected token '??='
        at Loader.moduleStrategy (internal/modules/esm/translators.js:145:1[8](https://github.com/un-ts/yocto-ttl-cache/runs/7299432226?check_suite_focus=true#step:7:9))
    Error: Process completed with exit code 1.
    
    opened by JounQin 3
Owner
evan
🧋
evan
Cross-runtime JavaScript framework

Primate, a cross-runtime framework Primate is a full-stack cross-runtime Javascript framework (Node.js and Deno). It relieves you of dealing with repe

null 8 Nov 7, 2022
I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certificate. I use node-signpdf and pdf-lib library.

pdf-digital-signature-with-node-signpdf-ejs I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certif

null 5 Dec 25, 2022
A JavaScript lib with more functions for the normal Date class.

A JavaScript lib with more functions for the normal Date class.

Ícaro Gabriel 4 Jan 26, 2022
A Javascript lib about complex

RealComplex.js 一个关于复数的Javascript库 A Javascript lib about complex How to use it? 使用教程 导入与实例化 import { Complex } from './Complex.js'; let x = new Comple

Lemonix-xxx 1 Feb 9, 2022
Portfólio de projetos pessoais feito com NextJs e lib de animação Framer Motion

Portfólio - Carlos Amorim Esse portfólio foi criado para mostrar meus projetos e habilidades. ?? Projeto criado com as seguintes tecnologias: ✔️ NextJ

Carlos Amorim 13 May 12, 2022
This lib was created with the aim of simplifying the use of serverless Twilio.

Twilio Functions Utils About This lib was created with the aim of simplifying the use of serverless Twilio, reducing the need to apply frequent try-ca

Iago Calazans 9 Nov 8, 2022
A lib for text highlighting by using Canvas.

canvas-highlighter 基于 canvas 实现的文本划词高亮,与文本展示的结构完全解耦,不改变文本内容的 DOM 结构。 Installation npm install canvas-highlighter Usage 最简单的实现文本划词直接高亮 import CanvasHig

null 72 Dec 24, 2022
⚗️Nitro provides a powerful toolchain and a runtime framework from the UnJS ecosystem to build and deploy any JavaScript server, anywhere

⚗️Nitro provides a powerful toolchain and a runtime framework from the UnJS ecosystem to build and deploy any JavaScript server, anywhere

unjs 1.3k Jan 5, 2023
Windmill: Open-source platform and runtime to turn any scripts into internal apps, integrations and workflows

. Open-source and self-hostable alternative to Airplane, Pipedream, Superblocks and a simplified Temporal with autogenerated UIs to trigger flows and

Windmill Labs, Inc 1.6k Jan 4, 2023
Runtime object parsing and validation with static TypeScript typing.

TypeParse Runtime object transformation, parsing and validation with inferred static TypeScript typing. Install Using npm npm install typeparse Using

Kenneth Herrera 4 May 5, 2022
A CLI tool to create a NodeJS project with TypeScript CTSP is a CLI tool to make easier to start a new NodeJS project and configure Typescript on it.

CTSP- Create TS Project A CLI tool to create a NodeJS project with TypeScript CTSP is a CLI tool to make easier to start a new NodeJS project and conf

Jean Rodríguez 7 Sep 13, 2022
io-ts Typed Event Bus for the runtime of your Node.js application. A core for any event-driven architecture based app.

Typed Event Bus Based on io-ts types, this bus provides a handy interface to publish and consume events in the current runtime of the Node.js process.

Konstantin Knyazev 3 May 23, 2022
Simple, lightweight at-runtime type checking functions, with full TypeScript support

pheno Simple, lightweight at-runtime type checking functions, with full TypeScript support Features Full TypeScript integration: TypeScript understand

Lily Scott 127 Sep 5, 2022
🦆 lightning fast duckdb bindings for bun runtime

@evan/duckdb lightning fast duckdb bindings for bun runtime Install bun add @evan/duckdb Features ?? batteries included ?? jit optimized bindings ?? 4

evan 29 Oct 20, 2022
Example code for MFE routing, shared state, build-time & runtime deploy video

Turborepo starter with pnpm This is an official starter turborepo. What's inside? This turborepo uses pnpm as a packages manager. It includes the foll

Jack Herrington 42 Nov 2, 2022
Runtime type checking in pure javascript.

Install npm install function-schema Usage import { signature } from 'function-schema'; const myFunction = signature(...ParamTypeChecks)(ReturnValueCh

Jeysson Guevara 3 May 30, 2022
Zero runtime type-safe CSS in the same file as components

macaron comptime-css is now called macaron! macaron is a zero-runtime and type-safe CSS-in-JS library made with performance in mind Powered by vanilla

Mokshit Jain 205 Jan 4, 2023
This document introduces an early implementation of the Node-RED runtime that runs on resource-constrained microcontrollers (MCUs).

Node-RED MCU Edition Copyright 2022, Moddable Tech, Inc. All rights reserved. Peter Hoddie Updated June 25, 2022 Introduction This document introduces

Peter Hoddie 53 Jan 3, 2023
Bun-Bakery is a web framework for Bun. It uses a file based router in style like svelte-kit. No need to define routes during runtime.

Bun Bakery Bun-Bakery is a web framework for Bun. It uses a file based router in style like svelte-kit. No need to define routes during runtime. Quick

Dennis Dudek 44 Dec 6, 2022