@aiwayds/pi-turbo

pi-turbo — optimized pi launcher with parallel extension loading and footer caching

Packages

Package details

package

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

English | 中文

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:

  1. 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).
  2. 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:

  1. Profile (first run): Load each extension individually, record per-extension timing (EMA)
  2. 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
  3. 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, where estimated_serial is 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-opt global command
  • Install the new pi-tb command
  • 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:

  1. Extension loader — DefaultResourceLoader.prototype.loadFinalExtensionSet is replaced with a targeted parallel loader that uses EMA timing profiles to identify I/O-bound extensions and load them concurrently.

  2. Footer render cache — SessionManager.prototype.getEntries and AgentSession.prototype.getContextUsage are 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 point
  • src/patch.js — monkey-patch orchestrator
  • src/footer-patch.js — footer render caching
  • src/targeted-loader.js — targeted parallel loading algorithm
  • src/timing.js — EMA timing recorder
  • src/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