npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

benchik

v1.3.0

Published

A tiny benchmark library with via a simple `console.log` and `console.group`, works nicely in a browser and terminal

Readme

benchik

A tiny benchmark library using console.log and console.group. Works equally well in the browser and terminal - zero dependencies, ~200 lines of code.

Use this library if you need a quick, fast and reliable peek to benchmark results.

import { bench } from "benchik"

bench("Array.from", () => { Array.from({ length: 1000 }) })
bench("for loop",  () => { const a = []; for (let i = 0; i < 1000; i++) a.push(i) })

Install

npm i benchik

Benchik ships as raw TypeScript, import it directly with any ESM-compatible runtime (Bun, tsx, ts-node, or a bundler like Vite/esbuild).

Usage

Basic benchmark

bench(() => Math.sqrt(Math.random()))
// → [1.40ns] Math.sqrt(Math.random())

bench("sqrt 1000x", () => { for (let i = 0; i < 1000; i++) Math.sqrt(i) })
// → [500.00ns] sqrt 1000x

Grouped benchmarks

Groups show which test was fastest and how much slower the others were:

using g = bench.group("Array iteration")

bench("for",      () => { for (let i = 0; i < 1e4; i++) })
bench("forEach",  () => { Array(1e4).fill(0).forEach(x => x) })
bench("for..of",  () => { for (const x of Array(1e4).fill(0)) })
Array iteration
  [5.00ms]  for       [fastest]
  [8.30ms]  forEach   [+1.66x]
  [5.10ms]  for..of   [+1.02x]

Fresh data per iteration

Prevent state cross-contamination between runs with fresh(), each iteration gets its own copy:

using g = bench.group("Array push")
const f = g.fresh(() => ({ array: [1, 2, 3] }))

bench("push",      () => { f.array.push(4) })
bench("spread",    () => { const x = [...f.array, 4] })

Assertions

Verify that every iteration returns the same value:

using g = bench.group("Sorting")
g.assert = [1, 2, 3, 4, 5]

bench("unsorted", () => [3, 1, 5, 2, 4])
bench("sorted", () => [3, 1, 5, 2, 4].sort())
// → [40.00ns] [✗] unsorted
// → [50.00µs] [✓] sorted

Deterministic randomness

Use bench.random() to generate the same pseudorandom sequence across warmup and measurement phases:

bench("rand test", () => {
  const seed = bench.random()
  // `seed` is deterministic, same in warmup and measurement.
})

Wait for JIT compilation

// Declare static variables and compute things.
const d1 = []
const d2 = [2, 3, 4]

// Wait for JIT to make sure the first benchmark doesn't get slowed down.
await bench.untilCompiled()

bench("first test", () => { /* ... */ })

Black hole

The JS engines may optimize pure functions that doesn't really do anything. To avoid skipping over iterations or function execution, always return the output of iterations.

For example, if you're benchmarking for loop iteration speed, you should setup some quick calculations like summing many numbers or pushing numbers to array and then returning them. If you don't return anything and just do empty iterations, the engine might skip them.

const MAX = 1_000

using g = bench.group("loops")
g.assert = MAX

let r = 0
bench("for", () => {
  r = 0
  for (let i = 0; i < MAX; i++) r += 1
  return r
})

Even though these calculations introduce overhead, it's very often tolerable

Relativeness & Fairness (advice)

When measuring speed, the actual numbers matter, but usually it's more important how an option/approach/solution is better/faster over another one.

So remember to make sure your benchmarks are fair to each other, e.g. init same amount of variables, do the same setups. It should be clear what you're actually measuring and comparing between.

Full Example

import { bench } from "benchik"

class asd { }

const arrD = []
const MAX = 1_000
const finalResult = Array.from({ length: MAX }).map(() => new asd)

await bench.untilCompiled()

{
  using _ = bench.group("Array Populate")

  bench("dynamic (push)", () => {
    const array = []
    for (let i = 0; i < MAX; i++) array.push(new asd)
    return array
  })
  bench("dynamic (no push)", () => {
    const array = []
    for (let i = 0; i < MAX; i++) array[i] = new asd
    return array
  })
  bench("dynamic (no push via length)", () => {
    const array = []
    for (let i = 0; i < MAX; i++) array[array.length] = new asd
    return array
  })
  bench("pre-allocate", () => {
    const array = Array(MAX)
    for (let i = 0; i < MAX; i++) array[i] = new asd
    return array
  })
  bench("reusing", () => {
    const array = arrD.fill(null, 0, MAX)
    for (let i = 0; i < MAX; i++) array[i] = new asd
    return array
  })
}

Then simply run the file with your JS runtime, for example:

bun benchmark/array.js

And you will have result like this:

Array Populate
  [038.272µs] [+1.46x] dynamic (push)
  [034.935µs] [+1.33x] dynamic (no push)
  [035.316µs] [+1.34x] dynamic (no push via length)
  [029.726µs] [+1.13x] pre-allocate
  [026.259µs] [fastest] reusing

This example is also placed at ./examples/array.js.

Browser usage

Benchik works natively in the browser, just import from a CDN:

const { bench } = await import("https://esm.sh/benchik")
using _ = bench.group("Array Populate")

bench("dynamic (push)", () => {
  const array = []
  for (let i = 0; i < MAX; i++) array.push(new asd)
  return array
})
bench("dynamic (no push)", () => {
  const array = []
  for (let i = 0; i < MAX; i++) array[i] = new asd
  return array
})

Or via module script:

<script type="module">
import { bench } from "https://esm.sh/benchik"

using g = bench.group("DOM tests")
bench("createElement", () => document.createElement("div"))
bench("querySelector", () => document.querySelector("body"))
</script>

API

bench(label?, callback)

Runs a 50ms warmup phase, then measures the callback for 50ms and reports the median iteration time.

bench.group(label)

Creates a console group. Returns a Disposable object (works with using declarations).

| Property | Description | |----------|-------------| | fresh(factory) | Returns fresh values per iteration | | assert | Expected return value for all iterations |

bench.random()

Returns a deterministic pseudorandom number (mulberry32). Reseeded to 0xDEADBEEF before both warmup and measurement phases for reproducibility.

bench.untilCompiled()

Returns a promise that resolves after 200ms, giving the runtime time to JIT-compile objects before the first benchmark runs.

Agent Instruction

You can adopt this instruction to allow your agent to run benchmarks from time to time and have better insights for you.

---
description: Asses code speed, do benchmarks.
---

- Read files in `github.com/FrameMuse/benchik/tree/main/examples`, these are benchmarks of a real project, results of which can be used to make considerations, include in the task or show to a user.
- To run the benchmarks, run `file.js`.
- To create a permanent benchmark, create a file (`.js` format, ESNext style) in a desired path.
- To create a temporary benchmark, create a `.js` file in `/tmp/benchiky` directory, run `/tmp/benchiky/file.js` and use the results.

Use installed JS runtime: `bun` or `node` or `deno`.

(Bun only) Note: make sure to import a benchmark suite, use `bun link benchik` once and then import like usual `import { bench } from "benchik"`.

Browser & terminal

Benchik uses only universally available APIs:

  • performance.now(): the high-resolution monotonic clock available in all browsers and Node.js/Bun/Deno
  • console.log / console.group / console.groupEnd: native console output everywhere
  • ANSI escape codes for color

No DOM, no Node-specific APIs, no runtime-specific imports. The same code runs identically in both environments.

Why it's reliable

  • Warmup phase: the callback runs for 50ms before measurement begins. This allows JIT compilation to stabilize and CPU caches to warm, so the first measurement isn't artificially slow.
  • Deterministic random: the mulberry32 randomizer is reset with the same value before both warmup and measurement, ensuring identical random inputs and eliminating a benchmark variance.
  • Fresh data pattern: g.fresh() creates a new data object for each iteration, preventing state mutations from one run affecting the cost of the next (a common pitfall in JS microbenchmarks).
  • Median aggregation: default aggregation uses the median, gives a more representative measurement than the mean (average).
  • Runs each benchmark either for 50ms or 50_000 times, which works great both for very slow and very fast programs.
  • No dependencies: zero third-party code means zero risk of dependency-related performance noise or version conflicts.

Why you may not need it

  • Benchmarks are measured in perfect conditions, which works most of the time, but might not be what you need.
  • This library doesn't measure time deviations, only final measurement, stable across multiple runs.
  • This library doesn't measure memory usage.