Skip to content

ghc-fastboot

Zero-cost serialization for GHC Haskell. Freeze any closure graph — constructors, thunks, functions, partial applications — into a snapshot and thaw it back in microseconds.

What it does

main = do
    let config  = frozen ".cache/config"  loadConfig
        index   = frozen ".cache/index"   buildIndex
        grammar = frozen ".cache/grammar" parseGrammar
    runApp config index grammar

First run: evaluates each expression, walks the closure graph, serializes to disk. Every subsequent run: mmap + mremap the data directly into the GHC heap. No parsing, no deserialization, no allocation. The closures are just there.

Performance

1M-entry Vector Text benchmark (91MB closure data):

Approach Startup time Speedup
Build from scratch 618ms 1x
Thaw from file (mmap) 2.6ms 238x
Thaw from ELF section (mremap) 2.6ms 238x

Data pages are demand-paged: only pages you actually access incur a fault (~40ns each). A CLI tool that touches 100 entries out of 1M pays for 100 entries, not 1M.

How it differs from GHC Compact Regions

Compact Regions ghc-fastboot
Thunks No (NF only) Yes
Functions / PAPs No Yes
Closures over free vars No Yes
Cross-binary migration No Yes (symbol table + dlsym)
Layout Compacted, contiguous Compacted, contiguous
GC integration Built-in Manual bdescr + StablePtr
Requires GHC patch No No

The closure walker handles all GHC closure types via info table traversal — the same mechanism the GC uses.

Haskell API

-- Freeze/thaw without binary compatibility checks.
-- First run: evaluates action, serializes result to path.
-- Subsequent runs: thaws from snapshot (or ELF section if embedded).
frozen :: FilePath -> IO a -> a

-- Same as frozen (no binary hash verification yet).
unsafeFrozen :: FilePath -> IO a -> a

frozen uses unsafePerformIO. The snapshot path is the fallback — if the binary has an embedded snapshot (ELF section), it's used first with zero file I/O.

Building

# Default build (library + benchmarks)
nix build

# Embedded binary (three-phase: build → snapshot → objcopy)
nix build .#bench-embedded

# Development
nix develop
cabal build

Project Structure

ghc-fastboot/
├── lib/FastBoot.hs              # Haskell API: frozen, unsafeFrozen
├── cbits/
   ├── fastboot.h               # Snapshot format structs, constants
   ├── freeze.c                 # Closure graph walk + serialize
   ├── thaw.c                   # mmap/mremap restore + GC setup
   ├── closure_walk.h           # Info table-driven closure walker
   ├── relocate.c               # ASLR relocation (v1)
   ├── embed.c                  # Binary embedding
   ├── embed_section.S          # ELF section placeholder
   └── fastboot.ld              # Linker script (section after .bss)
├── bench/
   ├── mini-fzf-frozen/         # 1M-entry Vector Text benchmark
   ├── mini-fzf/                # Unfrozen baseline
   └── minimal/                 # RTS overhead baseline
├── docs/                        # Documentation (this site)
└── flake.nix                    # Nix build with three-phase pipeline