learned_index_lab 0.3.0

High-performance Piecewise Geometric Model (PGM) learned index implementation in pure Rust with strict epsilon error guarantees.
Documentation

Learned Index: Piecewise Geometric Model (PGM-Index) in Rust

CI Crates.io Docs.rs License: Apache-2.0 Ko-fi GitHub Sponsors

A clean, zero-dependency Rust implementation and comparative benchmark suite of Piecewise Geometric Model (PGM) Learned Index Structures.

This repository evaluates mathematical function approximations of Empirical Cumulative Distribution Functions (CDFs) against standard pointer trees (BTreeMap), contiguous binary search (slice::binary_search), and cache-conscious breadth-first search (EytzingerSearch).

[!NOTE] Academic Attribution: Learned Indexes were originated by Kraska et al. (MIT/Google, SIGMOD 2018). The PGM-index was invented and proven by Paolo Ferragina and Giorgio Vinciguerra (University of Pisa, PVLDB 2020). The Eytzinger layout was formalized by Paul-Virak Khuong and Pat Morin (ACM JEA 2017). This repository is an independent, clean-room implementation in pure safe Rust.


Architectural Comparison (4 Search Paradigms)

Dimension Standard BTreeMap Flat slice::binary_search EytzingerSearch (Breadth-First) PGM-Index (Learned Model)
Data Layout Heap node hierarchy Contiguous sorted slice 1-based complete binary tree array Compact array of $(a, b, k_0)$ segments
Lookup Mechanism 4–6 pointer hops across heap $\sim 20$ binary jumps across RAM Branchless arithmetic traversal (k = 2k + bit) In-register ALU prediction + bounded local scan
Cache Locality Poor (scattered heap nodes) Medium (strided binary jumps) High (upper tree levels in L1/L2) Highest (segments fit entirely in L1/L2)
Index Overhead $\sim 45.78\text{ MB}$ per $10^6$ keys $0\text{ KB}$ (in-situ data only) $7.63\text{ MB}$ per $10^6$ keys (duplicate layout) 3.1 KB – 48.1 KB (-99.9% vs BTree)
Primary Strength Dynamic in-place mutations Zero extra memory Branchless search, cache friendly Sub-microsecond lookups, extreme compression

Benchmark Summary (v0.3.0 Consolidated Results)

Measured on x86_64 hardware in --release mode. Consolidated average across all 3 workload distributions (30 independent runs per target structure with a 32MB L1-L3 CPU cache eviction barrier between phases):

Cible / Target Build Time Hit Latency Miss Latency Lower Bound Index Memory Moyenne Totale Latence Speedup vs BTree
BTreeMap 84.12 ms 314.4 ns 331.4 ns 350.0 ns 45.78 MB 331.9 ns 1.00x
Flat Slice 0.00 ms 282.7 ns 272.9 ns 275.3 ns 0.00 KB 277.0 ns 1.20x
Eytzinger 6.30 ms 163.7 ns 160.9 ns 169.2 ns 7.63 MB 164.6 ns 2.02x
PGM-Index 6.82 ms 153.0 ns 154.1 ns 208.6 ns 21.1 KB 171.9 ns 1.93x
MOYENNE GLOBALE 24.31 ms 228.4 ns 229.8 ns 250.8 ns 13.36 MB 236.3 ns —

Detailed breakdown per distribution (Dense Time-Series, Uniform Sparse, and Heavy-Tailed Lognormal) is available in docs/benchmarks/statistical_benchmarks.md.


Roadmap & Architecture Evolution

  • v0.1.0 – v0.2.0 (Completed):
    • Streaming PLA index builder ($O(N)$ single-pass construction).
    • Exact point queries (find), predecessor search (lower_bound), and zero-copy slice scans (range).
    • Baseline comparison against BTreeMap and slice::binary_search.
    • Multi-platform CI matrix (Ubuntu, Windows, macOS).
  • v0.3.0 (Current Release):
    • Cache-conscious Eytzinger array layout baseline (EytzingerSearch) with branchless search.
    • Multi-target isolated benchmark harness with --target selector and 32MB CPU cache eviction barrier.
    • Single-threaded isolated test execution (--test-threads=1).
    • Per-target summary matrix with combined total average metrics.
    • Documentation modularization into dedicated docs/ topic guides.
  • v0.4.0 (Planned Milestone):
    • SOSD (Search on Sorted Data) real-world dataset ingestion (OpenStreetMap, Books, Wikipedia).
    • Skewed Zipfian query distribution generator.
    • Multi-level recursive PGM indexing for massive datasets ($S > 10^5$).
  • v0.5.0 (Future Monorepo Architecture):
    • Cargo Workspace structure within this repository:
      • crates/learned-index: Pure, zero-dependency production storage engine core.
      • crates/index-benchmarks: Exhaustive comparative testbed.
    • Dynamic LSM extensions (buffered writes with asynchronous background segment compaction).

Quickstart & Code Example

Add learned_index_lab to your Cargo.toml:

[dependencies]

learned_index_lab = "0.3.0"

use learned_index_lab::{EytzingerSearch, PgmIndex};

fn main() {
    // 1. Prepare sorted dataset
    let data: Vec<u64> = vec![
        100, 105, 110, 115, 120, 250, 500, 1000, 1500, 2000, 5000, 10000
    ];

    // 2. Build PGM index with error bound epsilon (e.g. 16 or 32)
    let pgm = PgmIndex::build(data.clone(), 16);

    // 3. Exact point lookup (O(log S + log epsilon))
    assert_eq!(pgm.find(1500), Some(8));

    // 4. Predecessor / Lower Bound search (first element >= target)
    let lb_idx = pgm.lower_bound(1200);
    println!("Lower bound for 1200 is index {} (value: {})", lb_idx, pgm.data[lb_idx]);

    // 5. Zero-copy Range Query (slice scan)
    let slice = pgm.range(110..=2000);
    println!("Keys in range [110, 2000]: {:?}", slice);

    // 6. Cache-conscious Eytzinger layout baseline
    let eyt = EytzingerSearch::build(&data);
    assert!(eyt.contains(1500));
}

Reproducibility & Verification

The v0.3.0 test suite and benchmark harness support strict single-threaded isolation and modular target selection:

# Clone the repository

git clone https://github.com/gilnett/learned_index_lab.git

cd learned_index_lab


# Run the unit test suite with thread isolation

cargo test --release -- --test-threads=1


# Execute the full 10-run release benchmark across all 4 targets

cargo run --release


# Run isolated benchmarks on specific targets

cargo run --release -- --target eytzinger,pgm --runs 10

cargo run --release -- --target pgm --suite uniform


Documentation Index


References

Technical Articles & Deep Dives

Seminal Research Papers

  • Kraska et al. (2018): "The Case for Learned Index Structures" (ACM SIGMOD 2018) – PDF (arXiv) · arXiv:1712.01208. Seminal paper proving that sorted indexes can be modeled as cumulative distribution function (CDF) regressions.
  • Ferragina & Vinciguerra (2020): "The PGM-index: a fully-dynamic compressed learned index with provable worst-case bounds" (PVLDB 2020) – PDF (PVLDB) · PDF (arXiv) · arXiv:1910.06169. Establishes optimal piecewise linear approximation (PLA) under bounded error corridors $\epsilon$.
  • Khuong & Morin (2017): "Array Layouts for Comparison-Based Searching" (ACM JEA 2017) – PDF (arXiv) · arXiv:1509.05053. Formalizes the cache-conscious Eytzinger array layout and branchless level-order search.

Reference Codebases & Implementations

Industry Storage Engines & Analytical Systems


License

Licensed under the Apache License, Version 2.0 (LICENSE or http://www.apache.org/licenses/LICENSE-2.0).