iterable-linq-utility 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,6 +59,26 @@ pnpm bench chains # only the chains
59
59
  BENCH_TIME=1000 pnpm bench # more time for each case (default 500 ms): more samples, less noise
60
60
  ```
61
61
 
62
+ ### Report
63
+
64
+ A pull request that adds an operation or changes its performance pastes the report in its description, as it is:
65
+
66
+ ```sh
67
+ pnpm bench:report functions/sum # the same filter as pnpm bench
68
+ BENCH_HOST="MacBook Pro M3" pnpm bench:report functions/sum # names the host, which a container cannot see
69
+ ```
70
+
71
+ ```text
72
+ Environment: Alpine Linux v3.24 (Docker), arm64, 12 cores, CPU unknown · host MacBook Pro M3 · Node v24.20.0 · Vitest 5.0.2 · BENCH_TIME 500 ms · commit 2fba44d
73
+
74
+ | case | variant | ops/s | mean (ms) | p75 (ms) | p99 (ms) | rme | vs native |
75
+ | --- | --- | --- | --- | --- | --- | --- | --- |
76
+ | sum/map | native | 567 | 1.79 | 1.76 | 3.64 | ±2.5% | — |
77
+ | sum/map | chain | 855 | 1.2 | 1.26 | 1.76 | ±1.8% | +51% |
78
+ ```
79
+
80
+ `vs native` compares the ops/s with the `native` case of the same group. When a baseline exists, a `vs baseline` column compares them with it. [ADR 0021](docs/decisions/0021-benchmark-standards.md) explains the format.
81
+
62
82
  ### Find regressions
63
83
 
64
84
  A baseline is a saved run. Each table shows it as extra rows, marked `(baseline)`, next to the new run.
@@ -68,6 +88,7 @@ git switch main
68
88
  pnpm bench:baseline # saves every result in .bench/
69
89
  git switch my-branch
70
90
  pnpm bench # shows each case next to its "(baseline)" row
91
+ pnpm bench:report functions/sum # or the report, with a "vs baseline" column
71
92
  ```
72
93
 
73
94
  The timings depend on the machine, so `.bench/` is not committed and the benchmarks do not run in CI. Create the baseline and the new run on the same machine, with the same load.
@@ -76,7 +97,7 @@ The timings depend on the machine, so `.bench/` is not committed and the benchma
76
97
 
77
98
  | Column | Meaning |
78
99
  |---|---|
79
- | `hz` | runs per second: higher is faster |
100
+ | `hz` | runs per second (ops/s in the report): higher is faster |
80
101
  | `mean`, `p75`, `p99` | time of one run, in ms |
81
102
  | `rme` | relative margin of error |
82
103
  | `samples` | how many runs were measured |
@@ -85,26 +106,28 @@ A difference smaller than the `rme` of the two rows is noise.
85
106
 
86
107
  ### Add a benchmark
87
108
 
88
- Create `test/bench/functions/<name>.bench.ts` or `test/bench/chains/<scenario>.bench.ts`:
109
+ Create `test/bench/functions/<name>.bench.ts`. `scenarios()` registers the standard groups of [ADR 0021](docs/decisions/0021-benchmark-standards.md) and [ADR 0022](docs/decisions/0022-benchmark-scenarios-in-practice.md): `<name>/direct` on `numbers`, `<name>/small` on `small`, `<name>/map` and `<name>/filter` after `map(double)` and `filter(isEven)`. `group()` adds one more group on `numbers`: an early exit (`start`, `middle`) or an optional callback.
89
110
 
90
111
  ```ts
91
- import { test } from 'vitest';
92
112
  import * as Helpers from '../helpers';
93
113
 
94
114
  import * as IterableLinq from 'iterable-linq-utility';
95
115
 
96
116
  // Read the exports once: an imported binding goes through a module runner getter on every read.
97
- const { from } = IterableLinq;
98
- const { cases, numbers, sum } = Helpers;
99
-
100
- test('map: number', async ({ bench }) => {
101
- await cases(bench, 'map/number') // the baseline folder: <function>/<variant>
102
- .add('native', () => sum(numbers.map(v => v * 2)))
103
- .add('chain', () => sum(from(numbers).map(v => v * 2)))
104
- .run();
117
+ const { Functions } = IterableLinq;
118
+ const { double, scenarios, sum } = Helpers;
119
+
120
+ scenarios('map', {
121
+ native: values => sum(values.map(double)), // an array
122
+ chain: chain => sum(chain.map(double)), // a chain
123
+ Functions: values => sum(Functions.map(values, double)) // an iterable
105
124
  });
106
125
  ```
107
126
 
127
+ - `native` is the array method a user would write. Add `loop`, a hand-written `for…of`, when the array method is a different algorithm (`reduce` for `sum`); when there is no array method, `native` is the loop.
128
+ - `pnpm check:structure` fails when the bench of an operation has no `direct` or `small` group. `test/bench/functions/sum.bench.ts` is the example.
129
+ - The benches of `test/bench/chains/<scenario>.bench.ts`, and the groups that need other data, use `cases(bench, '<function>/<group>')` directly: the id is also the baseline folder.
130
+
108
131
  - Every table needs at least two cases: add a native reference.
109
132
  - Import the library and the helpers as namespaces and copy the exports into local constants, as above. Vitest prints a `Benchmark Warning` when a benchmark reads an imported binding too many times.
110
- - The shared data is in `test/bench/helpers.ts`: `numbers` (100,000 integers), `records` (100,000 objects) and `small` (1,000 integers).
133
+ - The shared data is in `test/bench/helpers.ts`: `numbers` (100,000 integers) for every scenario, `small` (1,000 integers) for `<name>/small`, `records` (100,000 objects) only for a key or a selector on objects. The callbacks and values of the scenarios are there too: `double`, `isEven`, `first`, `middle`, `missing`.