mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
## Summary
- Extends the existing string fast path to support simple objects with
primitive values
- Achieves 2-241x performance improvements for postMessage with objects
- Maintains compatibility with existing code while significantly
reducing overhead
## Performance Results
### Bun (this PR)
```
postMessage({ prop: 11 chars string, ...9 more props }) - 648ns (was 1.36µs)
postMessage({ prop: 14 KB string, ...9 more props }) - 719ns (was 2.09µs)
postMessage({ prop: 3 MB string, ...9 more props }) - 1.26µs (was 168µs)
```
### Node.js v24.6.0 (for comparison)
```
postMessage({ prop: 11 chars string, ...9 more props }) - 1.19µs
postMessage({ prop: 14 KB string, ...9 more props }) - 2.69µs
postMessage({ prop: 3 MB string, ...9 more props }) - 304µs
```
## Implementation Details
The fast path activates when:
- Object is a plain object (ObjectType or FinalObjectType)
- Has no indexed properties
- All property values are primitives or strings
- No transfer list is involved
Properties are stored in a `SimpleInMemoryPropertyTableEntry` vector
that holds property names and values directly, avoiding the overhead of
full serialization.
## Test plan
- [x] Added tests for memory usage with simple objects
- [x] Added test for objects exceeding JSFinalObject::maxInlineCapacity
- [x] Created benchmark to verify performance improvements
- [x] Existing structured clone tests continue to pass
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
|
|
describe("Structured Clone Fast Path", () => {
|
|
test("structuredClone should work with empty object", () => {
|
|
const object = {};
|
|
const cloned = structuredClone(object);
|
|
expect(cloned).toStrictEqual({});
|
|
});
|
|
|
|
test("structuredClone should work with empty string", () => {
|
|
const string = "";
|
|
const cloned = structuredClone(string);
|
|
expect(cloned).toStrictEqual("");
|
|
});
|
|
|
|
const deOptimizations = [
|
|
{
|
|
get accessor() {
|
|
return 1;
|
|
},
|
|
},
|
|
Object.create(Object.prototype, {
|
|
data: {
|
|
value: 1,
|
|
writable: false,
|
|
configurable: false,
|
|
},
|
|
}),
|
|
Object.create(Object.prototype, {
|
|
data: {
|
|
value: 1,
|
|
writable: true,
|
|
configurable: false,
|
|
},
|
|
}),
|
|
Object.create(Object.prototype, {
|
|
data: {
|
|
get: () => 1,
|
|
configurable: true,
|
|
},
|
|
}),
|
|
Object.create(Object.prototype, {
|
|
data: {
|
|
set: () => {},
|
|
enumerable: true,
|
|
configurable: true,
|
|
},
|
|
}),
|
|
];
|
|
|
|
for (const deOptimization of deOptimizations) {
|
|
test("structuredCloneDeOptimization", () => {
|
|
structuredClone(deOptimization);
|
|
});
|
|
}
|
|
|
|
test("structuredClone should use a constant amount of memory for string inputs", () => {
|
|
const clones: Array<string> = [];
|
|
// Create a 512KB string to test fast path
|
|
const largeString = Buffer.alloc(512 * 1024, "a").toString();
|
|
for (let i = 0; i < 100; i++) {
|
|
clones.push(structuredClone(largeString));
|
|
}
|
|
Bun.gc(true);
|
|
const rss = process.memoryUsage.rss();
|
|
for (let i = 0; i < 10000; i++) {
|
|
clones.push(structuredClone(largeString));
|
|
}
|
|
Bun.gc(true);
|
|
const rss2 = process.memoryUsage.rss();
|
|
const delta = rss2 - rss;
|
|
expect(delta).toBeLessThan(1024 * 1024 * 8);
|
|
expect(clones.length).toBe(10000 + 100);
|
|
});
|
|
|
|
test("structuredClone should use a constant amount of memory for simple object inputs", () => {
|
|
// Create a 512KB string to test fast path
|
|
const largeValue = { property: Buffer.alloc(512 * 1024, "a").toString() };
|
|
for (let i = 0; i < 100; i++) {
|
|
structuredClone(largeValue);
|
|
}
|
|
Bun.gc(true);
|
|
const rss = process.memoryUsage.rss();
|
|
for (let i = 0; i < 10000; i++) {
|
|
structuredClone(largeValue);
|
|
}
|
|
Bun.gc(true);
|
|
const rss2 = process.memoryUsage.rss();
|
|
const delta = rss2 - rss;
|
|
expect(delta).toBeLessThan(1024 * 1024);
|
|
});
|
|
|
|
test("structuredClone on object with simple properties can exceed JSFinalObject::maxInlineCapacity", () => {
|
|
let largeValue = {};
|
|
for (let i = 0; i < 100; i++) {
|
|
largeValue["property" + i] = i;
|
|
}
|
|
|
|
for (let i = 0; i < 100; i++) {
|
|
expect(structuredClone(largeValue)).toStrictEqual(largeValue);
|
|
}
|
|
Bun.gc(true);
|
|
for (let i = 0; i < 100; i++) {
|
|
expect(structuredClone(largeValue)).toStrictEqual(largeValue);
|
|
}
|
|
});
|
|
});
|