This is an automated email from the ASF dual-hosted git repository.
MaxGekk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 526d88da65e0 [SPARK-57666][SQL] Add Timestamp nano benchmarks
526d88da65e0 is described below
commit 526d88da65e0ba200fcca80285733731f4d3e2ba
Author: Stevo Mitric <[email protected]>
AuthorDate: Thu Jun 25 18:03:36 2026 +0200
[SPARK-57666][SQL] Add Timestamp nano benchmarks
### What changes were proposed in this pull request?
Add `TimestampNanosBenchmark`, comparing the microsecond timestamp path
against the nanosecond timestamp types `TIMESTAMP_NTZ(p)` / `TIMESTAMP_LTZ(p)`
(p in [7, 9]) across cast, string parse/format, hashing, sort, group-by,
MIN/MAX, datetime functions, and Parquet/ORC read/write.
### Why are the changes needed?
To quantify the per-row overhead of the 16-byte `TimestampNanosVal` carrier
vs the 8-byte micros `Long`, and to surface known nanos gaps (no radix sort
prefix; row-based-only vectorized Parquet read).
### Does this PR introduce _any_ user-facing change?
No, test-only.
### How was this patch tested?
N/A (benchmark). Results regenerated via the `Run benchmarks` GitHub Action.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)
Closes #56744 from stevomitric/stevomitric/timestamp-nanos-benchmark.
Authored-by: Stevo Mitric <[email protected]>
Signed-off-by: Max Gekk <[email protected]>
---
.../benchmarks/TimestampNanosBenchmark-results.txt | 294 +++++++++++++++++++++
.../benchmark/TimestampNanosBenchmark.scala | 245 +++++++++++++++++
2 files changed, 539 insertions(+)
diff --git a/sql/core/benchmarks/TimestampNanosBenchmark-results.txt
b/sql/core/benchmarks/TimestampNanosBenchmark-results.txt
new file mode 100644
index 000000000000..33a104b9a537
--- /dev/null
+++ b/sql/core/benchmarks/TimestampNanosBenchmark-results.txt
@@ -0,0 +1,294 @@
+================================================================================================
+Cast
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+micros -> string: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros -> string wholestage off 3586 3610
35 2.8 358.6 1.0X
+micros -> string wholestage on 3663 3682
27 2.7 366.3 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+nanos(9) -> string: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+nanos(9) -> string wholestage off 8024 8048
33 1.2 802.4 1.0X
+nanos(9) -> string wholestage on 7987 8002
10 1.3 798.7 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+nanos(7) -> string: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+nanos(7) -> string wholestage off 7879 7884
7 1.3 787.9 1.0X
+nanos(7) -> string wholestage on 7882 7936
70 1.3 788.2 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+micros -> nanos(9): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros -> nanos(9) wholestage off 868 870
4 11.5 86.8 1.0X
+micros -> nanos(9) wholestage on 838 845
12 11.9 83.8 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+nanos(9) -> micros: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+nanos(9) -> micros wholestage off 1999 2024
35 5.0 199.9 1.0X
+nanos(9) -> micros wholestage on 1988 2025
57 5.0 198.8 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+nanos(9) -> nanos(7): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+nanos(9) -> nanos(7) wholestage off 2042 2044
3 4.9 204.2 1.0X
+nanos(9) -> nanos(7) wholestage on 2024 2031
4 4.9 202.4 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+micros -> date: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros -> date wholestage off 1418 1515
136 7.0 141.8 1.0X
+micros -> date wholestage on 1344 1359
18 7.4 134.4 1.1X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+nanos(9) -> date: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+nanos(9) -> date wholestage off 2506 2513
10 4.0 250.6 1.0X
+nanos(9) -> date wholestage on 2525 2542
21 4.0 252.5 1.0X
+
+
+================================================================================================
+Parse from string
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+string(6 digits) -> micros: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+-------------------------------------------------------------------------------------------------------------------------
+string(6 digits) -> micros wholestage off 288 291
4 3.5 288.1 1.0X
+string(6 digits) -> micros wholestage on 279 282
3 3.6 278.9 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+string(9 digits) -> nanos(9): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+---------------------------------------------------------------------------------------------------------------------------
+string(9 digits) -> nanos(9) wholestage off 313 327
19 3.2 313.2 1.0X
+string(9 digits) -> nanos(9) wholestage on 305 310
9 3.3 305.1 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+string(7 digits) -> nanos(7): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+---------------------------------------------------------------------------------------------------------------------------
+string(7 digits) -> nanos(7) wholestage off 295 301
9 3.4 295.1 1.0X
+string(7 digits) -> nanos(7) wholestage on 293 295
2 3.4 292.7 1.0X
+
+
+================================================================================================
+Hashing
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+murmur3 hash(micros): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+murmur3 hash(micros) wholestage off 858 868
14 11.7 85.8 1.0X
+murmur3 hash(micros) wholestage on 834 836
3 12.0 83.4 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+murmur3 hash(nanos(9)): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+murmur3 hash(nanos(9)) wholestage off 1987 2044
80 5.0 198.7 1.0X
+murmur3 hash(nanos(9)) wholestage on 2024 2030
6 4.9 202.4 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+xxhash64(micros): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+xxhash64(micros) wholestage off 806 807
1 12.4 80.6 1.0X
+xxhash64(micros) wholestage on 801 808
8 12.5 80.1 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+xxhash64(nanos(9)): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+xxhash64(nanos(9)) wholestage off 2043 2043
0 4.9 204.3 1.0X
+xxhash64(nanos(9)) wholestage on 2003 2038
32 5.0 200.3 1.0X
+
+
+================================================================================================
+Sort (global ORDER BY)
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+order by micros: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+order by micros wholestage off 2345 2399
77 4.3 234.5 1.0X
+order by micros wholestage on 2325 2344
22 4.3 232.5 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+order by nanos(9): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+order by nanos(9) wholestage off 3037 3172
190 3.3 303.7 1.0X
+order by nanos(9) wholestage on 3103 3186
65 3.2 310.3 1.0X
+
+
+================================================================================================
+Group-by aggregation (~1M distinct keys)
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+group by micros key: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+group by micros key wholestage off 2080 2114
47 4.8 208.0 1.0X
+group by micros key wholestage on 1888 1919
19 5.3 188.8 1.1X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+group by nanos(9) key: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+group by nanos(9) key wholestage off 3576 3611
50 2.8 357.6 1.0X
+group by nanos(9) key wholestage on 3039 3127
82 3.3 303.9 1.2X
+
+
+================================================================================================
+MIN/MAX aggregation
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+min/max micros: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+min/max micros wholestage off 998 1004
7 10.0 99.8 1.0X
+min/max micros wholestage on 617 651
33 16.2 61.7 1.6X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+min/max nanos(9): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+min/max nanos(9) wholestage off 2402 2410
11 4.2 240.2 1.0X
+min/max nanos(9) wholestage on 1895 1912
26 5.3 189.5 1.3X
+
+
+================================================================================================
+Datetime functions
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+hour(micros): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+hour(micros) wholestage off 749 758
13 13.4 74.9 1.0X
+hour(micros) wholestage on 723 728
5 13.8 72.3 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+hour(nanos(9)): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+hour(nanos(9)) wholestage off 1803 1816
19 5.5 180.3 1.0X
+hour(nanos(9)) wholestage on 1765 1789
34 5.7 176.5 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+second(micros): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+second(micros) wholestage off 777 783
9 12.9 77.7 1.0X
+second(micros) wholestage on 863 1117
146 11.6 86.3 0.9X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+second(nanos(9)): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+second(nanos(9)) wholestage off 2094 2098
6 4.8 209.4 1.0X
+second(nanos(9)) wholestage on 1781 2059
340 5.6 178.1 1.2X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+extract(SECOND, micros): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+extract(SECOND, micros) wholestage off 880 880
0 11.4 88.0 1.0X
+extract(SECOND, micros) wholestage on 993 1215
145 10.1 99.3 0.9X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+extract(SECOND, nanos(9)): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+extract(SECOND, nanos(9)) wholestage off 2409 2470
87 4.2 240.9 1.0X
+extract(SECOND, nanos(9)) wholestage on 2217 2346
132 4.5 221.7 1.1X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+unix_micros(micros): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+unix_micros(micros) wholestage off 295 301
8 33.8 29.5 1.0X
+unix_micros(micros) wholestage on 254 272
18 39.4 25.4 1.2X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+unix_nanos(nanos(9)): Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+unix_nanos(nanos(9)) wholestage off 2450 2478
40 4.1 245.0 1.0X
+unix_nanos(nanos(9)) wholestage on 2398 2450
59 4.2 239.8 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+micros + interval 1 hour: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros + interval 1 hour wholestage off 1727 1763
51 5.8 172.7 1.0X
+micros + interval 1 hour wholestage on 1693 1750
52 5.9 169.3 1.0X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+nanos(9) + interval 1 hour: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+-------------------------------------------------------------------------------------------------------------------------
+nanos(9) + interval 1 hour wholestage off 3678 3709
44 2.7 367.8 1.0X
+nanos(9) + interval 1 hour wholestage on 3192 3300
89 3.1 319.2 1.2X
+
+
+================================================================================================
+Parquet write/read
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+Save to Parquet: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros (TIMESTAMP_NTZ) 2279 2279
0 4.4 227.9 1.0X
+nanos(9) (TIMESTAMP_NTZ(9)) 3485 3485
0 2.9 348.5 0.7X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+Load from Parquet: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros, vectorized on 532 616
76 18.8 53.2 1.0X
+micros, vectorized off 1798 2067
447 5.6 179.8 0.3X
+nanos(9), vectorized on (row-based) 1949 1982
29 5.1 194.9 0.3X
+nanos(9), vectorized off (row-based) 1804 1817
12 5.5 180.4 0.3X
+
+
+================================================================================================
+ORC write/read
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+Save to ORC: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros (TIMESTAMP_NTZ) 1257 1257
0 8.0 125.7 1.0X
+nanos(9) (TIMESTAMP_NTZ(9)) 5537 5537
0 1.8 553.7 0.2X
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux
5.4.0-1160-aws-fips
+Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
+Load from ORC: Best Time(ms) Avg Time(ms)
Stdev(ms) Rate(M/s) Per Row(ns) Relative
+------------------------------------------------------------------------------------------------------------------------
+micros, vectorized on 361 398
42 27.7 36.1 1.0X
+micros, vectorized off 1026 1047
19 9.7 102.6 0.4X
+nanos(9), vectorized on 1504 1517
16 6.7 150.4 0.2X
+nanos(9), vectorized off 2213 2264
44 4.5 221.3 0.2X
+
+
diff --git
a/sql/core/src/test/scala/org/apache/spark/sql/execution/benchmark/TimestampNanosBenchmark.scala
b/sql/core/src/test/scala/org/apache/spark/sql/execution/benchmark/TimestampNanosBenchmark.scala
new file mode 100644
index 000000000000..97372d8402a9
--- /dev/null
+++
b/sql/core/src/test/scala/org/apache/spark/sql/execution/benchmark/TimestampNanosBenchmark.scala
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.sql.execution.benchmark
+
+import org.apache.spark.benchmark.Benchmark
+import
org.apache.spark.sql.catalyst.util.DateTimeTestUtils.{withDefaultTimeZone, LA}
+import org.apache.spark.sql.internal.SQLConf
+
+/**
+ * Synthetic benchmark comparing the microsecond timestamp path against the
nanosecond-precision
+ * timestamp types `TIMESTAMP_NTZ(p)` / `TIMESTAMP_LTZ(p)`, `p in [7, 9]`
(umbrella SPARK-56822).
+ *
+ * Most groups pair the same operation over a microsecond timestamp and the
nanosecond type, so the
+ * per-row delta of the nanos path (a 16-byte `TimestampNanosVal` =
epochMicros:Long +
+ * nanosWithinMicro:Short carrier, vs a plain 8-byte Long) is read directly
off adjacent rows. A few
+ * groups pair related operations instead: Cast includes the cross-casts
(micros <-> nanos(9)) and a
+ * nanos-only narrowing (nanos(9) -> nanos(7)), and Datetime functions pairs
`unix_micros` with the
+ * nanos-only `unix_nanos`.
+ * The areas where nanos is expected to diverge from micros:
+ * - hashing: two hash ops (hashInt(nanos), hashLong(micros)) vs one;
+ * - ordering/sort: nanos has NO radix sort prefix, so it falls back to a
full `compareTo` per
+ * comparison (micros sort on the primitive Long prefix);
+ * - Parquet read: nanos read is row-based only (vectorized read is disabled
for the 16-byte
+ * two-field carrier), whereas micros read is vectorized;
+ * - ORC read: nanos reads vectorized over the full year range, like micros.
+ *
+ * Parsing and cast-to-string are additionally spot-checked at p=7 (alongside
p=9): the 16-byte
+ * carrier is identical for all p, so only fractional-digit
truncation/formatting differs.
+ *
+ * The nanosecond timestamp types are behind the preview flag
+ * `spark.sql.timestampNanosTypes.enabled` (default = `isTesting`).
`BenchmarkBase.main` sets
+ * `IS_TESTING=true`, so the flag is on during a benchmark run; this suite
also sets it explicitly.
+ *
+ * To run this benchmark:
+ * {{{
+ * 1. without sbt:
+ * bin/spark-submit --class <this class>
+ * --jars <spark core test jar>,<spark catalyst test jar> <sql core
test jar>
+ * 2. build/sbt "sql/Test/runMain <this class>"
+ * 3. generate result:
+ * SPARK_GENERATE_BENCHMARK_FILES=1 build/sbt "sql/Test/runMain <this
class>"
+ * Results will be written to
"benchmarks/TimestampNanosBenchmark-results.txt".
+ * }}}
+ */
+object TimestampNanosBenchmark extends SqlBasedBenchmark {
+
+ private val N = 10000000
+
+ //
---------------------------------------------------------------------------
+ // Value generators (SQL expressions over `id` from spark.range).
+ //
+ // Nanos values are built as `id` seconds + (id % 1000)
nanoseconds-of-microsecond, so both the
+ // micros field and the sub-microsecond field vary across rows. The whole
range stays in ~1970,
+ // inside the Parquet INT64-NANOS window (1677-09-21 .. 2262-04-11) so
Parquet writes never throw
+ // DATETIME_OVERFLOW.
+ //
---------------------------------------------------------------------------
+ private val microLtz = "timestamp_seconds(id)"
+ private val microNtz = "cast(timestamp_seconds(id) as timestamp_ntz)"
+ private val nanosArg = "id * 1000000000 + id % 1000"
+ private val nanosLtz = s"timestamp_nanos($nanosArg)"
+ private val nanosNtz = s"cast(timestamp_nanos($nanosArg) as
timestamp_ntz(9))"
+ private val nanosNtz7 = s"cast(timestamp_nanos($nanosArg) as
timestamp_ntz(7))"
+
+ // 9-, 7- and 6-digit fractional-second strings for the parsing group.
+ private val nanosStr =
+ "concat('2019-01-27 11:02:01.', lpad(cast(id % 1000000000 as string), 9,
'0'))"
+ private val nanosStr7 =
+ "concat('2019-01-27 11:02:01.', lpad(cast(id % 10000000 as string), 7,
'0'))"
+ private val microStr =
+ "concat('2019-01-27 11:02:01.', lpad(cast(id % 1000000 as string), 6,
'0'))"
+
+ private def doBenchmark(cardinality: Int, exprs: String*): Unit = {
+ spark.range(cardinality).selectExpr(exprs: _*).noop()
+ }
+
+ /** A wholestage on/off codegen benchmark that just projects `exprs` over
`cardinality` rows. */
+ private def runProject(cardinality: Int, name: String, exprs: String*): Unit
= {
+ codegenBenchmark(name, cardinality) {
+ doBenchmark(cardinality, exprs: _*)
+ }
+ }
+
+ /** A codegen benchmark that builds a single `ts` column then runs `op(ds)`.
*/
+ private def runOnColumn(
+ cardinality: Int, name: String, tsExpr: String)(op:
org.apache.spark.sql.DataFrame => Unit)
+ : Unit = {
+ codegenBenchmark(name, cardinality) {
+ op(spark.range(cardinality).selectExpr(s"$tsExpr as ts"))
+ }
+ }
+
+ override def runBenchmarkSuite(mainArgs: Array[String]): Unit = {
+ withDefaultTimeZone(LA) {
+ withSQLConf(
+ SQLConf.SESSION_LOCAL_TIMEZONE.key -> LA.getId,
+ SQLConf.TIMESTAMP_NANOS_TYPES_ENABLED.key -> "true") {
+
+ runBenchmark("Cast") {
+ runProject(N, "micros -> string", s"cast($microNtz as string)")
+ runProject(N, "nanos(9) -> string", s"cast($nanosNtz as string)")
+ runProject(N, "nanos(7) -> string", s"cast($nanosNtz7 as string)")
+ runProject(N, "micros -> nanos(9)", s"cast($microNtz as
timestamp_ntz(9))")
+ runProject(N, "nanos(9) -> micros", s"cast($nanosNtz as
timestamp_ntz)")
+ runProject(N, "nanos(9) -> nanos(7)", s"cast($nanosNtz as
timestamp_ntz(7))")
+ runProject(N, "micros -> date", s"cast($microNtz as date)")
+ runProject(N, "nanos(9) -> date", s"cast($nanosNtz as date)")
+ }
+
+ runBenchmark("Parse from string") {
+ val n = 1000000
+ runProject(n, "string(6 digits) -> micros", s"cast($microStr as
timestamp_ntz)")
+ runProject(n, "string(9 digits) -> nanos(9)", s"cast($nanosStr as
timestamp_ntz(9))")
+ runProject(n, "string(7 digits) -> nanos(7)", s"cast($nanosStr7 as
timestamp_ntz(7))")
+ }
+
+ runBenchmark("Hashing") {
+ runProject(N, "murmur3 hash(micros)", s"hash($microNtz)")
+ runProject(N, "murmur3 hash(nanos(9))", s"hash($nanosNtz)")
+ runProject(N, "xxhash64(micros)", s"xxhash64($microNtz)")
+ runProject(N, "xxhash64(nanos(9))", s"xxhash64($nanosNtz)")
+ }
+
+ runBenchmark("Sort (global ORDER BY)") {
+ runOnColumn(N, "order by micros", microNtz)(_.orderBy("ts").noop())
+ runOnColumn(N, "order by nanos(9)", nanosNtz)(_.orderBy("ts").noop())
+ }
+
+ runBenchmark("Group-by aggregation (~1M distinct keys)") {
+ val microKey = "timestamp_seconds(id % 1000000)"
+ val nanosKey = "timestamp_nanos((id % 1000000) * 1000000000 + (id %
1000000) % 1000)"
+ runOnColumn(N, "group by micros key",
microKey)(_.groupBy("ts").count().noop())
+ runOnColumn(N, "group by nanos(9) key",
nanosKey)(_.groupBy("ts").count().noop())
+ }
+
+ runBenchmark("MIN/MAX aggregation") {
+ runProject(N, "min/max micros", s"min($microNtz) as mn",
s"max($microNtz) as mx")
+ runProject(N, "min/max nanos(9)", s"min($nanosNtz) as mn",
s"max($nanosNtz) as mx")
+ }
+
+ runBenchmark("Datetime functions") {
+ runProject(N, "hour(micros)", s"hour($microLtz)")
+ runProject(N, "hour(nanos(9))", s"hour($nanosLtz)")
+ runProject(N, "second(micros)", s"second($microLtz)")
+ runProject(N, "second(nanos(9))", s"second($nanosLtz)")
+ runProject(N, "extract(SECOND, micros)", s"extract(SECOND from
$microLtz)")
+ runProject(N, "extract(SECOND, nanos(9))", s"extract(SECOND from
$nanosLtz)")
+ // unix_nanos is nanos-only (requires p in [7,9]); the micro analog
is unix_micros.
+ runProject(N, "unix_micros(micros)", s"unix_micros($microLtz)")
+ runProject(N, "unix_nanos(nanos(9))", s"unix_nanos($nanosLtz)")
+ runProject(N, "micros + interval 1 hour", s"$microLtz + interval 1
hour")
+ runProject(N, "nanos(9) + interval 1 hour", s"$nanosLtz + interval 1
hour")
+ }
+
+ runBenchmark("Parquet write/read") {
+ withTempPath { path =>
+ val microPath = s"${path.getAbsolutePath}/micros"
+ val nanosPath = s"${path.getAbsolutePath}/nanos"
+
+ val write = new Benchmark("Save to Parquet", N, output = output)
+ write.addCase("micros (TIMESTAMP_NTZ)", 1) { _ =>
+ spark.range(N).selectExpr(s"$microNtz as ts")
+ .write.mode("overwrite").format("parquet").save(microPath)
+ }
+ write.addCase("nanos(9) (TIMESTAMP_NTZ(9))", 1) { _ =>
+ spark.range(N).selectExpr(s"$nanosNtz as ts")
+ .write.mode("overwrite").format("parquet").save(nanosPath)
+ }
+ write.run()
+
+ val read = new Benchmark("Load from Parquet", N, output = output)
+ Seq(true, false).foreach { vec =>
+ read.addCase(s"micros, vectorized ${if (vec) "on" else "off"}",
3) { _ =>
+ withSQLConf(SQLConf.PARQUET_VECTORIZED_READER_ENABLED.key ->
vec.toString) {
+ spark.read.format("parquet").load(microPath).noop()
+ }
+ }
+ }
+ // For nanos, vectorized read is disabled internally regardless of
the flag - running
+ // both documents that there is no vectorized fast path yet.
+ Seq(true, false).foreach { vec =>
+ val name =
+ s"nanos(9), vectorized ${if (vec) "on" else "off"} (row-based)"
+ read.addCase(name, 3) { _ =>
+ withSQLConf(SQLConf.PARQUET_VECTORIZED_READER_ENABLED.key ->
vec.toString) {
+ spark.read.format("parquet").load(nanosPath).noop()
+ }
+ }
+ }
+ read.run()
+ }
+ }
+
+ runBenchmark("ORC write/read") {
+ withTempPath { path =>
+ val microPath = s"${path.getAbsolutePath}/micros"
+ val nanosPath = s"${path.getAbsolutePath}/nanos"
+
+ val write = new Benchmark("Save to ORC", N, output = output)
+ write.addCase("micros (TIMESTAMP_NTZ)", 1) { _ =>
+ spark.range(N).selectExpr(s"$microNtz as ts")
+ .write.mode("overwrite").format("orc").save(microPath)
+ }
+ write.addCase("nanos(9) (TIMESTAMP_NTZ(9))", 1) { _ =>
+ spark.range(N).selectExpr(s"$nanosNtz as ts")
+ .write.mode("overwrite").format("orc").save(nanosPath)
+ }
+ write.run()
+
+ val read = new Benchmark("Load from ORC", N, output = output)
+ Seq(true, false).foreach { vec =>
+ read.addCase(s"micros, vectorized ${if (vec) "on" else "off"}",
3) { _ =>
+ withSQLConf(SQLConf.ORC_VECTORIZED_READER_ENABLED.key ->
vec.toString) {
+ spark.read.format("orc").load(microPath).noop()
+ }
+ }
+ }
+ Seq(true, false).foreach { vec =>
+ read.addCase(s"nanos(9), vectorized ${if (vec) "on" else
"off"}", 3) { _ =>
+ withSQLConf(SQLConf.ORC_VECTORIZED_READER_ENABLED.key ->
vec.toString) {
+ spark.read.format("orc").load(nanosPath).noop()
+ }
+ }
+ }
+ read.run()
+ }
+ }
+ }
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]