Skip to content

lrsaturnino#97

Open
lrsaturnino wants to merge 4 commits intotempestphp:mainfrom
lrsaturnino:main
Open

lrsaturnino#97
lrsaturnino wants to merge 4 commits intotempestphp:mainfrom
lrsaturnino:main

Conversation

@lrsaturnino
Copy link

@lrsaturnino lrsaturnino commented Feb 26, 2026

Approach

Flat-integer-array architecture with discover phase, 4 parallel workers,
in-buffer strpos() scanning, and binary IPC.

Key optimizations

  1. Discover phase: Scans first 1 MB to map all unique paths and dates
    to sequential integer IDs in first-encounter order
  2. Flat integer array: counts[pathId * dateCount + dateId] replaces
    nested associative arrays — eliminates hash-table overhead in the hot path
  3. 4 workers: pcntl_fork() with newline-aligned file segment splitting
  4. In-buffer strpos() scanning: Extracts path/date directly from the
    read buffer without explode("\n") intermediate string arrays
  5. pack('V') binary IPC*: Unsigned 32-bit LE encoding (~1.9 MB per child
    vs ~50-140 MB with serialize) via temp files
  6. Manual JSON streaming: Writes output matching json_encode(JSON_PRETTY_PRINT)
    byte-for-byte, including \/ escaping
  7. /dev/shm preference: RAM-backed temp dir with sys_get_temp_dir() fallback

Additional optimizations

  • O(1) comma detection: strlen($line) - 26 exploits the fixed 25-char
    ISO 8601 timestamp suffix
  • gc_disable(): No circular references in the workload
  • Minimum line length guard: lineLen > 45 skips malformed/empty lines

Correctness

  • Prefix offset 19 (not 20) preserves leading / in path keys
  • No outer ksort() — preserves first-encounter insertion order
  • Inner dates sorted chronologically via ksort()
  • Manual JSON output matches json_encode(JSON_PRETTY_PRINT) byte-for-byte
  • data:validate passes

Two-worker pcntl_fork architecture that splits CSV at newline-aligned
midpoint. Each worker uses 1MB buffered fread with explode line splitting
and O(1) comma detection via strlen-26 (exploiting fixed 25-char timestamp
suffix). Results merged via igbinary/serialize IPC through temp files.
Inner dates sorted with ksort; outer key order preserved (insertion order).
gc_disable eliminates unnecessary cyclic GC overhead.
@github-actions
Copy link

Benchmarks finished with success.

Workflow run: https://github.com/tempestphp/100-million-row-challenge/actions/runs/22422666557

…trpos scanning, binary IPC, and manual JSON output

Replace associative-array architecture with flat integer indexing for the hot
path. A discover phase scans the first 1 MB to map paths and dates to integer
IDs in first-encounter order. processSegment now uses in-buffer strpos()
scanning instead of explode("\n"), and counts into a flat array indexed by
pathId * dateCount + dateId. Workers communicate via pack('V*') binary IPC
(~1.9 MB per child vs ~50-140 MB with serialize). Manual JSON streaming
output matches json_encode(JSON_PRETTY_PRINT) byte-for-byte. Prefers
/dev/shm for temp files with sys_get_temp_dir() fallback. Parallelism
increased from 2 to 4 workers. Local macOS benchmark: 8.55s for 100M rows.
@lrsaturnino
Copy link
Author

@brendt @xHeaven Updated my submission with significant optimizations — could you re-run the benchmark when you get a chance? Thanks!

@github-actions
Copy link

Benchmarks finished with failure.

Workflow run: https://github.com/tempestphp/100-million-row-challenge/actions/runs/22424378208

1 MB scan (~13.6K lines) has ~5.6% chance of missing any given date
out of ~1818 unique dates, statistically losing ~1 date and ~55K rows.
Increased to 8 MB (~109K lines) where miss probability is ~0.
@brendt
Copy link
Member

brendt commented Feb 26, 2026

Benchmarking complete! Mean execution time: 12.58167775836s

@brendt
Copy link
Member

brendt commented Feb 26, 2026

You've improved your result! Have a cookie: 🍪

@brendt brendt removed the verified label Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants