Skip to content

perf(agent): ship the release binary uncompressed — UPX cost 19 MB RSS and 300 ms per exec#30

Merged
jaredLunde merged 1 commit into
mainfrom
jared/drop-upx-agent-release
Jul 14, 2026
Merged

perf(agent): ship the release binary uncompressed — UPX cost 19 MB RSS and 300 ms per exec#30
jaredLunde merged 1 commit into
mainfrom
jared/drop-upx-agent-release

Conversation

@jaredLunde

Copy link
Copy Markdown
Contributor

What

Drops the upx --best --lzma step from the agent's release workflow. The published beyond-ai-agent tarball now contains a plain static-musl binary.

Why

UPX makes a self-extracting binary: at exec it decompresses the whole image into anonymous memory and jumps in. That defeats demand paging. Normally the text segment is a clean, shared, file-backed mapping — the kernel pages in only the code you actually execute, drops those pages for free under pressure, and shares one copy across processes. After UPX it's private dirty anon: unevictable (swap-only), unshareable, and fully resident, because an LZMA stream is sequential so there's no way to inflate only the parts a given invocation calls.

Measured on the release binary (smaps_rollup, idle serve; 30 warm runs for startup):

plain UPX --best --lzma
idle RSS 12.1 MB 28.8 MB
↳ private dirty anon 2.6 MB 26.6 MB
↳ clean file-backed 7.4 MB 0.09 MB
--version (median) 4.5 ms 305 ms

Of the 24.5 MB binary, only 7.4 MB was ever resident under demand paging — ~17 MB is code (OAuth flows, MCP transports, unused provider dialects) that never ran and never cost RAM. UPX materializes all of it.

And the payoff was smaller than the raw binary sizes suggest. The asset is already gzipped by tar -czf, so gzip was doing most of UPX's job for free:

release asset size
plain, tar -czf 9.8 MB
UPX, tar -czf 6.8 MB

So UPX bought 3 MB of one-time download in exchange for ~19 MB of RSS and ~300 ms per exec.

Neither cost is worth it for how this binary is actually deployed. As a session-multiplexing daemon, that 19 MB is ~2.5% of a 754 MB node, held permanently, before it serves a single session — spending the RAM budget on compressed-binary overhead is backwards when the whole point of the architecture is packing sessions into one process. And the same binary ships one-shot CLI subcommands (run, tools, login), which would eat 300 ms of LZMA before doing anything at all — including on --help.

The step's comment claimed "a trivial one-time self-extract at exec." It's trivial in neither dimension; it was true about CPU only in the sense that nobody had timed it, and it said nothing about memory. Replaced with a note recording the measurements, so the step doesn't get re-added later to shave 3 MB off the asset.

Notes

  • Measurements are from the glibc release binary (what's buildable locally); CI ships static musl. Absolute numbers will shift a little, but ~24 MB of LZMA has to be inflated either way — the order of magnitude holds.
  • No source change; workflow-only. Verified the YAML parses and the remaining steps are unchanged.

🤖 Generated with Claude Code

…S and 300 ms per exec

`upx --best --lzma` self-extracts the entire image into anonymous memory at exec, which defeats
demand paging. The text segment stops being a clean, shared, file-backed mapping and becomes private
dirty anon: unevictable (swap-only) and unshareable between processes. It also has to inflate all
~24 MB before `main` runs — an LZMA stream is sequential, so there is no paging in only the code a
given invocation actually calls.

Measured on the release binary:

  idle RSS      12.1 MB → 28.8 MB   (+19 MB; 26.6 MB of it private dirty anon vs 2.6 MB before)
  `--version`      4.5 ms →  305 ms  (~68x, 30 runs each, warm cache)

And the payoff was smaller than it looked: the asset is already gzipped by `tar -czf`, so dropping
UPX takes it from 6.8 MB to 9.8 MB — a 3 MB one-time download, not the ~18 MB the raw binary sizes
suggest.

3 MB off a download does not justify 19 MB of RAM in a session-multiplexing daemon (~2.5% of a
754 MB node, permanently, before it serves a single session), nor 300 ms on every invocation of a
binary that also ships one-shot CLI subcommands (`run`, `tools`, `login`).

The old step's comment — "a trivial one-time self-extract at exec" — was true about CPU only in the
sense that nobody had timed it, and said nothing about memory. Left a note in its place so the step
doesn't get re-added to shave the asset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jaredLunde
jaredLunde merged commit d908de5 into main Jul 14, 2026
1 check passed
@jaredLunde
jaredLunde deleted the jared/drop-upx-agent-release branch July 14, 2026 05:42
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.

1 participant