Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
reviews:
pre_merge_checks:
docstrings:
mode: "off"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/coverage
/dist
/.idea
/.ai
target/
jmh-result.json
.env
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"format": "prettier -w **",
"format:check": "prettier -c **",
"test": "jest",
"test-s": "jest --silent",
"test:watch": "jest --watch",
"coverage": "jest --coverage",
"coverage:open": "jest --coverage && open ./coverage/lcov-report/index.html"
Expand Down
61 changes: 33 additions & 28 deletions src/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
}

interface PullRequest {
[key: string]: any;

Check warning on line 37 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

Unexpected any. Specify a different type
number: number;
html_url?: string;
body?: string;
Expand Down Expand Up @@ -343,9 +343,16 @@
return ret;
}

function extractGoResult(output: string): BenchmarkResult[] {
const lines = output.split(/\r?\n/g);
const ret = [];
export function extractGoResult(output: string): BenchmarkResult[] {
// Split into sections by "pkg:" lines, keeping package name with each section
const sections = output.split(/^pkg:\s+/m).map((section, index) => {
if (index === 0) return { pkg: '', lines: section.split(/\r?\n/g) };
const [pkg, ...rest] = section.split(/\r?\n/g);
return { pkg, lines: rest };
});

const hasMultiplePackages = sections.filter((s) => s.pkg).length > 1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Could sections have the same pkg? I'm thinking if this is correct or if we need a Set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it's possible to have duplicate pkg sections but it's an easy improvement. Thanks!


// Example:
// BenchmarkFib20-8 30000 41653 ns/op
// BenchmarkDoWithConfigurer1-8 30000000 42.3 ns/op
Expand All @@ -356,15 +363,19 @@
// reference, "Proposal: Go Benchmark Data Format": https://go.googlesource.com/proposal/+/master/design/14313-benchmark-format.md
// "A benchmark result line has the general form: <name> <iterations> <value> <unit> [<value> <unit>...]"
// "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."
const reExtract =
const reExtractRegexp =
/^(?<name>Benchmark\w+[\w()$%^&*-=|,[\]{}"#]*?)(?<procs>-\d+)?\s+(?<times>\d+)\s+(?<remainder>.+)$/;

for (const line of lines) {
const m = line.match(reExtract);
if (m?.groups) {
const procs = m.groups.procs !== undefined ? m.groups.procs.slice(1) : null;
const times = m.groups.times;
const remainder = m.groups.remainder;
// Flatten sections into lines with package context, then process each line
return sections
.flatMap(({ pkg, lines }) => lines.map((line) => ({ pkg, line })))
.flatMap(({ pkg, line }) => {
const match = line.match(reExtractRegexp);
if (!match?.groups) return [];

const { name, procs: procsRaw, times, remainder } = match.groups;
const procs = procsRaw?.slice(1) ?? null;
const extra = procs !== null ? `${times} times\n${procs} procs` : `${times} times`;

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

Expand All @@ -374,25 +385,19 @@
pieces.unshift(pieces[0], remainder.slice(remainder.indexOf(pieces[1])));
}

for (let i = 0; i < pieces.length; i = i + 2) {
let extra = `${times} times`.replace(/\s\s+/g, ' ');
if (procs !== null) {
extra += `\n${procs} procs`;
}
const value = parseFloat(pieces[i]);
const unit = pieces[i + 1];
let name;
if (i > 0) {
name = m.groups.name + ' - ' + unit;
} else {
name = m.groups.name;
}
ret.push({ name, value, unit, extra });
}
}
}
const baseName = hasMultiplePackages && pkg ? `${name} (${pkg})` : name;
// Chunk into [value, unit] pairs and map to results
return chunkPairs(pieces).map(([valueStr, unit], i) => ({
name: i > 0 ? `${baseName} - ${unit}` : baseName,
value: parseFloat(valueStr),
unit,
extra,
}));
});
}

return ret;
function chunkPairs(arr: string[]): Array<[string, string]> {
return Array.from({ length: Math.floor(arr.length / 2) }, (_, i) => [arr[i * 2], arr[i * 2 + 1]]);
Comment on lines +399 to +400
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against odd-length value/unit sequences.

chunkPairs silently drops the last token when arr.length is odd, which can yield partial or misleading metrics on malformed input. Consider throwing to surface invalid Go output early.

🛡️ Suggested validation
 function chunkPairs(arr: string[]): Array<[string, string]> {
+    if (arr.length % 2 !== 0) {
+        throw new Error(
+            `Malformed Go benchmark metrics (expected value/unit pairs, got ${arr.length} fields): ${arr.join(' ')}`
+        );
+    }
     return Array.from({ length: Math.floor(arr.length / 2) }, (_, i) => [arr[i * 2], arr[i * 2 + 1]]);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function chunkPairs(arr: string[]): Array<[string, string]> {
return Array.from({ length: Math.floor(arr.length / 2) }, (_, i) => [arr[i * 2], arr[i * 2 + 1]]);
function chunkPairs(arr: string[]): Array<[string, string]> {
if (arr.length % 2 !== 0) {
throw new Error(
`Malformed Go benchmark metrics (expected value/unit pairs, got ${arr.length} fields): ${arr.join(' ')}`
);
}
return Array.from({ length: Math.floor(arr.length / 2) }, (_, i) => [arr[i * 2], arr[i * 2 + 1]]);
}
🤖 Prompt for AI Agents
In `@src/extract.ts` around lines 399 - 400, The chunkPairs function currently
drops the last token when given an odd-length arr; add a validation at the top
of chunkPairs to check arr.length % 2 === 0 and throw a descriptive error
(including arr length or the offending token) when odd so malformed Go output
surfaces immediately; keep the rest of the logic intact (Array.from(...)) and
ensure callers can catch this exception if necessary.

}

function extractBenchmarkJsResult(output: string): BenchmarkResult[] {
Expand Down Expand Up @@ -441,7 +446,7 @@
const extra = `mean: ${mean} ${meanUnit}\nrounds: ${stats.rounds}`;
return { name, value, unit, range, extra };
});
} catch (err: any) {

Check warning on line 449 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

Unexpected any. Specify a different type
throw new Error(
`Output file for 'pytest' must be JSON file generated by --benchmark-json option: ${err.message}`,
);
Expand All @@ -452,7 +457,7 @@
let json: GoogleCppBenchmarkJson;
try {
json = JSON.parse(output);
} catch (err: any) {

Check warning on line 460 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

Unexpected any. Specify a different type
throw new Error(
`Output file for 'googlecpp' must be JSON file generated by --benchmark_format=json option: ${err.message}`,
);
Expand Down Expand Up @@ -580,7 +585,7 @@
return ret;
}

function extractJuliaBenchmarkHelper([_, bench]: JuliaBenchmarkGroup, labels: string[] = []): BenchmarkResult[] {

Check warning on line 588 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

'_' is defined but never used
const res: BenchmarkResult[] = [];
for (const key in bench.data) {
const value = bench.data[key];
Expand Down Expand Up @@ -611,7 +616,7 @@
let json: JuliaBenchmarkJson;
try {
json = JSON.parse(output);
} catch (err: any) {

Check warning on line 619 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

Unexpected any. Specify a different type
throw new Error(
`Output file for 'julia' must be JSON file generated by BenchmarkTools.save("output.json", suit::BenchmarkGroup) : ${err.message}`,
);
Expand All @@ -629,7 +634,7 @@
let json: JmhBenchmarkJson[];
try {
json = JSON.parse(output);
} catch (err: any) {

Check warning on line 637 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

Unexpected any. Specify a different type
throw new Error(`Output file for 'jmh' must be JSON file generated by -rf json option: ${err.message}`);
}
return json.map((b) => {
Expand All @@ -646,7 +651,7 @@
let json: BenchmarkDotNetBenchmarkJson;
try {
json = JSON.parse(output);
} catch (err: any) {

Check warning on line 654 in src/extract.ts

View workflow job for this annotation

GitHub Actions / Run linting and formatting check

Unexpected any. Specify a different type
throw new Error(
`Output file for 'benchmarkdotnet' must be JSON file generated by '--exporters json' option or by adding the JsonExporter to your run config: ${err.message}`,
);
Expand Down
188 changes: 188 additions & 0 deletions test/__snapshots__/extract.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,163 @@ exports[`extractResult() extracts benchmark output from customSmallerIsBetter -
}
`;

exports[`extractResult() extracts benchmark output from go - go_fiber_duplicate_names_output.txt 1`] = `
{
"benches": [
{
"extra": "21228057 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache)",
"unit": "ns/op 2833.37 MB/s 0 B/op 0 allocs/op",
"value": 55.76,
},
{
"extra": "21228057 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - ns/op",
"unit": "ns/op",
"value": 55.76,
},
{
"extra": "21228057 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - MB/s",
"unit": "MB/s",
"value": 2833.37,
},
{
"extra": "21228057 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - B/op",
"unit": "B/op",
"value": 0,
},
{
"extra": "21228057 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/cache) - allocs/op",
"unit": "allocs/op",
"value": 0,
},
{
"extra": "6855655 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache)",
"unit": "ns/op 907.83 MB/s 0 B/op 0 allocs/op",
"value": 174,
},
{
"extra": "6855655 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - ns/op",
"unit": "ns/op",
"value": 174,
},
{
"extra": "6855655 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - MB/s",
"unit": "MB/s",
"value": 907.83,
},
{
"extra": "6855655 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - B/op",
"unit": "B/op",
"value": 0,
},
{
"extra": "6855655 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/cache) - allocs/op",
"unit": "allocs/op",
"value": 0,
},
{
"extra": "1000000000 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf)",
"unit": "ns/op 2678.46 MB/s 0 B/op 0 allocs/op",
"value": 0.3733,
},
{
"extra": "1000000000 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - ns/op",
"unit": "ns/op",
"value": 0.3733,
},
{
"extra": "1000000000 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - MB/s",
"unit": "MB/s",
"value": 2678.46,
},
{
"extra": "1000000000 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - B/op",
"unit": "B/op",
"value": 0,
},
{
"extra": "1000000000 times
4 procs",
"name": "BenchmarkAppendMsgitem (github.com/gofiber/fiber/v3/middleware/csrf) - allocs/op",
"unit": "allocs/op",
"value": 0,
},
{
"extra": "275056135 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf)",
"unit": "ns/op 229.35 MB/s 0 B/op 0 allocs/op",
"value": 4.36,
},
{
"extra": "275056135 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - ns/op",
"unit": "ns/op",
"value": 4.36,
},
{
"extra": "275056135 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - MB/s",
"unit": "MB/s",
"value": 229.35,
},
{
"extra": "275056135 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - B/op",
"unit": "B/op",
"value": 0,
},
{
"extra": "275056135 times
4 procs",
"name": "BenchmarkUnmarshalitem (github.com/gofiber/fiber/v3/middleware/csrf) - allocs/op",
"unit": "allocs/op",
"value": 0,
},
],
"commit": {
"author": null,
"committer": null,
"id": "123456789abcdef",
"message": "this is dummy",
"timestamp": "dummy timestamp",
"url": "https://github.com/dummy/repo",
},
"date": 1712131503296,
"tool": "go",
}
`;

exports[`extractResult() extracts benchmark output from go - go_fiber_output.txt 1`] = `
{
"benches": [
Expand Down Expand Up @@ -806,6 +963,37 @@ exports[`extractResult() extracts benchmark output from go - go_output.txt 1`] =
}
`;

exports[`extractResult() extracts benchmark output from go - go_single_package_output.txt 1`] = `
{
"benches": [
{
"extra": "5000000 times
8 procs",
"name": "BenchmarkFib10",
"unit": "ns/op",
"value": 325,
},
{
"extra": "30000 times
8 procs",
"name": "BenchmarkFib20",
"unit": "ns/op",
"value": 40537,
},
],
"commit": {
"author": null,
"committer": null,
"id": "123456789abcdef",
"message": "this is dummy",
"timestamp": "dummy timestamp",
"url": "https://github.com/dummy/repo",
},
"date": 1712131503296,
"tool": "go",
}
`;

exports[`extractResult() extracts benchmark output from googlecpp - googlecpp_output.json 1`] = `
{
"benches": [
Expand Down
27 changes: 27 additions & 0 deletions test/data/extract/go_fiber_duplicate_names_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
PASS
ok github.com/gofiber/fiber/v3/log 0.173s
PASS
ok github.com/gofiber/fiber/v3/middleware/adaptor 0.184s
PASS
ok github.com/gofiber/fiber/v3/middleware/basicauth 0.173s
goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3/middleware/cache
cpu: AMD EPYC 7763 64-Core Processor
BenchmarkAppendMsgitem-4 21228057 55.76 ns/op 2833.37 MB/s 0 B/op 0 allocs/op
BenchmarkUnmarshalitem-4 6855655 174.0 ns/op 907.83 MB/s 0 B/op 0 allocs/op
PASS
ok github.com/gofiber/fiber/v3/middleware/cache 2.649s
PASS
ok github.com/gofiber/fiber/v3/middleware/compress 0.219s
PASS
ok github.com/gofiber/fiber/v3/middleware/cors 0.188s
goos: linux
goarch: amd64
pkg: github.com/gofiber/fiber/v3/middleware/csrf
cpu: AMD EPYC 7763 64-Core Processor
BenchmarkAppendMsgitem-4 1000000000 0.3733 ns/op 2678.46 MB/s 0 B/op 0 allocs/op
BenchmarkUnmarshalitem-4 275056135 4.360 ns/op 229.35 MB/s 0 B/op 0 allocs/op
PASS
ok github.com/gofiber/fiber/v3/middleware/csrf 0.842s
PASS
9 changes: 9 additions & 0 deletions test/data/extract/go_single_package_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
goos: darwin
goarch: arm64
pkg: github.com/example/mypackage
BenchmarkFib10
BenchmarkFib10-8 5000000 325 ns/op
BenchmarkFib20
BenchmarkFib20-8 30000 40537 ns/op
PASS
ok github.com/example/mypackage 3.614s
8 changes: 8 additions & 0 deletions test/extract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ describe('extractResult()', function () {
tool: 'go',
file: 'go_fiber_output.txt',
},
{
tool: 'go',
file: 'go_fiber_duplicate_names_output.txt',
},
{
tool: 'go',
file: 'go_single_package_output.txt',
},
{
tool: 'benchmarkjs',
file: 'benchmarkjs_output.txt',
Expand Down
Loading
Loading