perf(python3): avoid quadratic copying on file writes - #433
josephbajor wants to merge 1 commit into
Conversation
|
@josephbajor is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
🤖 auto-maintain reviewAutomated, advisory triage for
Review panel: 🟢 low highest severity just-bash maintainer code review: 🟢 low
General code review: 🟢 low
Adversarial security: 🟢 low
Adversarial security (second opinion): 🟢 low
Standard Bash and host portability: 🟢 low
Posted by auto-maintain. This automated code review is advisory; a human maintainer makes the call. |
Repeated small writes from Python currently allocate a new
Uint8Arrayand copy the entire previous file on every extending HOSTFS write. Sequential output therefore incurs quadratic copying, including when Python's own buffering coalesces writes from serializers such asjson.dump.Grow the buffer geometrically and track logical file length separately. For 8,192 consecutive 1 KiB extensions, the previous algorithm copies about 32 GiB of existing contents; doubling capacity reduces that to about 8 MiB. The benchmark below measures the actual Python command and verifies every file it writes.
Implementation
The runtime change is in
packages/just-bash/src/commands/python3/worker.ts:hostLengthseparately from allocatedhostContent.length.maxFileSizeguard.SEEK_END; send only logical contents across the bridge on close.seek(100); write(b'')enlarged a five-byte file to 100 bytes. Native Python leaves it at five bytes.Buffer capacity can approach twice the logical file length, bounded by the existing file-size limit; the previous allocation is also temporarily live during growth. Existing bridge capacity and write-back-on-close behavior are retained. This changes TypeScript source and adds a changeset and tests. It applies independently on upstream
062ce005c0a7676163852fb6f0c8590cbdaa1d45(3.4.2).Reproduce and compare
Use Node 22 or 24 and the repository's pinned pnpm version. Save the following temporary benchmark as
/tmp/just-bash-write-benchmark.mjs:Benchmark script
Then run from a fresh checkout:
The benchmark makes repeated 1 KiB
file.write()calls, with both unbuffered files and an 8 KiB Python buffer. It writes to the virtual filesystem, reads back every completed file, and asserts exact byte equality. Timing includes the writes and close, and excludes interpreter startup, opening the file, and verification. Each size/buffering pair gets one warmup followed by five measured runs.Measured locally on Node 24.10.0, macOS 26.6.2, arm64:
The worker deliberately limits its clock to millisecond precision, so small timings are coarse and machine/load dependent. These measurements describe the file-write phase; interpreter startup still contributes to total command latency. Performance measurements live in the standalone benchmark to avoid machine-dependent timing assertions in CI.
Validation
readintoat EOF, overwrites, append after seeking and reopening, sparse zero-filled gaps, empty writes, truncating opens, an odd 1,025-byte file limit, and flushing an 8 MiB file whose allocated capacity exceeds the bridge limit. All nine pass on Node 22.21.0 and 24.10.0. The empty-write test fails on upstream with a reported EOF of 100 instead of 5.pnpm lint:fix,pnpm lint,pnpm knip, andpnpm check:worker-syncpass.cross-fs-security.test.ts, plus four special-mode-bit assertions inread-write-fs.hard-links.test.ts,read-write-fs.mode-and-copy.test.ts, andread-write-fs.test.ts.