Measured
Installed v0.5.0-rc3 via install.sh on macOS arm64 (tan-aarch64-apple-darwin, sha256 183b28dc310c1def8b05af1fb7d84eb7ffdd9e445ea2188b2b3ee982ba3f0669), /usr/bin/time -p tan --version, five consecutive runs:
13.25 s
19.35 s
19.35 s
18.58 s
19.74 s
For reference on the same host, same shell:
git --version 0.01 s
python3 -c pass 0.03 s
--version is the cheapest possible command — it does no I/O, no network, no SDK resolution. So this is pure process startup, and every subcommand pays it. There is no warm path: repeat runs are not faster, and the first run was actually the fastest of the five.
Cause
The binary is a PyInstaller onefile bundle. Confirmed:
strings on the executable contains pyi-runtime-tmpdir and PyInstaller's PKG-archive error string.
- While a command is running, a fresh extraction directory exists —
/var/folders/dh/…/T/_MEIYC8HRM, 15 entries — and it is gone afterwards.
So each invocation unpacks 14 MB of Python runtime plus shared libraries into a new temp directory, runs, and deletes it. On macOS every extracted .dylib/.so is unsigned once written (the parent's ad-hoc signature does not cover the extracted copies), so the OS inspects them individually on load. That is where the ~19 s goes.
Signature state, for completeness:
$ codesign -dv ~/.local/bin/tan
CodeDirectory v=20400 size=110693 flags=0x2(adhoc) hashes=3453+2 location=embedded
$ spctl -a -vv ~/.local/bin/tan
/Users/…/.local/bin/tan: rejected
$ xattr -l ~/.local/bin/tan
(empty — no com.apple.quarantine, so this is NOT the download-quarantine case)
Why this matters more than it looks
A 19-second --version is not a slow tool, it is an unusable one. Every place tan is invoked repeatedly pays it in series:
- the VS Code extension, which shells out per keystroke-ish interaction;
tan build over a multi-slice plan;
- CI jobs that call several subcommands (
clean-host.yml runs four in a row — a large part of that job's wall-clock is this, not the assertions);
- anyone exploring the CLI, where
--help on a few subcommands is a minute of waiting.
It also silently distorts every timing-based judgement made about the tool so far.
Directions
Roughly in order of effort/benefit — the first is the standard remedy for exactly this:
- Ship
--onedir instead of --onefile. Extraction happens once at install time rather than on every run; startup drops to normal Python-import cost. Costs: the asset becomes a tarball/zip rather than a single file, and install.sh must unpack it and place a launcher. This is what the size of the win argues for.
- Sign and notarize the macOS binaries. Ad-hoc → Developer ID + notarization would let the OS trust the extracted libraries. Helps macOS; does nothing for the re-extraction cost itself, and does not help Linux.
- Set a stable
pyi-runtime-tmpdir so the extraction directory is reused across runs rather than recreated. Cheapest change, but it caches security-sensitive extracted binaries in a fixed location — wants thinking about before adopting.
Not proposing a fix here — the packaging decision is scripts/build_binary.sh's and has release-asset consequences (install.sh, checksums.txt, the clean host (published asset) job all assume one file per target).
Reproduce
curl -fsSL https://raw.githubusercontent.com/alplabai/tan-cli/main/install.sh | sh -s -- --version v0.5.0-rc3
/usr/bin/time -p tan --version
Reported after a full command sweep of rc3 on a real host; timing was measured because a user reported the CLI felt "extremely slow", and the number turned out to be far worse than "felt".
Measured
Installed
v0.5.0-rc3viainstall.shon macOS arm64 (tan-aarch64-apple-darwin, sha256183b28dc310c1def8b05af1fb7d84eb7ffdd9e445ea2188b2b3ee982ba3f0669),/usr/bin/time -p tan --version, five consecutive runs:For reference on the same host, same shell:
--versionis the cheapest possible command — it does no I/O, no network, no SDK resolution. So this is pure process startup, and every subcommand pays it. There is no warm path: repeat runs are not faster, and the first run was actually the fastest of the five.Cause
The binary is a PyInstaller onefile bundle. Confirmed:
stringson the executable containspyi-runtime-tmpdirand PyInstaller's PKG-archive error string./var/folders/dh/…/T/_MEIYC8HRM, 15 entries — and it is gone afterwards.So each invocation unpacks 14 MB of Python runtime plus shared libraries into a new temp directory, runs, and deletes it. On macOS every extracted
.dylib/.sois unsigned once written (the parent's ad-hoc signature does not cover the extracted copies), so the OS inspects them individually on load. That is where the ~19 s goes.Signature state, for completeness:
Why this matters more than it looks
A 19-second
--versionis not a slow tool, it is an unusable one. Every placetanis invoked repeatedly pays it in series:tan buildover a multi-slice plan;clean-host.ymlruns four in a row — a large part of that job's wall-clock is this, not the assertions);--helpon a few subcommands is a minute of waiting.It also silently distorts every timing-based judgement made about the tool so far.
Directions
Roughly in order of effort/benefit — the first is the standard remedy for exactly this:
--onedirinstead of--onefile. Extraction happens once at install time rather than on every run; startup drops to normal Python-import cost. Costs: the asset becomes a tarball/zip rather than a single file, andinstall.shmust unpack it and place a launcher. This is what the size of the win argues for.pyi-runtime-tmpdirso the extraction directory is reused across runs rather than recreated. Cheapest change, but it caches security-sensitive extracted binaries in a fixed location — wants thinking about before adopting.Not proposing a fix here — the packaging decision is
scripts/build_binary.sh's and has release-asset consequences (install.sh,checksums.txt, theclean host (published asset)job all assume one file per target).Reproduce
Reported after a full command sweep of rc3 on a real host; timing was measured because a user reported the CLI felt "extremely slow", and the number turned out to be far worse than "felt".