Brief
One text file, one billion rows of temperature readings, one job: print min/mean/max per weather station, in Java, as fast as you can make it. The naive solution takes minutes; the leaderboard's best run in seconds on the same hardware. The gap is the exercise — closed one measured optimization at a time, never by guessing.
Instructions
The spec
- Input —
measurements.txt: one reading per line,<station>;<temperature>\n. Station name: UTF-8, 1–100 bytes, no;or newline (a few hundred to ~10k distinct stations). Temperature: a decimal with exactly one fractional digit,-99.9to99.9(e.g.Hamburg;12.0,Bulawayo;8.9,St. John's;-1.5). - Output — to stdout, stations sorted alphabetically, one run of
<station>=<min>/<mean>/<max>each with one fractional digit, mean rounded half-up, in the format{Abha=-23.0/18.0/59.2, Abidjan=10.1/26.4/50.5, ...}. - Generator — produce the 1B-row file with the upstream repo's generator
(
create_measurements.sh), or write an equivalent one (station list + Gaussian per station). The file is ~13 GB; generate once, reuse for every run.
Measurement protocol
- Same machine for every measurement, otherwise numbers don't compare. Note its specs (CPU, cores, RAM, disk) once.
- Warm the file into page cache (or accept and note the cold-read cost consistently);
time with
hyperfineor repeatedtimeruns — at least 3 runs per version, report the best or median, consistently. - Keep a running results table: version, change made, wall time, delta. Every ladder stage adds a row. A change without a row didn't happen.
The optimization ladder
Climb in order; at each stage, profile first (JFR or async-profiler), change the one thing the profile indicts, measure, and record.
- Baseline —
BufferedReader.lines(),String.split(";"),HashMap<String, DoubleSummaryStatistics-ish>. Correct output, honest wall time: your denominator. - Fix what the profiler shows — expected kills:
splitand substring allocation (parse the line by index yourself), autoboxedDoubles (primitive min/sum/max/count accumulators), repeatedStringhashing. Single-threaded still. - Parallel chunks — split the file into N byte ranges aligned to newline boundaries, one worker per chunk, merge the per-station accumulators at the end. Try platform vs. virtual threads and record why the numbers barely differ (CPU-bound, no blocking to unmount).
- Memory-map + bytes —
MappedByteBufferor the FFM API'sMemorySegment; parse raw bytes with noStringcreation until final output; temperatures as scaled integers (-1.5→-15), custom byte-level number parse. - Leaderboard tricks (optional but scored) — read top entries and port techniques
one at a time: SWAR-style multi-byte scanning for
;/newline, branchless temperature parsing, a custom open-addressing hash keyed on raw name bytes. Keep only what your table shows earns its complexity.
Constraints
- Java only, single JVM process, no external dependencies for the computation itself (the original challenge's rule); any JDK distribution you like — note which.
- Correct output at every stage — a fast wrong answer is worthless. Diff each version's output against the baseline's before timing it.
- No optimization lands without a profile pointing at it and a results-table row behind it.
Examples
Input fragment:
Hamburg;12.0
Bulawayo;8.9
Palembang;38.8
St. John's;15.2
Hamburg;34.2
Matching output fragment (alphabetical, min/mean/max):
{Bulawayo=8.9/8.9/8.9, Hamburg=12.0/23.1/34.2, Palembang=38.8/38.8/38.8, St. John's=15.2/15.2/15.2}
Acceptance
Mapped one-to-one onto the exercise's milestones:
- Baseline — the 1B-row file exists; the naive solution produces byte-correct output; its wall time is the results table's first row.
- Profile & fix the obvious — a profile artifact exists for the baseline; the single-threaded optimized version is measurably faster, with per-change rows showing which fix bought what.
- Go parallel — chunked parallel version merges correctly (output still byte-identical); scaling vs. core count recorded; the platform-vs-virtual-threads comparison has numbers and a one-line explanation.
- Memory-map it — no
Stringallocated per line (verify by allocation profile); scaled-integer parsing; the table shows the drop. - Steal like an engineer — at least one leaderboard technique ported, measured, and kept or reverted based on the numbers; the final table tells the whole story from baseline to best.
Related
- One Billion Row Challenge — the exercise this is the subject of.
- gunnarmorling/1brc — the original challenge: generator tooling, full rules, and the leaderboard entries stage 5 mines.