Skip to content

Commit 7e8372b

Browse files
authored
fix: include package name for duplicate bench names [#264] (#330)
## Problem Solved When running Go benchmarks across multiple packages that have benchmarks with the same name (e.g., `BenchmarkAppendMsgitem` in both `middleware/cache` and `middleware/csrf`), the results would collide since they shared identical names. This made it impossible to track them separately over time. ## Solution Benchmark names are now disambiguated by appending the package name in parentheses when multiple packages are detected in the output: - `BenchmarkFoo` → `BenchmarkFoo (github.com/example/package1)` - `BenchmarkFoo` → `BenchmarkFoo (github.com/example/package2)` **Backward compatible**: When only a single package exists (or no `pkg:` lines), names remain unchanged. ## Code Changes ### `src/extract.ts` (+33/-28 lines) - Refactored `extractGoResult()` to detect multiple packages by parsing `pkg:` lines - Split output into sections by package context - Added `chunkPairs()` helper function for cleaner `[value, unit]` pair extraction - Changed from imperative loops to a functional, chained `flatMap` approach - Exported `extractGoResult` for direct unit testing ## New Test Coverage ### `test/extractGoResult.spec.ts` (254 lines) Comprehensive unit tests covering: - Basic benchmark extraction (with/without processor count) - Multiple metrics per benchmark - Single package backward compatibility - Multiple package disambiguation (the main fix) - Edge cases (orphan benchmarks, empty input, Windows line endings) ### Test fixtures added - `go_fiber_duplicate_names_output.txt` - Real-world example with duplicate names across `cache` and `csrf` packages - `go_single_package_output.txt` - Single package output for backward compatibility testing
1 parent 45cba09 commit 7e8372b

8 files changed

Lines changed: 561 additions & 28 deletions

File tree

.coderabbit.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
reviews:
2+
pre_merge_checks:
3+
docstrings:
4+
mode: "off"

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"format": "prettier -w **",
1212
"format:check": "prettier -c **",
1313
"test": "jest",
14+
"test-s": "jest --silent",
1415
"test:watch": "jest --watch",
1516
"coverage": "jest --coverage",
1617
"coverage:open": "jest --coverage && open ./coverage/lcov-report/index.html"

src/extract.ts

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,17 @@ function extractCargoResult(output: string): BenchmarkResult[] {
343343
return ret;
344344
}
345345

346-
function extractGoResult(output: string): BenchmarkResult[] {
347-
const lines = output.split(/\r?\n/g);
348-
const ret = [];
346+
export function extractGoResult(output: string): BenchmarkResult[] {
347+
// Split into sections by "pkg:" lines, keeping package name with each section
348+
const sections = output.split(/^pkg:\s+/m).map((section, index) => {
349+
if (index === 0) return { pkg: '', lines: section.split(/\r?\n/g) };
350+
const [pkg, ...rest] = section.split(/\r?\n/g);
351+
return { pkg, lines: rest };
352+
});
353+
354+
const uniquePackages = new Set(sections.map((s) => s.pkg).filter(Boolean));
355+
const hasMultiplePackages = uniquePackages.size > 1;
356+
349357
// Example:
350358
// BenchmarkFib20-8 30000 41653 ns/op
351359
// BenchmarkDoWithConfigurer1-8 30000000 42.3 ns/op
@@ -356,15 +364,19 @@ function extractGoResult(output: string): BenchmarkResult[] {
356364
// reference, "Proposal: Go Benchmark Data Format": https://go.googlesource.com/proposal/+/master/design/14313-benchmark-format.md
357365
// "A benchmark result line has the general form: <name> <iterations> <value> <unit> [<value> <unit>...]"
358366
// "The fields are separated by runs of space characters (as defined by unicode.IsSpace), so the line can be parsed with strings.Fields. The line must have an even number of fields, and at least four."
359-
const reExtract =
367+
const reExtractRegexp =
360368
/^(?<name>Benchmark\w+[\w()$%^&*-=|,[\]{}"#]*?)(?<procs>-\d+)?\s+(?<times>\d+)\s+(?<remainder>.+)$/;
361369

362-
for (const line of lines) {
363-
const m = line.match(reExtract);
364-
if (m?.groups) {
365-
const procs = m.groups.procs !== undefined ? m.groups.procs.slice(1) : null;
366-
const times = m.groups.times;
367-
const remainder = m.groups.remainder;
370+
// Flatten sections into lines with package context, then process each line
371+
return sections
372+
.flatMap(({ pkg, lines }) => lines.map((line) => ({ pkg, line })))
373+
.flatMap(({ pkg, line }) => {
374+
const match = line.match(reExtractRegexp);
375+
if (!match?.groups) return [];
376+
377+
const { name, procs: procsRaw, times, remainder } = match.groups;
378+
const procs = procsRaw?.slice(1) ?? null;
379+
const extra = procs !== null ? `${times} times\n${procs} procs` : `${times} times`;
368380

369381
const pieces = remainder.split(/[ \t]+/);
370382

@@ -374,25 +386,19 @@ function extractGoResult(output: string): BenchmarkResult[] {
374386
pieces.unshift(pieces[0], remainder.slice(remainder.indexOf(pieces[1])));
375387
}
376388

377-
for (let i = 0; i < pieces.length; i = i + 2) {
378-
let extra = `${times} times`.replace(/\s\s+/g, ' ');
379-
if (procs !== null) {
380-
extra += `\n${procs} procs`;
381-
}
382-
const value = parseFloat(pieces[i]);
383-
const unit = pieces[i + 1];
384-
let name;
385-
if (i > 0) {
386-
name = m.groups.name + ' - ' + unit;
387-
} else {
388-
name = m.groups.name;
389-
}
390-
ret.push({ name, value, unit, extra });
391-
}
392-
}
393-
}
389+
const baseName = hasMultiplePackages && pkg ? `${name} (${pkg})` : name;
390+
// Chunk into [value, unit] pairs and map to results
391+
return chunkPairs(pieces).map(([valueStr, unit], i) => ({
392+
name: i > 0 ? `${baseName} - ${unit}` : baseName,
393+
value: parseFloat(valueStr),
394+
unit,
395+
extra,
396+
}));
397+
});
398+
}
394399

395-
return ret;
400+
function chunkPairs(arr: string[]): Array<[string, string]> {
401+
return Array.from({ length: Math.floor(arr.length / 2) }, (_, i) => [arr[i * 2], arr[i * 2 + 1]]);
396402
}
397403

398404
function extractBenchmarkJsResult(output: string): BenchmarkResult[] {

test/__snapshots__/extract.spec.ts.snap

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,163 @@ exports[`extractResult() extracts benchmark output from customSmallerIsBetter -
524524
}
525525
`;
526526

527+
exports[`extractResult() extracts benchmark output from go - go_fiber_duplicate_names_output.txt 1`] = `
528+
{
529+
"benches": [
530+
{
531+
"extra": "21228057 times
532+
4 procs",
533+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache)",
534+
"unit": "ns/op 2833.37 MB/s 0 B/op 0 allocs/op",
535+
"value": 55.76,
536+
},
537+
{
538+
"extra": "21228057 times
539+
4 procs",
540+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - ns/op",
541+
"unit": "ns/op",
542+
"value": 55.76,
543+
},
544+
{
545+
"extra": "21228057 times
546+
4 procs",
547+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - MB/s",
548+
"unit": "MB/s",
549+
"value": 2833.37,
550+
},
551+
{
552+
"extra": "21228057 times
553+
4 procs",
554+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - B/op",
555+
"unit": "B/op",
556+
"value": 0,
557+
},
558+
{
559+
"extra": "21228057 times
560+
4 procs",
561+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - allocs/op",
562+
"unit": "allocs/op",
563+
"value": 0,
564+
},
565+
{
566+
"extra": "6855655 times
567+
4 procs",
568+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache)",
569+
"unit": "ns/op 907.83 MB/s 0 B/op 0 allocs/op",
570+
"value": 174,
571+
},
572+
{
573+
"extra": "6855655 times
574+
4 procs",
575+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - ns/op",
576+
"unit": "ns/op",
577+
"value": 174,
578+
},
579+
{
580+
"extra": "6855655 times
581+
4 procs",
582+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - MB/s",
583+
"unit": "MB/s",
584+
"value": 907.83,
585+
},
586+
{
587+
"extra": "6855655 times
588+
4 procs",
589+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - B/op",
590+
"unit": "B/op",
591+
"value": 0,
592+
},
593+
{
594+
"extra": "6855655 times
595+
4 procs",
596+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - allocs/op",
597+
"unit": "allocs/op",
598+
"value": 0,
599+
},
600+
{
601+
"extra": "1000000000 times
602+
4 procs",
603+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf)",
604+
"unit": "ns/op 2678.46 MB/s 0 B/op 0 allocs/op",
605+
"value": 0.3733,
606+
},
607+
{
608+
"extra": "1000000000 times
609+
4 procs",
610+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - ns/op",
611+
"unit": "ns/op",
612+
"value": 0.3733,
613+
},
614+
{
615+
"extra": "1000000000 times
616+
4 procs",
617+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - MB/s",
618+
"unit": "MB/s",
619+
"value": 2678.46,
620+
},
621+
{
622+
"extra": "1000000000 times
623+
4 procs",
624+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - B/op",
625+
"unit": "B/op",
626+
"value": 0,
627+
},
628+
{
629+
"extra": "1000000000 times
630+
4 procs",
631+
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - allocs/op",
632+
"unit": "allocs/op",
633+
"value": 0,
634+
},
635+
{
636+
"extra": "275056135 times
637+
4 procs",
638+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf)",
639+
"unit": "ns/op 229.35 MB/s 0 B/op 0 allocs/op",
640+
"value": 4.36,
641+
},
642+
{
643+
"extra": "275056135 times
644+
4 procs",
645+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - ns/op",
646+
"unit": "ns/op",
647+
"value": 4.36,
648+
},
649+
{
650+
"extra": "275056135 times
651+
4 procs",
652+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - MB/s",
653+
"unit": "MB/s",
654+
"value": 229.35,
655+
},
656+
{
657+
"extra": "275056135 times
658+
4 procs",
659+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - B/op",
660+
"unit": "B/op",
661+
"value": 0,
662+
},
663+
{
664+
"extra": "275056135 times
665+
4 procs",
666+
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - allocs/op",
667+
"unit": "allocs/op",
668+
"value": 0,
669+
},
670+
],
671+
"commit": {
672+
"author": null,
673+
"committer": null,
674+
"id": "123456789abcdef",
675+
"message": "this is dummy",
676+
"timestamp": "dummy timestamp",
677+
"url": "https://github.com/dummy/repo",
678+
},
679+
"date": 1712131503296,
680+
"tool": "go",
681+
}
682+
`;
683+
527684
exports[`extractResult() extracts benchmark output from go - go_fiber_output.txt 1`] = `
528685
{
529686
"benches": [
@@ -806,6 +963,37 @@ exports[`extractResult() extracts benchmark output from go - go_output.txt 1`] =
806963
}
807964
`;
808965

966+
exports[`extractResult() extracts benchmark output from go - go_single_package_output.txt 1`] = `
967+
{
968+
"benches": [
969+
{
970+
"extra": "5000000 times
971+
8 procs",
972+
"name": "BenchmarkFib10",
973+
"unit": "ns/op",
974+
"value": 325,
975+
},
976+
{
977+
"extra": "30000 times
978+
8 procs",
979+
"name": "BenchmarkFib20",
980+
"unit": "ns/op",
981+
"value": 40537,
982+
},
983+
],
984+
"commit": {
985+
"author": null,
986+
"committer": null,
987+
"id": "123456789abcdef",
988+
"message": "this is dummy",
989+
"timestamp": "dummy timestamp",
990+
"url": "https://github.com/dummy/repo",
991+
},
992+
"date": 1712131503296,
993+
"tool": "go",
994+
}
995+
`;
996+
809997
exports[`extractResult() extracts benchmark output from googlecpp - googlecpp_output.json 1`] = `
810998
{
811999
"benches": [
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
PASS
2+
ok github.com/gofiber/fiber/v3/log 0.173s
3+
PASS
4+
ok github.com/gofiber/fiber/v3/middleware/adaptor 0.184s
5+
PASS
6+
ok github.com/gofiber/fiber/v3/middleware/basicauth 0.173s
7+
goos: linux
8+
goarch: amd64
9+
pkg: github.com/gofiber/fiber/v3/middleware/cache
10+
cpu: AMD EPYC 7763 64-Core Processor
11+
BenchmarkAppendMsgitem-4 21228057 55.76 ns/op 2833.37 MB/s 0 B/op 0 allocs/op
12+
BenchmarkUnmarshalitem-4 6855655 174.0 ns/op 907.83 MB/s 0 B/op 0 allocs/op
13+
PASS
14+
ok github.com/gofiber/fiber/v3/middleware/cache 2.649s
15+
PASS
16+
ok github.com/gofiber/fiber/v3/middleware/compress 0.219s
17+
PASS
18+
ok github.com/gofiber/fiber/v3/middleware/cors 0.188s
19+
goos: linux
20+
goarch: amd64
21+
pkg: github.com/gofiber/fiber/v3/middleware/csrf
22+
cpu: AMD EPYC 7763 64-Core Processor
23+
BenchmarkAppendMsgitem-4 1000000000 0.3733 ns/op 2678.46 MB/s 0 B/op 0 allocs/op
24+
BenchmarkUnmarshalitem-4 275056135 4.360 ns/op 229.35 MB/s 0 B/op 0 allocs/op
25+
PASS
26+
ok github.com/gofiber/fiber/v3/middleware/csrf 0.842s
27+
PASS
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
goos: darwin
2+
goarch: arm64
3+
pkg: github.com/example/mypackage
4+
BenchmarkFib10
5+
BenchmarkFib10-8 5000000 325 ns/op
6+
BenchmarkFib20
7+
BenchmarkFib20-8 30000 40537 ns/op
8+
PASS
9+
ok github.com/example/mypackage 3.614s

test/extract.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ describe('extractResult()', function () {
9595
tool: 'go',
9696
file: 'go_fiber_output.txt',
9797
},
98+
{
99+
tool: 'go',
100+
file: 'go_fiber_duplicate_names_output.txt',
101+
},
102+
{
103+
tool: 'go',
104+
file: 'go_single_package_output.txt',
105+
},
98106
{
99107
tool: 'benchmarkjs',
100108
file: 'benchmarkjs_output.txt',

0 commit comments

Comments
 (0)