mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
perf(Response.json): use JSC's FastStringifier by passing undefined for space (#25717)
## Summary
- Fix performance regression where `Response.json()` was 2-3x slower
than `JSON.stringify() + new Response()`
- Root cause: The existing code called `JSC::JSONStringify` with
`indent=0`, which internally passes `jsNumber(0)` as the space
parameter. This bypasses WebKit's FastStringifier optimization.
- Fix: Add a new `jsonStringifyFast` binding that passes `jsUndefined()`
for the space parameter, triggering JSC's FastStringifier
(SIMD-optimized) code path.
## Root Cause Analysis
In WebKit's `JSONObject.cpp`, the `stringify()` function has this logic:
```cpp
static NEVER_INLINE String stringify(JSGlobalObject& globalObject, JSValue value, JSValue replacer, JSValue space)
{
// ...
if (String result = FastStringifier<Latin1Character, BufferMode::StaticBuffer>::stringify(globalObject, value, replacer, space, failureReason); !result.isNull())
return result;
// Falls back to slow Stringifier...
}
```
And `FastStringifier::stringify()` checks:
```cpp
if (!space.isUndefined()) {
logOutcome("space"_s);
return { }; // Bail out to slow path
}
```
So when we called `JSONStringify(globalObject, value, (unsigned)0)`, it
converted to `jsNumber(0)` which is NOT `undefined`, causing
FastStringifier to bail out.
## Performance Results
### Before (3.5x slower than manual approach)
```
Response.json(): 2415ms
JSON.stringify() + Response(): 689ms
Ratio: 3.50x
```
### After (parity with manual approach)
```
Response.json(): ~700ms
JSON.stringify() + Response(): ~700ms
Ratio: ~1.09x
```
## Test plan
- [x] Existing `Response.json()` tests pass
(`test/regression/issue/21257.test.ts`)
- [x] Response tests pass (`test/js/web/fetch/response.test.ts`)
- [x] Manual verification that output is correct for various JSON inputs
Fixes #25693
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Sosuke Suzuki <sosuke@bun.com>
This commit is contained in:
@@ -112,12 +112,40 @@ const obj = {
|
||||
},
|
||||
};
|
||||
|
||||
bench("Response.json(obj)", async () => {
|
||||
const smallObj = { id: 1, name: "test" };
|
||||
|
||||
const arrayObj = {
|
||||
items: Array.from({ length: 100 }, (_, i) => ({ id: i, value: `item-${i}` })),
|
||||
};
|
||||
|
||||
bench("Response.json(obj)", () => {
|
||||
return Response.json(obj);
|
||||
});
|
||||
|
||||
bench("Response.json(obj).json()", async () => {
|
||||
return await Response.json(obj).json();
|
||||
bench("new Response(JSON.stringify(obj))", () => {
|
||||
return new Response(JSON.stringify(obj), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
bench("Response.json(smallObj)", () => {
|
||||
return Response.json(smallObj);
|
||||
});
|
||||
|
||||
bench("new Response(JSON.stringify(smallObj))", () => {
|
||||
return new Response(JSON.stringify(smallObj), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
bench("Response.json(arrayObj)", () => {
|
||||
return Response.json(arrayObj);
|
||||
});
|
||||
|
||||
bench("new Response(JSON.stringify(arrayObj))", () => {
|
||||
return new Response(JSON.stringify(arrayObj), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
Reference in New Issue
Block a user