Skip to content

Add CodSpeed performance benchmarks#153

Closed
codspeed-hq[bot] wants to merge 1 commit into
mainfrom
codspeed-wizard-1779009447401
Closed

Add CodSpeed performance benchmarks#153
codspeed-hq[bot] wants to merge 1 commit into
mainfrom
codspeed-wizard-1779009447401

Conversation

@codspeed-hq
Copy link
Copy Markdown

@codspeed-hq codspeed-hq Bot commented May 17, 2026

Summary

This PR adds continuous performance benchmarking to node_reqwest using CodSpeed with vitest benchmarks in simulation mode.

What changed

Refactored normalize utilities into a standalone module

Extracted normalizeHeaders, normalizePem, and normalizeBody from packages/node/export/agent.ts into a new packages/node/export/normalize.ts. These are pure TypeScript functions that don't depend on the native addon, making them independently testable and benchmarkable. The agent.ts module now imports from normalize.ts -- no behavior change.

Added 18 vitest benchmarks across three suites

  • normalizeHeaders (7 benchmarks): empty input, object with string values, object with array values, object with many headers, flat string array pairs, Map iterable, mixed value types
  • normalizePem (6 benchmarks): undefined input, single string, single Buffer, array of strings, array of Buffers, mixed array
  • normalizeBody (5 benchmarks): null input, string body, Buffer body, Uint8Array body, large string body

Added CodSpeed CI workflow (.github/workflows/codspeed.yaml)

Runs benchmarks on pushes to main, pull requests, and manual dispatch. Uses OIDC authentication and simulation mode for deterministic, low-variance measurements.

Added CodSpeed badge to README.

Notes on runners

This repository is owned by a personal GitHub account, so CodSpeed's dedicated macro runners are not available. The workflow uses ubuntu-latest runners. Since these benchmarks run in simulation mode (CPU instruction counting), this does not affect measurement accuracy -- simulation mode produces deterministic results regardless of the runner hardware.

Next steps

  • As more functionality is implemented in the core crate and the Node.js binding layer, add benchmarks for the native addon calls (e.g., Agent construction, HTTP dispatch)
  • Review the CodSpeed dashboard after the first CI run to establish baseline measurements
  • Consider adding benchmarks for end-to-end HTTP request flows once agentDispatch is implemented

@semanticdiff-com
Copy link
Copy Markdown

semanticdiff-com Bot commented May 17, 2026

Review changes with  SemanticDiff

Changed Files
File Status
  packages/node/package.json  50% smaller
  packages/node/export/agent.ts  5% smaller
  pnpm-lock.yaml  2% smaller
  .github/workflows/codspeed.yaml  0% smaller
  README.md Unsupported file format
  packages/node/export/normalize.ts  0% smaller
  packages/node/tests/bench/normalize-body.bench.ts  0% smaller
  packages/node/tests/bench/normalize-headers.bench.ts  0% smaller
  packages/node/tests/bench/normalize-pem.bench.ts  0% smaller
  packages/node/vitest.bench.config.mts  0% smaller

timeout-minutes: 30
permissions:
contents: "read"
id-token: "write"
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​codspeed/​vitest-plugin@​5.4.0971009892100

View full report

@codspeed-hq
Copy link
Copy Markdown
Author

codspeed-hq Bot commented May 17, 2026

Congrats! CodSpeed is installed 🎉

🆕 18 new benchmarks were detected.

You will start to see performance impacts in the reports once the benchmarks are run from your default branch.

Detected benchmarks


Open in CodSpeed

@codspeed-hq codspeed-hq Bot marked this pull request as ready for review May 17, 2026 09:31
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 17, 2026

Greptile Summary

This PR wires up CodSpeed continuous performance benchmarking by extracting the three normalize utilities (normalizePem, normalizeHeaders, normalizeBody) from agent.ts into a standalone normalize.ts module, then adding 18 vitest benchmarks across three suites plus a CodSpeed CI workflow.

  • The normalize.ts extraction is a clean, no-behavior-change refactor; agent.ts simply re-exports from the new module.
  • The CI workflow is well-hardened (pinned SHA actions, minimal permissions, OIDC auth, --run flag).
  • normalize-body.bench.ts creates its fixture inputs (including a 1 KB \"x\".repeat(1024) + JSON.stringify) inside the bench callback on every iteration, so the measurements include allocation and serialization overhead rather than pure normalizeBody performance — unlike normalize-pem.bench.ts which correctly hoists all fixtures to module level.

Confidence Score: 4/5

Safe to merge; the production code change is a pure refactor with no behavior change, and the benchmark inaccuracies only affect measurement quality.

The normalize-body benchmarks measure allocation and JSON serialization overhead on every iteration rather than isolating the function under test, which will produce inflated and misleading baseline numbers in the CodSpeed dashboard from the first run onward. The other issues (missing --run flag, bare semver specifier) are minor. Production code is unaffected.

packages/node/tests/bench/normalize-body.bench.ts needs fixtures hoisted out of the bench callbacks before the first CI run establishes baselines.

Important Files Changed

Filename Overview
packages/node/tests/bench/normalize-body.bench.ts New benchmark file; three of five bench callbacks allocate their input fixtures on every iteration (Buffer, Uint8Array, and especially a 1 KB repeated string + JSON.stringify), inflating measured timings and making them incomparable to the PEM benchmarks that hoist fixtures to module level.
packages/node/tests/bench/normalize-headers.bench.ts New benchmark file; mostly correct, but the Map iterable bench constructs the Map inside the callback, adding Map-creation overhead to what should be a pure normalizeHeaders measurement.
packages/node/tests/bench/normalize-pem.bench.ts New benchmark file; correctly hoists all fixture data to module level so each bench callback measures only normalizePem overhead.
packages/node/package.json Adds bench script (missing --run flag) and @codspeed/vitest-plugin dev dependency (uses bare semver instead of catalog: specifier).
packages/node/export/normalize.ts New module extracting normalizePem, normalizeHeaders, and normalizeBody from agent.ts; pure refactor with no behavior changes.
.github/workflows/codspeed.yaml New CI workflow for CodSpeed benchmarks; pinned SHA references, minimal permissions, OIDC auth, and --run flag correctly applied.
packages/node/export/agent.ts Removes the inline normalize functions and imports them from normalize.ts; no logic changes.
packages/node/vitest.bench.config.mts New vitest bench config wiring in the CodSpeed plugin and pointing at the bench test glob.
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
packages/node/tests/bench/normalize-body.bench.ts:6-26
Fixture data created inside bench callbacks inflates benchmark numbers. `Buffer.from()`, `new Uint8Array()`, `JSON.stringify()`, and especially `"x".repeat(1024)` all run on every iteration, so measured timings include allocation and serialization cost rather than just `normalizeBody` performance. `normalize-pem.bench.ts` correctly hoists fixtures to module level — the same pattern should be applied here.

```suggestion
const sampleBuffer = Buffer.from("request body content");
const sampleUint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const largeStringBody = JSON.stringify({ data: "x".repeat(1024) });

describe("normalizeBody", () => {
  bench("null input", () => {
    normalizeBody(null);
  });

  bench("string body", () => {
    normalizeBody('{"key":"value"}');
  });

  bench("Buffer body", () => {
    normalizeBody(sampleBuffer);
  });

  bench("Uint8Array body", () => {
    normalizeBody(sampleUint8Array);
  });

  bench("large string body", () => {
    normalizeBody(largeStringBody);
  });
});
```

### Issue 2 of 4
packages/node/tests/bench/normalize-headers.bench.ts:53-60
The `Map` is constructed inside the bench callback, so every iteration measures both `Map` construction and `normalizeHeaders`. Hoisting it to module level (like the fixtures in `normalize-pem.bench.ts`) isolates the function under test.

```suggestion
const sampleMap = new Map<string, string>([
  ["Content-Type", "application/json"],
  ["Accept", "text/html"],
  ["Authorization", "Bearer token123"],
]);

  bench("Map iterable", () => {
    normalizeHeaders(sampleMap);
  });
```

### Issue 3 of 4
packages/node/package.json:70
The local `bench` script omits `--run`, so `pnpm bench` starts vitest in watch mode and never exits. The CI workflow explicitly adds `--run`; the local script should match to avoid accidental hangs in automation and to produce a deterministic one-shot run when invoked manually.

```suggestion
    "bench": "vitest bench --config vitest.bench.config.mts --run"
```

### Issue 4 of 4
packages/node/package.json:77
Every other dev dependency in this file uses the `catalog:` specifier; `@codspeed/vitest-plugin` uses a bare semver range instead. Using `catalog:` keeps the version centralised in the workspace catalog and consistent with the project's conventions.

```suggestion
    "@codspeed/vitest-plugin": "catalog:",
```

Reviews (1): Last reviewed commit: "Add CodSpeed benchmarks for normalize ut..." | Re-trigger Greptile

Comment on lines +6 to +26
describe("normalizeBody", () => {
bench("null input", () => {
normalizeBody(null);
});

bench("string body", () => {
normalizeBody('{"key":"value"}');
});

bench("Buffer body", () => {
normalizeBody(Buffer.from("request body content"));
});

bench("Uint8Array body", () => {
normalizeBody(new Uint8Array([72, 101, 108, 108, 111]));
});

bench("large string body", () => {
normalizeBody(JSON.stringify({ data: "x".repeat(1024) }));
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Fixture data created inside bench callbacks inflates benchmark numbers. Buffer.from(), new Uint8Array(), JSON.stringify(), and especially "x".repeat(1024) all run on every iteration, so measured timings include allocation and serialization cost rather than just normalizeBody performance. normalize-pem.bench.ts correctly hoists fixtures to module level — the same pattern should be applied here.

Suggested change
describe("normalizeBody", () => {
bench("null input", () => {
normalizeBody(null);
});
bench("string body", () => {
normalizeBody('{"key":"value"}');
});
bench("Buffer body", () => {
normalizeBody(Buffer.from("request body content"));
});
bench("Uint8Array body", () => {
normalizeBody(new Uint8Array([72, 101, 108, 108, 111]));
});
bench("large string body", () => {
normalizeBody(JSON.stringify({ data: "x".repeat(1024) }));
});
});
const sampleBuffer = Buffer.from("request body content");
const sampleUint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const largeStringBody = JSON.stringify({ data: "x".repeat(1024) });
describe("normalizeBody", () => {
bench("null input", () => {
normalizeBody(null);
});
bench("string body", () => {
normalizeBody('{"key":"value"}');
});
bench("Buffer body", () => {
normalizeBody(sampleBuffer);
});
bench("Uint8Array body", () => {
normalizeBody(sampleUint8Array);
});
bench("large string body", () => {
normalizeBody(largeStringBody);
});
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/node/tests/bench/normalize-body.bench.ts
Line: 6-26

Comment:
Fixture data created inside bench callbacks inflates benchmark numbers. `Buffer.from()`, `new Uint8Array()`, `JSON.stringify()`, and especially `"x".repeat(1024)` all run on every iteration, so measured timings include allocation and serialization cost rather than just `normalizeBody` performance. `normalize-pem.bench.ts` correctly hoists fixtures to module level — the same pattern should be applied here.

```suggestion
const sampleBuffer = Buffer.from("request body content");
const sampleUint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const largeStringBody = JSON.stringify({ data: "x".repeat(1024) });

describe("normalizeBody", () => {
  bench("null input", () => {
    normalizeBody(null);
  });

  bench("string body", () => {
    normalizeBody('{"key":"value"}');
  });

  bench("Buffer body", () => {
    normalizeBody(sampleBuffer);
  });

  bench("Uint8Array body", () => {
    normalizeBody(sampleUint8Array);
  });

  bench("large string body", () => {
    normalizeBody(largeStringBody);
  });
});
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +53 to +60
bench("Map iterable", () => {
const headers = new Map<string, string>([
["Content-Type", "application/json"],
["Accept", "text/html"],
["Authorization", "Bearer token123"],
]);
normalizeHeaders(headers);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The Map is constructed inside the bench callback, so every iteration measures both Map construction and normalizeHeaders. Hoisting it to module level (like the fixtures in normalize-pem.bench.ts) isolates the function under test.

Suggested change
bench("Map iterable", () => {
const headers = new Map<string, string>([
["Content-Type", "application/json"],
["Accept", "text/html"],
["Authorization", "Bearer token123"],
]);
normalizeHeaders(headers);
});
const sampleMap = new Map<string, string>([
["Content-Type", "application/json"],
["Accept", "text/html"],
["Authorization", "Bearer token123"],
]);
bench("Map iterable", () => {
normalizeHeaders(sampleMap);
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/node/tests/bench/normalize-headers.bench.ts
Line: 53-60

Comment:
The `Map` is constructed inside the bench callback, so every iteration measures both `Map` construction and `normalizeHeaders`. Hoisting it to module level (like the fixtures in `normalize-pem.bench.ts`) isolates the function under test.

```suggestion
const sampleMap = new Map<string, string>([
  ["Content-Type", "application/json"],
  ["Accept", "text/html"],
  ["Authorization", "Bearer token123"],
]);

  bench("Map iterable", () => {
    normalizeHeaders(sampleMap);
  });
```

How can I resolve this? If you propose a fix, please make it concise.

"pack-addon": "slsa pack dist/node_reqwest-v{version}-{platform}-{arch}.node.gz",
"postinstall": "slsa wget"
"postinstall": "slsa wget",
"bench": "vitest bench --config vitest.bench.config.mts"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The local bench script omits --run, so pnpm bench starts vitest in watch mode and never exits. The CI workflow explicitly adds --run; the local script should match to avoid accidental hangs in automation and to produce a deterministic one-shot run when invoked manually.

Suggested change
"bench": "vitest bench --config vitest.bench.config.mts"
"bench": "vitest bench --config vitest.bench.config.mts --run"
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/node/package.json
Line: 70

Comment:
The local `bench` script omits `--run`, so `pnpm bench` starts vitest in watch mode and never exits. The CI workflow explicitly adds `--run`; the local script should match to avoid accidental hangs in automation and to produce a deterministic one-shot run when invoked manually.

```suggestion
    "bench": "vitest bench --config vitest.bench.config.mts --run"
```

How can I resolve this? If you propose a fix, please make it concise.

},
"devDependencies": {
"@codecov/vite-plugin": "catalog:",
"@codspeed/vitest-plugin": "^5.4.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Every other dev dependency in this file uses the catalog: specifier; @codspeed/vitest-plugin uses a bare semver range instead. Using catalog: keeps the version centralised in the workspace catalog and consistent with the project's conventions.

Suggested change
"@codspeed/vitest-plugin": "^5.4.0",
"@codspeed/vitest-plugin": "catalog:",
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/node/package.json
Line: 77

Comment:
Every other dev dependency in this file uses the `catalog:` specifier; `@codspeed/vitest-plugin` uses a bare semver range instead. Using `catalog:` keeps the version centralised in the workspace catalog and consistent with the project's conventions.

```suggestion
    "@codspeed/vitest-plugin": "catalog:",
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@vadimpiven vadimpiven closed this May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants