Skip to content

Refactor: zero-copy pipeline — Bytes as universal currency, Framer/Aggregator model #303

Closed

Description

@strawgate

Summary

Redesign the read → parse → scan pipeline to eliminate unnecessary copies. Based on research of Vector, Fluent Bit, simdjson, and simd-json architectures.

Current status

The streaming StructuralIndex prototype is working (structural.rs on feat/structural-index-continued). Key sub-issues:

Target data flow

FileReader reads into BytesMut → freeze() → Bytes (refcounted)

For each 64-byte block:
  Stage 1: wide::u8x16 SIMD detect 10 chars → RawBlockMasks (stack)
  Stage 2: StreamingClassifier → ProcessedBlock (escape-aware, string-masked)
  Stage 3: Consume bitmasks immediately:
    - Newline bitmask → line ranges (framer)
    - Space bitmask → CRI field ranges
    - Quote+comma+colon → JSON field extraction (scanner)
    - Results → FieldSink → Arrow builders → RecordBatch

Cross-block state: 2 u64 values. Bitmask storage: zero heap.

Key changes from original design

  • No stored bitmask vectors — simdjson/simd-json never store them
  • Portable SIMD via wide crate — no hand-rolled platform backends
  • Streaming per-block — not two-pass (SIMD then scanner)
  • Sequential scanner — trailing_zeros iteration, not random-access ChunkIndex

References

  • dev-docs/research/structural-index-research.md
  • dev-docs/research/wide-crate-evaluation.md
  • dev-docs/DECISIONS.md (two-stage architecture, unified StructuralIndex)

Activity

  1. strawgate commented on Mar 31, 2026

    @strawgate
    OwnerAuthor

    Architecture update: unified SIMD StructuralIndex

    Benchmark results from #313 change the framing architecture. Instead of separate passes (memchr for newlines + ChunkIndex for quotes/backslashes), we'll extend ChunkIndex into a StructuralIndex that detects all structural characters in one SIMD pass.

    Benchmark (NEON, ~760KB NDJSON):

    • Current 2-char SIMD: 63µs / 12 GiB/s
    • Unified 5-char SIMD: 141µs / 5.4 GiB/s
    • Unified 9-char SIMD: 256µs / 3.0 GiB/s (11x headroom over target)

    Scaling is linear at ~28µs per character. The unified approach is viable.

    Data flow changes:

    Reader → Bytes → StructuralIndex::new(buf) [ONE SIMD pass]
      → Framer consumes newline bitmask → line ranges
      → CRI consumes space bitmask → field ranges  
      → Scanner consumes quote/backslash/comma/colon bitmasks
      → All zero-copy, all O(1) lookups into pre-computed bitmasks
    

    This eliminates the separate memchr newline pass and makes CRI field extraction and future CSV parsing essentially free — the bitmasks are already computed.

  2. strawgate commented on Mar 31, 2026

    @strawgate
    OwnerAuthor

    Implemented in PR #399. Pipeline now bypasses format.rs — raw bytes go directly to scanner. CRI uses parse_cri_line + CriAggregator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestperformancePerformance optimization

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions