|
| 1 | +# Performance |
| 2 | + |
| 3 | +## Benchmark Results |
| 4 | + |
| 5 | +### Flat Files |
| 6 | + |
| 7 | +These are the results from parsing files of the NYC Yellow Taxi Trip data set (subset 2016-01 to 2025-11, ~9.2GB overall, ~650M rows), |
| 8 | +running on a Macbook Pro M3 Max. |
| 9 | +The test (`FlatPerformanceTest`) parses all files and adds up the values of three columns (out of 20). |
| 10 | +The results shown are for: |
| 11 | + |
| 12 | +* The row reader API, using indexed access (mapping field names to indexes once upfront) |
| 13 | +* The columnar reader API, using indexed access |
| 14 | + |
| 15 | +``` |
| 16 | +==================================================================================================== |
| 17 | +PERFORMANCE TEST RESULTS |
| 18 | +==================================================================================================== |
| 19 | +
|
| 20 | +Environment: |
| 21 | + CPU cores: 16 |
| 22 | + Java version: 25 |
| 23 | + OS: Mac OS X aarch64 |
| 24 | +
|
| 25 | +Data: |
| 26 | + Files processed: 119 |
| 27 | + Total rows: 651,209,003 |
| 28 | + Total size: 9,241.1 MB |
| 29 | + Runs per contender: 5 |
| 30 | +
|
| 31 | +Correctness Verification: |
| 32 | + passenger_count trip_distance fare_amount |
| 33 | + Hardwood (multifile indexed) 972,078,547 2,701,223,013.48 9,166,943,759.83 |
| 34 | + Hardwood (column reader multifile) 972,078,547 2,701,223,013.48 9,166,943,759.83 |
| 35 | +
|
| 36 | +Performance (all runs): |
| 37 | + Contender Time (s) Records/sec Records/sec/core MB/sec |
| 38 | + ----------------------------------------------------------------------------------------------- |
| 39 | + Hardwood (multifile indexed) [1] 2.75 236,975,620 14,810,976 3362.8 |
| 40 | + Hardwood (multifile indexed) [2] 2.78 234,669,911 14,666,869 3330.1 |
| 41 | + Hardwood (multifile indexed) [3] 2.70 240,831,732 15,051,983 3417.6 |
| 42 | + Hardwood (multifile indexed) [4] 2.70 240,831,732 15,051,983 3417.6 |
| 43 | + Hardwood (multifile indexed) [5] 2.68 242,897,800 15,181,113 3446.9 |
| 44 | + Hardwood (multifile indexed) [AVG] 2.72 239,239,163 14,952,448 3395.0 |
| 45 | + min: 2.68s, max: 2.78s, spread: 0.09s |
| 46 | +
|
| 47 | + Hardwood (column reader multifile) [1] 1.30 502,476,083 31,404,755 7130.5 |
| 48 | + Hardwood (column reader multifile) [2] 1.11 584,568,225 36,535,514 8295.4 |
| 49 | + Hardwood (column reader multifile) [3] 1.06 614,348,116 38,396,757 8718.0 |
| 50 | + Hardwood (column reader multifile) [4] 1.06 616,091,772 38,505,736 8742.8 |
| 51 | + Hardwood (column reader multifile) [5] 1.08 603,530,123 37,720,633 8564.5 |
| 52 | + Hardwood (column reader multifile) [AVG] 1.12 580,917,933 36,307,371 8243.6 |
| 53 | + min: 1.06s, max: 1.30s, spread: 0.24s |
| 54 | +
|
| 55 | +==================================================================================================== |
| 56 | +``` |
| 57 | + |
| 58 | +### Nested Files |
| 59 | + |
| 60 | +These are the results from parsing a file with points of interest from the Overture Maps data set |
| 61 | +(~900 MB, ~9M rows), running on a Macbook Pro M3 Max. |
| 62 | +The test (`NestedPerformanceTest`) parses all columns of the file and determines min/max values, max array lengths, etc. |
| 63 | +As above, the results shown are for the row reader API and the columnar API with indexed access. |
| 64 | + |
| 65 | +``` |
| 66 | +==================================================================================================== |
| 67 | +NESTED SCHEMA PERFORMANCE TEST RESULTS |
| 68 | +==================================================================================================== |
| 69 | +
|
| 70 | +Environment: |
| 71 | + CPU cores: 16 |
| 72 | + Java version: 25 |
| 73 | + OS: Mac OS X aarch64 |
| 74 | +
|
| 75 | +Data: |
| 76 | + Total rows: 9,152,540 |
| 77 | + File size: 882.2 MB |
| 78 | + Runs per contender: 5 |
| 79 | +
|
| 80 | +Correctness Verification: |
| 81 | + min_ver max_ver rows websites sources addresses |
| 82 | + Hardwood (indexed) 1 9 9,152,540 3,687,576 18,305,080 9,152,540 |
| 83 | + Hardwood (columnar) 1 9 9,152,540 3,687,576 18,305,080 9,152,540 |
| 84 | +
|
| 85 | +Performance (all runs): |
| 86 | + Contender Time (s) Records/sec Records/sec/core MB/sec |
| 87 | + ----------------------------------------------------------------------------------------------- |
| 88 | + Hardwood (indexed) [1] 2.22 4,120,910 257,557 397.2 |
| 89 | + Hardwood (indexed) [2] 1.92 4,759,511 297,469 458.8 |
| 90 | + Hardwood (indexed) [3] 1.89 4,855,459 303,466 468.0 |
| 91 | + Hardwood (indexed) [4] 1.88 4,876,153 304,760 470.0 |
| 92 | + Hardwood (indexed) [5] 1.88 4,858,036 303,627 468.3 |
| 93 | + Hardwood (indexed) [AVG] 1.96 4,674,433 292,152 450.6 |
| 94 | + min: 1.88s, max: 2.22s, spread: 0.34s |
| 95 | +
|
| 96 | + Hardwood (columnar) [1] 1.34 6,830,254 426,891 658.4 |
| 97 | + Hardwood (columnar) [2] 1.32 6,918,020 432,376 666.8 |
| 98 | + Hardwood (columnar) [3] 1.24 7,363,266 460,204 709.8 |
| 99 | + Hardwood (columnar) [4] 1.24 7,404,968 462,810 713.8 |
| 100 | + Hardwood (columnar) [5] 1.22 7,477,565 467,348 720.8 |
| 101 | + Hardwood (columnar) [AVG] 1.27 7,189,741 449,359 693.0 |
| 102 | + min: 1.22s, max: 1.34s, spread: 0.12s |
| 103 | +
|
| 104 | +==================================================================================================== |
| 105 | +``` |
| 106 | + |
| 107 | +## Running Performance Tests |
| 108 | + |
| 109 | +The performance testing modules are not included in the default build. Enable them with `-Pperformance-test`. |
| 110 | + |
| 111 | +### End-to-End Performance Tests |
| 112 | + |
| 113 | +There are two end-to-end performance tests: one for flat schemas (NYC Yellow Taxi Trip data) and one for nested schemas (Overture Maps POI data). Test data is downloaded automatically on the first run. |
| 114 | + |
| 115 | +```shell |
| 116 | +./mvnw test -Pperformance-test |
| 117 | +``` |
| 118 | + |
| 119 | +**Flat schema test** (`FlatPerformanceTest`) — reads ~9GB of taxi trip data (2016-2025, ~650M rows) and sums three columns. |
| 120 | + |
| 121 | +| Property | Default | Description | |
| 122 | +|----------|---------|-------------| |
| 123 | +| `perf.contenders` | `HARDWOOD_MULTIFILE_INDEXED` | Comma-separated list of contenders, or `all` | |
| 124 | +| `perf.start` | `2016-01` | Start year-month for data range | |
| 125 | +| `perf.end` | `2025-11` | End year-month for data range | |
| 126 | +| `perf.runs` | `10` | Number of timed runs per contender | |
| 127 | + |
| 128 | +Available contenders: `HARDWOOD_INDEXED`, `HARDWOOD_NAMED`, `HARDWOOD_PROJECTION`, `HARDWOOD_MULTIFILE_INDEXED`, `HARDWOOD_MULTIFILE_NAMED`, `HARDWOOD_COLUMN_READER`, `HARDWOOD_COLUMN_READER_MULTIFILE`, `PARQUET_JAVA_INDEXED`, `PARQUET_JAVA_NAMED`. |
| 129 | + |
| 130 | +**Nested schema test** (`NestedPerformanceTest`) — reads ~900MB of Overture Maps POI data (~9M rows) with deeply nested columns. |
| 131 | + |
| 132 | +| Property | Default | Description | |
| 133 | +|----------|---------|-------------| |
| 134 | +| `perf.contenders` | `HARDWOOD_NAMED` | Comma-separated list of contenders, or `all` | |
| 135 | +| `perf.runs` | `5` | Number of timed runs per contender | |
| 136 | + |
| 137 | +Available contenders: `HARDWOOD_INDEXED`, `HARDWOOD_NAMED`, `HARDWOOD_COLUMNAR`, `PARQUET_JAVA`. |
| 138 | + |
| 139 | +**Examples:** |
| 140 | + |
| 141 | +```shell |
| 142 | +# Run all contenders for the flat test, limited to 2025 data |
| 143 | +./mvnw test -Pperformance-test -Dtest=FlatPerformanceTest -Dperf.contenders=all -Dperf.start=2025-01 |
| 144 | + |
| 145 | +# Compare multifile indexed vs named access |
| 146 | +./mvnw test -Pperformance-test -Dperf.contenders=HARDWOOD_MULTIFILE_INDEXED,HARDWOOD_MULTIFILE_NAMED |
| 147 | + |
| 148 | +# Run nested test only |
| 149 | +./mvnw test -Pperformance-test -Dtest=NestedPerformanceTest -Dperf.contenders=all |
| 150 | +``` |
| 151 | + |
| 152 | +### PyArrow Comparison Tests |
| 153 | + |
| 154 | +Python counterparts of the Java performance tests using PyArrow, for cross-platform comparison. |
| 155 | +These scripts require a Python environment with PyArrow installed (use the `.venv` venv). |
| 156 | + |
| 157 | +**Flat schema** (`flat_performance_test.py`) — counterpart of `FlatPerformanceTest.java`: |
| 158 | + |
| 159 | +```shell |
| 160 | +cd performance-testing/end-to-end |
| 161 | + |
| 162 | +# Run all contenders (single-threaded and multi-threaded), 5 runs each |
| 163 | +python flat_performance_test.py |
| 164 | + |
| 165 | +# Single-threaded only |
| 166 | +python flat_performance_test.py -c single_threaded |
| 167 | + |
| 168 | +# Multi-threaded, 10 runs |
| 169 | +python flat_performance_test.py -c multi_threaded -r 10 |
| 170 | +``` |
| 171 | + |
| 172 | +**Nested schema** (`nested_performance_test.py`) — counterpart of `NestedPerformanceTest.java`: |
| 173 | + |
| 174 | +```shell |
| 175 | +cd performance-testing/end-to-end |
| 176 | + |
| 177 | +# Run all contenders, 5 runs each |
| 178 | +python nested_performance_test.py |
| 179 | + |
| 180 | +# Single-threaded only, 3 runs |
| 181 | +python nested_performance_test.py -c single_threaded -r 3 |
| 182 | +``` |
| 183 | + |
| 184 | +**Options:** |
| 185 | + |
| 186 | +| Flag | Default | Description | |
| 187 | +|------|---------|-------------| |
| 188 | +| `-c`, `--contenders` | `all` | Contenders to run: `single_threaded`, `multi_threaded`, or `all` | |
| 189 | +| `-r`, `--runs` | `5` | Number of timed runs per contender | |
| 190 | + |
| 191 | +**Notes on comparability:** |
| 192 | + |
| 193 | +- The flat test uses column projection (reads only the 3 summed columns), matching the Hardwood projection and column-reader contenders. The parquet-java contenders in `FlatPerformanceTest.java` read all columns without projection, so direct comparison against parquet-java is not apples-to-apples. |
| 194 | +- PyArrow uses vectorized columnar operations (C++ engine) rather than row-by-row iteration. |
| 195 | +- The `single_threaded` contender (`use_threads=False`) is most comparable to single-threaded parquet-java; `multi_threaded` is comparable to Hardwood's parallel reading. |
| 196 | + |
| 197 | +### JMH Micro-Benchmarks |
| 198 | + |
| 199 | +For detailed micro-benchmarks, build the JMH benchmark JAR and run it directly: |
| 200 | + |
| 201 | +```shell |
| 202 | +# Build the benchmark JAR |
| 203 | +./mvnw package -Pperformance-test -pl performance-testing/micro-benchmarks -am -DskipTests |
| 204 | + |
| 205 | +# Run all benchmarks (with Vector API for SIMD support) |
| 206 | +java --add-modules jdk.incubator.vector \ |
| 207 | + -jar performance-testing/micro-benchmarks/target/benchmarks.jar \ |
| 208 | + -p dataDir=performance-testing/test-data-setup/target/tlc-trip-record-data |
| 209 | + |
| 210 | +# Run a specific benchmark |
| 211 | +java --add-modules jdk.incubator.vector \ |
| 212 | + -jar performance-testing/micro-benchmarks/target/benchmarks.jar \ |
| 213 | + "PageHandlingBenchmark.decodePages" \ |
| 214 | + -p dataDir=performance-testing/test-data-setup/target/tlc-trip-record-data |
| 215 | + |
| 216 | +# Run SIMD benchmark comparing scalar vs vectorized operations |
| 217 | +java --add-modules jdk.incubator.vector \ |
| 218 | + -jar performance-testing/micro-benchmarks/target/benchmarks.jar SimdBenchmark \ |
| 219 | + -p size=1024,8192,65536 -p implementation=scalar,auto |
| 220 | + |
| 221 | +# List available benchmarks |
| 222 | +java --add-modules jdk.incubator.vector \ |
| 223 | + -jar performance-testing/micro-benchmarks/target/benchmarks.jar -l |
| 224 | +``` |
| 225 | + |
| 226 | +**Available benchmarks:** |
| 227 | + |
| 228 | +| Benchmark | Description | |
| 229 | +|-----------|-------------| |
| 230 | +| `MemoryMapBenchmark.memoryMapToByteArray` | Memory map a file and copy to byte array | |
| 231 | +| `PageHandlingBenchmark.a_decompressPages` | Scan and decompress all pages | |
| 232 | +| `PageHandlingBenchmark.b_decodePages` | Scan, decompress, and decode all pages | |
| 233 | +| `PipelineBenchmark.a_assembleColumns` | Synchronous page decoding + column assembly | |
| 234 | +| `PipelineBenchmark.b_consumeRows` | Full pipeline with row-oriented access | |
| 235 | +| `SimdBenchmark.*` | SIMD operations (countNonNulls, markNulls, dictionary, bit unpacking) | |
| 236 | + |
| 237 | +**JMH options:** |
| 238 | + |
| 239 | +| Option | Description | |
| 240 | +|--------|-------------| |
| 241 | +| `-wi <n>` | Number of warmup iterations (default: 3) | |
| 242 | +| `-i <n>` | Number of measurement iterations (default: 5) | |
| 243 | +| `-f <n>` | Number of forks (default: 2) | |
| 244 | +| `-p param=value` | Set benchmark parameter | |
| 245 | +| `-l` | List available benchmarks | |
| 246 | +| `-h` | Show help | |
| 247 | + |
| 248 | +**Note:** The taxi data files use GZIP compression (2016-01 to 2023-01) and ZSTD compression (2023-02 onwards). The default benchmark file is `yellow_tripdata_2025-05.parquet` (ZSTD, 75MB). |
0 commit comments