This repository contains reproducible benchmarks and performance experiments for j-util libraries. It is a benchmark and demo workspace, not a reusable library.
Benchmark results depend on the hardware, operating system, JDK, JVM options, and runtime conditions under which they are collected. Results from this repository must not be treated as universal performance claims.
- JDK 17 or newer
The Maven Wrapper downloads the project's Maven version automatically.
./mvnw clean verifyOrdinary verification tests belong under src/test/java and use JUnit 5.
Dataset generation is a standalone step and is never performed as part of a measured benchmark operation. Build the self-contained JAR, then pass the desired row count to the generator:
./mvnw clean package
java -cp target/benchmarks.jar io.github.jutil.performancelab.CsvDatasetGenerator 1000000This example deterministically writes 1,000,000 data rows plus a header to
target/benchmark-data/benchmark-rows-1000000.csv. Repeating the command with
the same row count produces identical CSV data. To choose another destination,
pass it as the second argument:
java -cp target/benchmarks.jar io.github.jutil.performancelab.CsvDatasetGenerator 1000 /tmp/benchmark.csvGenerated files under target/ are build artifacts and must not be committed.
For a quick development run, generate the benchmark's default 10,000-row dataset:
java -cp target/benchmarks.jar io.github.jutil.performancelab.CsvDatasetGenerator 10000The benchmark includes full-row processing comparisons and two separate categories of reduction benchmarks.
- streaming directly to the full-row consumer;
ArrayListmaterialization with the expected row count as its initial capacity;ArrayListmaterialization starting with an initial capacity of 10;LinkedListmaterialization;ProjectionStorematerialization with the expected row count as its initial capacity; andProjectionStorematerialization starting with an initial capacity of 10.
Each measured operation includes opening and reading the file, parsing CSV, materializing the selected representation where applicable, and running the same full-row checksum consumer. Dataset generation remains a separate, unmeasured step.
The retained ArrayList and columnar ProjectionStore benchmarks compare
reductions after data has already been materialized. They include both an
unfiltered selected-column sum:
sum(priceCents)
and the business operation:
quantity >= 5
sum(priceCents) for matching rows
The existing retained LinkedList scan benchmarks remain available as an
additional representation. These benchmarks prepare their structures in JMH
trial setup, so measured execution excludes CSV ingestion and materialization.
Separate JMH states retain only the representation required by a benchmark.
The retained ArrayList and ProjectionStore use the expected row count as
their initial capacity.
Three end-to-end benchmarks produce the same aggregate from the same CSV input:
arrayListFilteredPriceSumEndToEndparses everyBenchmarkRowinto an expected-sizeArrayList, then scans the list;columnarFilteredPriceSumEndToEndparses everyBenchmarkRowinto an expected-size columnarProjectionStore, then scans its quantity and price projections; andreductionStoreFilteredPriceSumEndToEndfeeds everyBenchmarkRowto the generated reduction store, which incrementally appliesFilteredPriceSumwithout retaining a row collection.
All three compute:
quantity >= 5
sum(priceCents) for matching rows
Each measured operation includes opening and reading the file, parsing the same
BenchmarkRow objects through the same parser and InputStreamProcessor, and
producing the final long sum. The architectural work intentionally differs:
the ArrayList retains objects and performs a later traversal, the columnar
store retains projections and performs a later columnar traversal, and the
reduction store computes during ingestion without materializing retained rows.
The initial-capacity comparison is intentionally limited to the end-to-end benchmarks because it measures construction and growth cost. Once a structure is already loaded, its starting capacity is not part of the measured scan.
Run all methods with:
java -jar target/benchmarks.jar CsvFullRowBenchmarkOverride the rowCount JMH parameter with -p; the corresponding dataset must
already exist:
java -jar target/benchmarks.jar CsvFullRowBenchmark -p rowCount=100000Add JMH's GC profiler to collect allocation and garbage-collection metrics:
java -jar target/benchmarks.jar CsvFullRowBenchmark -p rowCount=10000 -prof gcThe GC profiler does not directly measure retained heap or peak heap usage.
Dataset generation is separate and unmeasured for all categories. JMH warmup means filesystem and operating-system page-cache effects may be present in the end-to-end comparisons. No performance conclusions should be drawn without running controlled experiments on the intended hardware and dataset sizes.