@aiwayds/pi-turbo
pi-turbo — optimized pi launcher with parallel extension loading and footer caching
Package details
Install @aiwayds/pi-turbo from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@aiwayds/pi-turbo- Package
@aiwayds/pi-turbo- Version
0.2.1- Published
- Oct 18, 2026
- Downloads
- 247/mo · 18/wk
- Author
- aiwayds
- License
- MIT
- Types
- package
- Size
- 46.4 KB
- Dependencies
- 0 dependencies · 1 peer
Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-turbo
Performance wrapper for pi — two runtime monkey-patches, zero modification to pi itself.
- Startup: parallel extension loading (33% faster cold start)
- Runtime: footer render caching (eliminates Enter-key lag in long conversations)
Why pi-turbo
This repo exists to fix two pains in daily pi use:
- Extension loading is slow and fully serial. pi loads extensions one by one — no async — so every cold start means seconds of staring at a blank terminal. pi-turbo profiles per-extension timing, then loads I/O-bound extensions (e.g. MCP handshakes) in the background while the others load, overlapping I/O wait with CPU work (~30% faster in A/B runs).
- Long sessions start to feel like a hang. Past ~100 messages, pressing Enter shows latency that grows with the session. One major cause: the footer re-scans all session entries on every render. pi-turbo caches those results (
getEntries()/getContextUsage()) by entry count, so repeated renders hit an O(1) cache instead of paying the O(n) scan again — cutting the per-render footer cost that compounds in long sessions.
Problems it solves
1. Slow startup (extension loading)
Symptom: pi takes 4-5 seconds to start. You watch a blank terminal while extensions load one by one.
Cause: pi loads all extensions serially. Some (like MCP handshakes) are I/O-bound and waste CPU idle time.
Fix: pi-turbo patches DefaultResourceLoader.loadFinalExtensionSet to:
- Profile (first run): Load each extension individually, record per-extension timing (EMA)
- Optimize (subsequent runs): Identify I/O-bound extensions (EMA > 1000ms, e.g. MCP handshakes) and load them in background while CPU-bound extensions load serially
- Overlap: I/O wait and CPU work run concurrently → total = max(background, serial)
| Mode | Extension Loading | Speedup |
|---|---|---|
| Serial (pi default) | ~4660ms | — |
| Targeted parallel | ~3125ms | 33% |
2. Enter key lag (footer O(n) scan)
Symptom: After 100+ messages, pressing Enter has a visible delay. The longer the conversation, the worse it gets.
Cause: pi's footer re-scans ALL session entries (O(n)) on every render to compute token usage stats, plus getContextUsage() does tree walks and JSON.stringify of every message.
Fix: pi-turbo patches SessionManager.getEntries() and AgentSession.getContextUsage() with smart caching:
getEntries(): returns a cached array, invalidated only when new entries are appended (O(1) vs O(n) filter + allocation)getContextUsage(): cached per entry-count, avoiding repeated tree walks and token estimation (O(1) vs O(n) + O(content size))
Impact on pi
Zero. pi-turbo never touches pi's source code, node_modules, or config files.
- All patches are runtime monkey-patches on prototype methods
- Every patch is wrapped in try-catch — any error falls back to pi's original behavior
- Kill-switches let you disable any optimization independently
- Uninstall removes all traces; pi works exactly as before
The only observable difference: pi starts faster and the footer doesn't lag.
Evidence
1. Startup: A/B benchmark
20 extensions installed, 3 runs per mode:
Serial baseline (PI_TURBO_SERIAL=1):
5140ms
5000ms
9251ms
Parallel (default):
⚡ pi-turbo: 20 exts in 6507ms (serial ~9432ms, saved 31%, 1 chunk)
⚡ pi-turbo: 20 exts in 8387ms (serial ~11489ms, saved 27%, 1 chunk)
⚡ pi-turbo: 20 exts in 9796ms (serial ~13797ms, saved 29%, 1 chunk)
The "saved %" is computed per-run:
(estimated_serial − actual_parallel) / estimated_serial, whereestimated_serialis the sum of individually-profiled per-extension EMA times. Absolute times vary with system load; the ratio is the stable metric.
2. Startup: EMA profiling
Example pi-tb --status output:
Top extensions by EMA:
extensions/index.ts 2527ms ← lean-ctx MCP bridge (I/O bound)
dist/index.js 309ms
(remaining 18 extensions <100ms each)
The I/O-bound extension (2527ms) is loaded in a background chunk while CPU-bound extensions load serially, overlapping I/O wait with CPU work.
3. Footer: O(n) scan in pi source
Code-level evidence (not benchmarkable). The lag originates from three hot paths:
// footer.js:80 — every render frame
sessionManager.getEntries()
// session-manager.js:980 — O(n) filter + new array allocation on every call
this.fileEntries.filter(e => e.type !== "session")
// agent-session.js:2534 — O(n) tree walk + JSON.stringify of all messages
getContextUsage() → getBranch() + estimateContextTokens()
fileEntries is append-only, so n grows with conversation length. After 100+ messages, every Enter key triggers O(n) scan + O(content) serialization → visible lag.
pi-turbo's fix: WeakMap cache keyed on fileEntries.length (append-only invariant). Cache hit = O(1). Cache miss (new entry appended) = one O(n) pass, then cached.
Install
npm i -g @aiwayds/pi-turbo
Or from source:
git clone https://github.com/fan56/pi-turbo.git
cd pi-turbo
./install.sh
Upgrading from pi-opt
If you previously installed pi-opt, just re-run ./install.sh. It will:
- Remove the old
pi-optglobal command - Install the new
pi-tbcommand - Migrate your timing data from
~/.pi-opt/to~/.pi-turbo/
Usage
pi-tb # launch pi with all optimizations
pi-tb --status # show per-extension timing statistics
PI_TURBO_SERIAL=1 pi-tb # disable startup optimization (A/B baseline)
PI_TURBO_NO_FOOTER=1 pi-tb # disable footer caching only
Startup logging
pi-turbo prints informational startup logs to stderr. Control them with PI_TURBO_STATS:
PI_TURBO_STATS=0— silence all informational startup logs (error and fallback logs are still shown). The one-line startup summary is on by default.PI_TURBO_STATS=1— additionally show detailed stats such as chunk details.PI_TURBO_SERIAL=1— disable parallel loading (for A/B baseline tests).
Uninstall
./uninstall.sh
How it works
pi-turbo is a thin wrapper that monkey-patches two things at runtime:
Extension loader —
DefaultResourceLoader.prototype.loadFinalExtensionSetis replaced with a targeted parallel loader that uses EMA timing profiles to identify I/O-bound extensions and load them concurrently.Footer render cache —
SessionManager.prototype.getEntriesandAgentSession.prototype.getContextUsageare wrapped with entry-count-keyed caches, eliminating per-render O(n) scans.
Both patches are fail-safe: any error falls back to pi's original behavior.
Files
bin/pi-tb.js— CLI entry pointsrc/patch.js— monkey-patch orchestratorsrc/footer-patch.js— footer render cachingsrc/targeted-loader.js— targeted parallel loading algorithmsrc/timing.js— EMA timing recordersrc/config.js— paths and constants~/.pi-turbo/per-ext-timings.json— per-extension timing data (auto-created)
Environment variables
| Variable | Default | Effect |
|---|---|---|
PI_TURBO_SERIAL=1 |
— | Kill-switch: force serial extension loading |
PI_TURBO_OFF=1 |
— | Skip all patches entirely |
PI_TURBO_CHUNK |
4 | Extensions per parallel chunk |
PI_TURBO_DEBUG=1 |
— | Verbose loading logs |
PI_TURBO_PROFILE=1 |
— | Force re-profiling (ignore existing EMA data) |
PI_TURBO_NO_FOOTER=1 |
— | Disable footer caching only |
PI_TURBO_STATS |
summary on | =0 silences all informational startup logs (errors/fallbacks still shown); =1 adds detailed stats |
Requirements
- Node.js >= 22.19.0
- pi (
@earendil-works/pi-coding-agent) >= 0.50.0