11package io .github .dfa1 .vortex .performance ;
22
33import dev .hardwood .InputFile ;
4+ import dev .hardwood .reader .ColumnReader ;
5+ import dev .hardwood .reader .ColumnReaders ;
46import dev .hardwood .reader .ParquetFileReader ;
57import dev .hardwood .reader .RowReader ;
68import dev .hardwood .schema .ColumnProjection ;
3537
3638/// Benchmark: Hardwood Parquet read vs Java Vortex read on the same real-world dataset.
3739///
38- /// Dataset: NYC Yellow Taxi 2024-01 (~3M rows), three columns:
39- /// trip_distance (DOUBLE), fare_amount (DOUBLE), PULocationID (INT32)
40+ /// Dataset: NYC Yellow Taxi 2024-01 (~3M rows).
41+ /// Columns: trip_distance (DOUBLE), fare_amount (DOUBLE), PULocationID (INT32).
4042///
41- /// Setup downloads the Parquet file once per trial (cached in /tmp), converts it to
42- /// Vortex with column projection, then both benchmarks scan and sum trip_distance.
43+ /// Two Parquet variants:
44+ /// - {@code parquetRead} / {@code parquetReadMultiColumn}: batch column API
45+ /// ({@link ColumnReader#nextBatch()} + {@link ColumnReader#getDoubles()}) —
46+ /// apples-to-apples comparison with Vortex's batch fold.
47+ /// - {@code parquetReadRowByRow} / {@code parquetReadMultiColumnRowByRow}:
48+ /// Hardwood row cursor ({@link RowReader}) — measures row-oriented API overhead
49+ /// on top of format decode cost.
4350///
44- /// Override the Parquet source with: -Dbench.parquet=/path/to/file.parquet
51+ /// Override the Parquet source with: {@code -Dbench.parquet=/path/to/file.parquet}
4552///
46- /// Run: java -jar performance/target/benchmarks.jar ParquetVsVortexReadBenchmark
53+ /// Run: {@code java -jar performance/target/benchmarks.jar ParquetVsVortexReadBenchmark}
4754@ State (Scope .Benchmark )
4855@ BenchmarkMode (Mode .Throughput )
4956@ OutputTimeUnit (TimeUnit .SECONDS )
@@ -106,9 +113,50 @@ public void tearDown() throws IOException {
106113 }
107114 }
108115
109- /// Hardwood: scan all rows, sum trip_distance (single column).
116+ // ── Batch API (apples-to-apples with Vortex) ─────────────────────────────
117+
118+ /// Hardwood batch: decode trip_distance column in chunks, sum all values.
110119 @ Benchmark
111120 public double parquetRead () throws IOException {
121+ double sum = 0.0 ;
122+ try (ParquetFileReader reader = ParquetFileReader .open (InputFile .of (parquetFile ));
123+ ColumnReader cr = reader .columnReader ("trip_distance" )) {
124+ while (cr .nextBatch ()) {
125+ double [] vals = cr .getDoubles ();
126+ int n = cr .getRecordCount ();
127+ for (int i = 0 ; i < n ; i ++) {
128+ sum += vals [i ];
129+ }
130+ }
131+ }
132+ return sum ;
133+ }
134+
135+ /// Hardwood batch: decode fare_amount + PULocationID in chunks, sum both.
136+ @ Benchmark
137+ public double parquetReadMultiColumn () throws IOException {
138+ double fareSum = 0.0 ;
139+ long idSum = 0L ;
140+ try (ParquetFileReader reader = ParquetFileReader .open (InputFile .of (parquetFile ));
141+ ColumnReaders crs = reader .columnReaders (ColumnProjection .columns ("fare_amount" , "PULocationID" ))) {
142+ while (crs .nextBatch ()) {
143+ int n = crs .getRecordCount ();
144+ double [] fares = crs .getColumnReader ("fare_amount" ).getDoubles ();
145+ int [] locs = crs .getColumnReader ("PULocationID" ).getInts ();
146+ for (int i = 0 ; i < n ; i ++) {
147+ fareSum += fares [i ];
148+ idSum += locs [i ];
149+ }
150+ }
151+ }
152+ return fareSum + idSum ;
153+ }
154+
155+ // ── Row cursor API (measures row-oriented overhead on top of decode) ──────
156+
157+ /// Hardwood row cursor: sum trip_distance, one virtual call per row.
158+ @ Benchmark
159+ public double parquetReadRowByRow () throws IOException {
112160 double sum = 0.0 ;
113161 ColumnProjection projection = ColumnProjection .columns ("trip_distance" );
114162 try (ParquetFileReader reader = ParquetFileReader .open (InputFile .of (parquetFile ));
@@ -121,9 +169,9 @@ public double parquetRead() throws IOException {
121169 return sum ;
122170 }
123171
124- /// Hardwood: scan all rows, sum fare_amount + PULocationID ( two columns) .
172+ /// Hardwood row cursor: sum fare_amount + PULocationID, two virtual calls per row .
125173 @ Benchmark
126- public double parquetReadMultiColumn () throws IOException {
174+ public double parquetReadMultiColumnRowByRow () throws IOException {
127175 double fareSum = 0.0 ;
128176 long idSum = 0L ;
129177 ColumnProjection projection = ColumnProjection .columns ("fare_amount" , "PULocationID" );
@@ -138,7 +186,9 @@ public double parquetReadMultiColumn() throws IOException {
138186 return fareSum + idSum ;
139187 }
140188
141- /// Vortex: scan all rows, sum trip_distance via fold.
189+ // ── Vortex ────────────────────────────────────────────────────────────────
190+
191+ /// Vortex: decode trip_distance in chunks, sum via batch fold.
142192 @ Benchmark
143193 public double vortexRead () throws IOException {
144194 double sum = 0.0 ;
@@ -153,7 +203,7 @@ public double vortexRead() throws IOException {
153203 return sum ;
154204 }
155205
156- /// Vortex: scan fare_amount + PULocationID, sum both (exercises multi-column projection) .
206+ /// Vortex: decode fare_amount + PULocationID in chunks , sum both via batch fold .
157207 @ Benchmark
158208 public double vortexReadMultiColumn () throws IOException {
159209 double fareSum = 0.0 ;
@@ -171,6 +221,8 @@ public double vortexReadMultiColumn() throws IOException {
171221 return fareSum + idSum ;
172222 }
173223
224+ // ── Setup helper ──────────────────────────────────────────────────────────
225+
174226 private static Path downloadIfAbsent (Path dest , String url ) throws Exception {
175227 if (Files .exists (dest )) {
176228 System .out .printf ("[ParquetVsVortexReadBenchmark] cache hit: %s%n" , dest );
0 commit comments