# Benchmarks

URL: https://workclock.karthik.nz/docs/benchmarks.md

The Rust, Python and JavaScript bindings are timed on the same three cases, against businesstimedelta 1.0.1 and against the loop you would write with each language's standard library. The Python suite also times `workclock.compat`. The cases and the commands come from [benchmarks/README.md](https://github.com/Karthik-beta/workclock/blob/main/benchmarks/README.md). The numbers in [benchmarks/RESULTS.md](https://github.com/Karthik-beta/workclock/blob/main/benchmarks/RESULTS.md) come from the Benchmarks workflow on a GitHub-hosted Ubuntu runner, and RESULTS.md links the run, so anyone can open it.

Runner CPUs vary from run to run. On 2026-10-26, three runs on AMD EPYC 7763 and Intel Xeon 8573C runners moved the times by up to about 20%, while the speed-ups over businesstimedelta, measured in the same run, stayed within about 6%. Compare the "vs businesstimedelta" and "vs stdlib" columns rather than raw times.

CI also counts instructions under Valgrind for Rust and Python, and fails a pull request whose `single` or `year` case executes more than 3% more instructions. [Instruction counts](https://github.com/Karthik-beta/workclock/blob/main/benchmarks/README.md#instruction-counts) has the details.

## Cases

The Rust, Python and JavaScript suites measure the same three cases, all with
one schedule: Europe/London, Monday to Friday 09:00–17:00, a 12:00–13:00
break, and the bank holidays of England in 2026 and 2027. The 2027 ones are
there because businesstimedelta 1.0.1 raises `TypeError` for instants after
its last holiday (upstream issue 8).

| Case | Call |
|---|---|
| `single` | `between` Tuesday 2026-03-10 10:15 and 15:45 UTC |
| `year` | `between` 2026-01-01 and 2027-01-01 UTC |
| `batch` | `between` for 1,000,000 pairs: pair `i` starts `i * 7919 mod 525600` minutes into 2026 and lasts `i * 104729 mod 10080` minutes |

Building the schedule and the pairs is not timed.

## Standard-library baselines

Neither Python nor JavaScript can compute working time out of the box, so each
suite also times `stdlib`: the loop a developer would write for this one
schedule with the standard library alone. It walks the local dates from the
first instant to the second, skips weekends and holidays, and adds the
overlap with 09:00–12:00 and 13:00–17:00 London time.

| Language | Baseline | Built on |
|---|---|---|
| Python | `benchmarks/python/stdlib.py` | `datetime` and `zoneinfo` |
| JavaScript | `js/bench/stdlib.ts` | `Temporal`, on by default from Node.js 26 |

Tests check both against workclock on the `single` and `year` cases, both
daylight saving changes, Easter, a reversed pair and 10,000 `batch` pairs, so
every speed-up compares code that gives the same answers. RESULTS.md divides
each language's `stdlib` median by every row's median in its "vs stdlib"
column. Rust's standard library has no time zones, so Rust has no baseline.

## Running

```sh
cargo bench -p workclock --bench workclock
uv run pytest benchmarks/python
cd js && npm run build:node && npm run bench
```

The JavaScript suite needs Node.js 26, for `Temporal`.

The Python suite also measures workclock's `compat` API, `between_many` on
lists and on NumPy arrays, and businesstimedelta 1.0.1. It runs the single case
on `compat` and businesstimedelta a second time with a python-holidays object
for England in place of the list; `compat` fills it in for 1970 to 2100. One
round of the batch case takes businesstimedelta about half an hour; add
`-k "not (test_batch and businesstimedelta)"` to skip it.

The JavaScript suite runs the batch case three ways: `between` in a loop, and
`betweenMany` on arrays of `Date`s and on `Float64Array`s.

## Results

Cases and commands are in [README.md](https://github.com/Karthik-beta/workclock/blob/main/benchmarks/README.md).

| Machine | |
|---|---|
| Date | 2026-10-26 |
| Run | [40285792116](https://github.com/Karthik-beta/workclock/actions/runs/40285792116) |
| Runner | GitHub-hosted ubuntu24, image 20261023.334.3 |
| CPU | AMD EPYC 7763 64-Core Processor, 4 logical cores |
| Memory | 16 GiB |
| OS | Linux-6.17.0-1022-azure-x86_64-with-glibc2.39 |
| Rust | rustc 1.99.1 (58cf8f9c3 2026-10-13) |
| Python | 3.15.1 |
| Node.js | v26.12.1 |

## single

| Implementation | Median | p95 | Samples | vs businesstimedelta | vs stdlib |
|---|---|---|---|---|---|
| Rust | 1.19 µs | 1.2 µs | 100 | 140× | |
| JavaScript | 1.77 µs | 1.81 µs | 1109598 | 95× | 5.1× |
| Python, workclock | 3.96 µs | 4.08 µs | 50564 | 43× | 1× |
| Python, stdlib | 4.01 µs | 4.12 µs | 41903 | 42× | 1× |
| Python, workclock.compat, python-holidays | 6.93 µs | 7.33 µs | 1829 | 24× | 0.58× |
| Python, workclock.compat | 7.24 µs | 14 µs | 9427 | 23× | 0.55× |
| JavaScript, stdlib | 9.06 µs | 9.6 µs | 173166 | 19× | 1× |
| Python, businesstimedelta | 169 µs | 182 µs | 3879 | 1× | 0.024× |
| Python, businesstimedelta, python-holidays | 196 µs | 210 µs | 2790 | 0.86× | 0.02× |

## year

| Implementation | Median | p95 | Samples | vs businesstimedelta | vs stdlib |
|---|---|---|---|---|---|
| Rust | 84.5 µs | 86.9 µs | 100 | 630× | |
| Python, workclock | 85.4 µs | 96.1 µs | 8093 | 620× | 8.4× |
| Python, workclock.compat | 88.7 µs | 100 µs | 5481 | 600× | 8.1× |
| JavaScript | 106 µs | 114 µs | 18747 | 500× | 16× |
| Python, stdlib | 719 µs | 747 µs | 1256 | 74× | 1× |
| JavaScript, stdlib | 1.71 ms | 1.74 ms | 891 | 31× | 1× |
| Python, businesstimedelta | 53 ms | 54.7 ms | 19 | 1× | 0.014× |

## batch

| Implementation | Median | p95 | Samples | vs businesstimedelta | vs stdlib |
|---|---|---|---|---|---|
| Rust | 1.96 s | 1.97 s | 10 | 300× | |
| Python, workclock between_many, NumPy | 2.13 s | 2.15 s | 5 | 280× | 4.7× |
| JavaScript, betweenMany, Float64Array | 2.54 s | 2.55 s | 5 | 230× | 15× |
| JavaScript, between in a loop | 2.68 s | 2.68 s | 5 | 220× | 14× |
| JavaScript, betweenMany, Dates | 2.68 s | 2.71 s | 5 | 220× | 14× |
| Python, workclock between_many | 3.42 s | 3.45 s | 5 | 170× | 2.9× |
| Python, workclock | 4.6 s | 4.62 s | 3 | 130× | 2.2× |
| Python, workclock.compat | 7.99 s | 8.01 s | 3 | 74× | 1.2× |
| Python, stdlib | 9.95 s | 9.95 s | 3 | 60× | 1× |
| JavaScript, stdlib | 38.3 s | 39 s | 5 | 16× | 1× |
| Python, businesstimedelta | 9.9 min | 9.9 min | 1 | 1× | 0.017× |
