Skip to content

perf: optimize map_sort singleton normalization (18x faster) - #5887

Merged
andygrove merged 2 commits into
apache:mainfrom
viirya:codex/map-sort-singleton-normalization
Sep 13, 2026
Merged

perf: optimize map_sort singleton normalization (18x faster)#5887
andygrove merged 2 commits into
apache:mainfrom
viirya:codex/map-sort-singleton-normalization

Conversation

@viirya

@viirya viirya commented Sep 12, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

Closes #5886. Follow-up to #5822.

Rationale for this change

A singleton map already has its only possible ordering, but normalization still creates a key slice and invokes Arrow sorting for every row. Avoiding that work speeds up matched singleton normalization by 19–22x in the full measurement and 18.4x in an independent forward-order confirmation; normalization plus hashing improves by 13.84x in the full forward singleton case.

What changes are included in this PR?

Skip per-row sort dispatch only for supported flat singleton key types. A batch check chooses a const-specialized loop so wide maps retain a fallback without the added per-row singleton branch. Keep nested and unsupported types on Arrow's validation path, including singleton errors. Preserve global permutation/take, rebased offsets, physical entries beneath null maps, schema metadata, and existing early returns. Hashing implementation and nested-hashing defaults are unchanged.

Extend the shared matched benchmark harness with null, sliced, mixed-cardinality, long Unicode, empty, singleton, and unsupported-key cases. Record the local performance audit.

How are these changes tested?

The unchanged nine map-sort tests and three new tests pass (12 total), covering manually expected permutations, exact values, schema metadata, null buffers, nonzero offsets, nested keys, unsupported errors, NaN payloads, signed zero, and binary keys. All 11 Murmur3 tests pass. Both benchmark smoke runs pass (92 cases). Full make succeeds (native and JVM packaging; JVM tests skipped by the build target). Workspace all-target Clippy passes with warnings denied, and Rust formatting passes.

Benchmark environment: Apple M4 Max, macOS 26.6.2, Rust 1.97.1, thin LTO, one codegen unit, DataFusion 55.0.0. Both sides use commit 3810936 plus identical benchmark additions and the same dependency lock (SHA-256 02a7248aa329e1f98c7cce0ae921601ca66cefae66dd2c08631c98d3ae8337cb); only the optimized side includes the production change. These are microbenchmarks, not end-to-end Spark query speedups.

Criterion: 8192 visible rows, seed 42, 100 samples, 3-second warmup, 5-second requested measurement, 95% confidence, sequential execution with no concurrent builds. Input construction and checks and hash-buffer allocation are untimed; seed reset, normalization allocation/drop, and combined struct construction/drop are timed. No differences between stage or process timings are presented as exact cost attribution. The unsupported-key benchmark fails at the first row and is not batch throughput.

All 92 cases were measured. Fourteen initial regression flags were independently remeasured. The three residual approximately 1.4% flags were measured again with execution order reversed: hash-only directions reversed and the combined case returned within the 1% noise threshold. No stable meaningful regression was observed; confidence intervals within a process do not cover machine drift. All initial flags remain visible below.

All 92 cases: timings and 95% confidence intervals

Timings use Criterion's slope estimate when available, otherwise its mean. Speedup is the ratio of those point estimates. Regression confirmation separately uses Criterion's relative mean change, so its percentages need not equal the slope ratio.

Case Baseline µs (95% CI) Optimized µs (95% CI) Speedup
matched_maps/hash_buffer_seed_reset 0.263 [0.260, 0.267] 0.269 [0.264, 0.273] 0.98x
matched_maps/hash_only/map/0/forward 4.358 [4.346, 4.372] 4.373 [4.363, 4.384] 1.00x
matched_maps/hash_only/map/0/reversed 4.350 [4.335, 4.366] 4.389 [4.376, 4.401] 0.99x
matched_maps/hash_only/map/10/forward 307.551 [306.709, 308.443] 313.874 [312.767, 314.991] 0.98x
matched_maps/hash_only/map/10/reversed 308.392 [307.098, 309.777] 313.881 [312.468, 315.257] 0.98x
matched_maps/hash_only/map/1/forward 28.939 [28.854, 29.035] 28.866 [28.791, 28.964] 1.00x
matched_maps/hash_only/map/1/reversed 28.979 [28.941, 29.024] 28.599 [28.577, 28.627] 1.01x
matched_maps/hash_only/map/50/forward 1982.022 [1978.217, 1985.983] 2009.726 [2004.212, 2015.929] 0.99x
matched_maps/hash_only/map/50/reversed 1995.254 [1989.496, 2002.549] 2009.241 [2004.659, 2014.103] 0.99x
matched_maps/hash_only/struct_map_int/0/forward 11.023 [10.966, 11.085] 10.919 [10.898, 10.942] 1.01x
matched_maps/hash_only/struct_map_int/0/reversed 10.914 [10.879, 10.952] 10.934 [10.916, 10.954] 1.00x
matched_maps/hash_only/struct_map_int/10/forward 314.182 [313.506, 314.853] 320.542 [319.750, 321.316] 0.98x
matched_maps/hash_only/struct_map_int/10/reversed 314.365 [313.710, 315.004] 320.435 [319.247, 321.802] 0.98x
matched_maps/hash_only/struct_map_int/1/forward 35.606 [35.519, 35.765] 35.491 [35.460, 35.520] 1.00x
matched_maps/hash_only/struct_map_int/1/reversed 35.606 [35.509, 35.725] 35.529 [35.466, 35.608] 1.00x
matched_maps/hash_only/struct_map_int/50/forward 2009.917 [2003.161, 2017.124] 2038.874 [2030.470, 2047.787] 0.99x
matched_maps/hash_only/struct_map_int/50/reversed 2003.698 [1999.940, 2007.498] 2013.819 [2009.619, 2017.996] 0.99x
matched_maps/normalize_hash/map/0/forward 13.266 [13.231, 13.303] 11.371 [11.335, 11.406] 1.17x
matched_maps/normalize_hash/map/0/reversed 13.214 [13.194, 13.233] 11.267 [11.247, 11.291] 1.17x
matched_maps/normalize_hash/map/10/forward 1996.807 [1987.359, 2008.286] 2031.503 [2026.964, 2036.142] 0.98x
matched_maps/normalize_hash/map/10/reversed 2340.178 [2336.594, 2343.801] 2217.325 [2210.136, 2225.140] 1.06x
matched_maps/normalize_hash/map/1/forward 1062.828 [1060.699, 1065.016] 76.774 [76.197, 77.396] 13.84x
matched_maps/normalize_hash/map/1/reversed 1068.415 [1064.285, 1073.914] 76.439 [76.270, 76.614] 13.98x
matched_maps/normalize_hash/map/50/forward 5078.149 [5068.935, 5087.245] 5188.890 [5171.902, 5207.781] 0.98x
matched_maps/normalize_hash/map/50/reversed 5761.625 [5748.137, 5775.915] 5628.312 [5618.579, 5638.041] 1.02x
matched_maps/normalize_hash/struct_map_int/0/forward 21.526 [21.298, 21.741] 18.924 [18.892, 18.958] 1.14x
matched_maps/normalize_hash/struct_map_int/0/reversed 21.047 [20.927, 21.187] 19.045 [18.988, 19.106] 1.11x
matched_maps/normalize_hash/struct_map_int/10/forward 1834.056 [1821.423, 1848.331] 2072.182 [2066.520, 2078.047] 0.89x
matched_maps/normalize_hash/struct_map_int/10/reversed 2177.800 [2174.655, 2181.225] 2233.399 [2225.831, 2242.952] 0.98x
matched_maps/normalize_hash/struct_map_int/1/forward 888.911 [887.435, 890.550] 84.048 [83.901, 84.205] 10.58x
matched_maps/normalize_hash/struct_map_int/1/reversed 888.614 [887.431, 889.746] 84.509 [84.085, 85.038] 10.51x
matched_maps/normalize_hash/struct_map_int/50/forward 4904.439 [4894.428, 4914.384] 5189.399 [5179.957, 5198.630] 0.95x
matched_maps/normalize_hash/struct_map_int/50/reversed 5596.028 [5585.908, 5606.392] 5650.289 [5638.447, 5662.645] 0.99x
matched_maps/normalize_only/map/0/forward 8.697 [8.682, 8.713] 6.963 [6.936, 6.989] 1.25x
matched_maps/normalize_only/map/0/reversed 8.727 [8.677, 8.788] 6.950 [6.921, 6.979] 1.26x
matched_maps/normalize_only/map/10/forward 1520.334 [1503.240, 1538.826] 1387.357 [1384.957, 1389.861] 1.10x
matched_maps/normalize_only/map/10/reversed 1786.607 [1783.197, 1790.003] 1802.371 [1796.599, 1809.541] 0.99x
matched_maps/normalize_only/map/1/forward 1016.637 [1012.577, 1020.968] 45.606 [45.437, 45.781] 22.29x
matched_maps/normalize_only/map/1/reversed 881.932 [880.571, 883.312] 45.788 [45.579, 45.997] 19.26x
matched_maps/normalize_only/map/50/forward 2917.830 [2910.339, 2925.296] 2786.641 [2780.691, 2793.075] 1.05x
matched_maps/normalize_only/map/50/reversed 3627.696 [3612.163, 3644.130] 3494.888 [3490.076, 3499.865] 1.04x
regression_maps/hash_only/map/mixed_dense_null 530.397 [529.235, 531.609] 535.249 [533.267, 537.235] 0.99x
regression_maps/hash_only/map/mixed_long_unicode_dense_null 33090.461 [33024.790, 33158.383] 33449.208 [33375.787, 33527.887] 0.99x
regression_maps/hash_only/map/mixed_no_null 883.493 [881.264, 886.035] 890.820 [888.152, 893.783] 0.99x
regression_maps/hash_only/map/mixed_sparse_null 898.180 [896.128, 900.272] 908.005 [904.434, 912.382] 0.99x
regression_maps/hash_only/map/singleton_dense_null 11.593 [11.548, 11.651] 11.970 [11.959, 11.982] 0.97x
regression_maps/hash_only/map/singleton_long_unicode 4819.593 [4808.020, 4831.923] 4959.078 [4919.150, 5001.795] 0.97x
regression_maps/hash_only/map/singleton_no_null 31.615 [31.594, 31.637] 30.619 [30.584, 30.665] 1.03x
regression_maps/hash_only/map/singleton_sparse_null 39.444 [39.428, 39.461] 39.415 [39.316, 39.545] 1.00x
regression_maps/hash_only/struct_map_int/mixed_dense_null 540.584 [539.145, 542.015] 542.068 [540.433, 543.738] 1.00x
regression_maps/hash_only/struct_map_int/mixed_long_unicode_dense_null 33142.289 [33075.041, 33210.295] 33442.414 [33367.520, 33518.977] 0.99x
regression_maps/hash_only/struct_map_int/mixed_no_null 895.356 [892.053, 898.849] 896.652 [894.612, 899.170] 1.00x
regression_maps/hash_only/struct_map_int/mixed_sparse_null 904.901 [902.708, 907.069] 912.547 [910.803, 914.183] 0.99x
regression_maps/hash_only/struct_map_int/singleton_dense_null 19.346 [19.325, 19.370] 19.914 [19.858, 19.976] 0.97x
regression_maps/hash_only/struct_map_int/singleton_long_unicode 4870.237 [4856.345, 4884.560] 4895.789 [4878.665, 4913.516] 0.99x
regression_maps/hash_only/struct_map_int/singleton_no_null 38.589 [38.441, 38.761] 38.518 [38.480, 38.565] 1.00x
regression_maps/hash_only/struct_map_int/singleton_sparse_null 46.393 [46.370, 46.419] 46.062 [45.977, 46.205] 1.01x
regression_maps/normalize_hash/map/mixed_dense_null 2143.014 [2140.629, 2145.506] 1847.420 [1844.266, 1850.881] 1.16x
regression_maps/normalize_hash/map/mixed_long_unicode_dense_null 44150.836 [43946.110, 44387.846] 43751.794 [43651.367, 43857.605] 1.01x
regression_maps/normalize_hash/map/mixed_no_null 2506.559 [2500.153, 2513.648] 2196.272 [2190.218, 2202.485] 1.14x
regression_maps/normalize_hash/map/mixed_sparse_null 2814.428 [2810.473, 2818.450] 2630.843 [2617.479, 2644.789] 1.07x
regression_maps/normalize_hash/map/singleton_dense_null 889.032 [887.028, 891.237] 79.558 [79.458, 79.670] 11.17x
regression_maps/normalize_hash/map/singleton_long_unicode 6490.904 [6470.765, 6514.549] 5309.598 [5290.480, 5330.968] 1.22x
regression_maps/normalize_hash/map/singleton_no_null 896.903 [895.415, 898.591] 94.169 [93.963, 94.382] 9.52x
regression_maps/normalize_hash/map/singleton_sparse_null 945.445 [939.856, 952.355] 130.696 [130.506, 130.913] 7.23x
regression_maps/normalize_hash/struct_map_int/mixed_dense_null 2156.043 [2151.440, 2161.354] 1847.514 [1844.581, 1850.543] 1.17x
regression_maps/normalize_hash/struct_map_int/mixed_long_unicode_dense_null 43885.701 [43779.178, 44000.591] 43625.097 [43557.909, 43690.529] 1.01x
regression_maps/normalize_hash/struct_map_int/mixed_no_null 2502.840 [2498.547, 2507.360] 2178.861 [2173.722, 2184.473] 1.15x
regression_maps/normalize_hash/struct_map_int/mixed_sparse_null 2818.518 [2813.588, 2823.423] 2577.222 [2572.445, 2582.045] 1.09x
regression_maps/normalize_hash/struct_map_int/singleton_dense_null 887.476 [886.410, 888.552] 88.842 [87.909, 89.915] 9.99x
regression_maps/normalize_hash/struct_map_int/singleton_long_unicode 6521.820 [6500.257, 6544.674] 5299.202 [5287.395, 5310.817] 1.23x
regression_maps/normalize_hash/struct_map_int/singleton_no_null 910.014 [908.170, 911.990] 103.197 [102.782, 103.656] 8.82x
regression_maps/normalize_hash/struct_map_int/singleton_sparse_null 943.194 [941.695, 944.842] 138.875 [138.372, 139.494] 6.79x
regression_maps/normalize_only/map/mixed_dense_null 1560.851 [1556.181, 1566.411] 1226.211 [1224.168, 1228.166] 1.27x
regression_maps/normalize_only/map/mixed_long_unicode_dense_null 9756.407 [9737.301, 9777.119] 9595.223 [9552.448, 9642.472] 1.02x
regression_maps/normalize_only/map/mixed_no_null 1551.304 [1546.411, 1557.510] 1213.289 [1208.616, 1218.532] 1.28x
regression_maps/normalize_only/map/mixed_sparse_null 1969.415 [1963.950, 1975.033] 1521.928 [1517.531, 1527.180] 1.29x
regression_maps/normalize_only/map/singleton_dense_null 996.014 [993.343, 998.993] 65.240 [65.004, 65.491] 15.27x
regression_maps/normalize_only/map/singleton_long_unicode 1494.588 [1491.623, 1497.603] 371.395 [370.676, 372.253] 4.02x
regression_maps/normalize_only/map/singleton_no_null 988.289 [986.724, 989.873] 61.800 [61.587, 62.065] 15.99x
regression_maps/normalize_only/map/singleton_sparse_null 945.525 [938.139, 952.676] 91.335 [91.065, 91.609] 10.35x
spark_map_sort/int_keys/0 8.783 [8.765, 8.801] 6.842 [6.815, 6.870] 1.28x
spark_map_sort/int_keys/1 1085.181 [1076.105, 1096.393] 17.926 [17.800, 18.044] 60.54x
spark_map_sort/int_keys/16 1526.663 [1523.958, 1529.606] 1416.724 [1414.780, 1418.604] 1.08x
spark_map_sort/int_keys/4 1086.943 [1081.799, 1092.166] 912.810 [908.096, 917.935] 1.19x
spark_map_sort/int_keys/64 2063.836 [2049.778, 2079.561] 1780.854 [1776.659, 1785.195] 1.16x
spark_map_sort/string_keys/0 8.812 [8.792, 8.831] 6.943 [6.919, 6.966] 1.27x
spark_map_sort/string_keys/1 905.248 [902.262, 909.163] 35.440 [35.327, 35.551] 25.54x
spark_map_sort/string_keys/16 3990.175 [3984.497, 3996.183] 3968.998 [3962.344, 3976.562] 1.01x
spark_map_sort/string_keys/4 1110.114 [1107.135, 1113.329] 1107.243 [1104.536, 1110.209] 1.00x
spark_map_sort/string_keys/64 4228.212 [4222.093, 4234.320] 4179.843 [4174.472, 4185.768] 1.01x
spark_map_sort/unsupported_singleton_struct_key 0.422 [0.421, 0.423] 0.417 [0.416, 0.418] 1.01x
Independent and reversed-order confirmation

Positive means optimized is slower. Reverse-order estimates are reciprocally converted to the same direction.

Case Full run change (95% CI) Independent paired change Reversed-order change
matched_maps/hash_only/map/0/reversed +1.52% [+1.01, +2.03] +0.31% [+0.02, +0.59]
matched_maps/hash_only/map/1/forward -0.60% [-1.01, -0.19] -4.14% [-4.27, -4.01] -0.28% [-0.47, -0.11]
matched_maps/hash_only/map/10/forward +1.92% [+1.49, +2.33] +1.42% [+1.06, +1.77] -1.40% [-1.84, -0.98]
matched_maps/hash_only/map/50/forward +1.40% [+1.07, +1.76] +1.14% [+0.81, +1.49]
matched_maps/hash_only/struct_map_int/10/forward +2.07% [+1.70, +2.46] +1.37% [+1.05, +1.68] -0.95% [-1.32, -0.61]
matched_maps/hash_only/struct_map_int/10/reversed +1.73% [+1.35, +2.11] -0.56% [-1.40, +0.21]
matched_maps/normalize_hash/map/10/forward +1.74% [+1.12, +2.28] -1.32% [-1.59, -1.04]
matched_maps/normalize_hash/map/50/forward +2.18% [+1.79, +2.59] -10.15% [-10.56, -9.75]
matched_maps/normalize_hash/struct_map_int/10/forward +13.03% [+12.36, +13.68] -2.76% [-3.27, -2.19]
matched_maps/normalize_hash/struct_map_int/10/reversed +2.55% [+2.16, +2.99] +1.40% [+1.08, +1.80] +0.34% [-0.17, +0.84]
matched_maps/normalize_hash/struct_map_int/50/forward +5.81% [+5.53, +6.09] -9.62% [-10.11, -9.09]
matched_maps/normalize_only/map/0/forward -19.94% [-20.29, -19.62] -21.05% [-21.28, -20.83]
matched_maps/normalize_only/map/1/forward -95.50% [-95.53, -95.48] -94.56% [-94.57, -94.55]
regression_maps/hash_only/map/mixed_sparse_null +1.62% [+1.18, +2.04] +0.11% [-0.27, +0.48]
regression_maps/hash_only/map/singleton_dense_null +2.15% [+1.28, +2.95] -1.47% [-2.01, -0.95]
regression_maps/hash_only/map/singleton_long_unicode +2.89% [+2.02, +3.86] +0.63% [+0.26, +1.04]
regression_maps/hash_only/struct_map_int/singleton_dense_null +2.21% [+1.87, +2.54] -2.27% [-3.15, -1.38]
Criterion timing and change output for all 92 cases
spark_map_sort/int_keys/0
                        time:   [6.8147 µs 6.8419 µs 6.8697 µs]
                        change: [−22.519% −22.206% −21.855%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/string_keys/0
                        time:   [6.9187 µs 6.9425 µs 6.9657 µs]
                        change: [−21.642% −21.356% −21.065%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/int_keys/1
                        time:   [17.800 µs 17.926 µs 18.044 µs]
                        change: [−98.363% −98.352% −98.339%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/string_keys/1
                        time:   [35.327 µs 35.440 µs 35.551 µs]
                        change: [−96.136% −96.107% −96.083%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/int_keys/4
                        time:   [908.10 µs 912.81 µs 917.94 µs]
                        change: [−16.427% −16.053% −15.641%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/string_keys/4
                        time:   [1.1045 ms 1.1072 ms 1.1102 ms]
                        change: [−0.2600% +0.1169% +0.4541%] (p = 0.53 > 0.05)
                        No change in performance detected.
spark_map_sort/int_keys/16
                        time:   [1.4148 ms 1.4167 ms 1.4186 ms]
                        change: [−7.3766% −7.1542% −6.9343%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/string_keys/16
                        time:   [3.9623 ms 3.9690 ms 3.9766 ms]
                        change: [−0.7597% −0.5307% −0.2946%] (p = 0.00 < 0.05)
                        Change within noise threshold.
spark_map_sort/int_keys/64
                        time:   [1.7767 ms 1.7809 ms 1.7852 ms]
                        change: [−14.630% −13.943% −13.324%] (p = 0.00 < 0.05)
                        Performance has improved.
spark_map_sort/string_keys/64
                        time:   [4.1745 ms 4.1798 ms 4.1858 ms]
                        change: [−1.3342% −1.1439% −0.9527%] (p = 0.00 < 0.05)
                        Change within noise threshold.
matched_maps/normalize_only/map/0/forward
                        time:   [6.9362 µs 6.9631 µs 6.9888 µs]
                        thrpt:  [1.1722 Gelem/s 1.1765 Gelem/s 1.1811 Gelem/s]
                 change:
                        time:   [−20.291% −19.945% −19.622%] (p = 0.00 < 0.05)
                        thrpt:  [+24.412% +24.914% +25.457%]
                        Performance has improved.
matched_maps/normalize_only/map/0/reversed
                        time:   [6.9208 µs 6.9498 µs 6.9792 µs]
                        thrpt:  [1.1738 Gelem/s 1.1787 Gelem/s 1.1837 Gelem/s]
                 change:
                        time:   [−24.630% −23.352% −22.138%] (p = 0.00 < 0.05)
                        thrpt:  [+28.432% +30.466% +32.679%]
                        Performance has improved.
matched_maps/normalize_only/map/1/forward
                        time:   [45.437 µs 45.606 µs 45.781 µs]
                        thrpt:  [178.94 Melem/s 179.63 Melem/s 180.29 Melem/s]
                 change:
                        time:   [−95.526% −95.504% −95.483%] (p = 0.00 < 0.05)
                        thrpt:  [+2114.1% +2124.4% +2135.1%]
                        Performance has improved.
matched_maps/normalize_only/map/1/reversed
                        time:   [45.579 µs 45.788 µs 45.997 µs]
                        thrpt:  [178.10 Melem/s 178.91 Melem/s 179.73 Melem/s]
                 change:
                        time:   [−94.840% −94.822% −94.804%] (p = 0.00 < 0.05)
                        thrpt:  [+1824.7% +1831.4% +1837.8%]
                        Performance has improved.
matched_maps/normalize_only/map/10/forward
                        time:   [1.3850 ms 1.3874 ms 1.3899 ms]
                        thrpt:  [5.8941 Melem/s 5.9048 Melem/s 5.9150 Melem/s]
                 change:
                        time:   [−7.6408% −6.9597% −6.2938%] (p = 0.00 < 0.05)
                        thrpt:  [+6.7165% +7.4803% +8.2730%]
                        Performance has improved.
matched_maps/normalize_only/map/10/reversed
                        time:   [1.7966 ms 1.8024 ms 1.8095 ms]
                        thrpt:  [4.5271 Melem/s 4.5451 Melem/s 4.5597 Melem/s]
                 change:
                        time:   [+0.4224% +0.8417% +1.2717%] (p = 0.00 < 0.05)
                        thrpt:  [−1.2558% −0.8347% −0.4206%]
                        Change within noise threshold.
matched_maps/normalize_only/map/50/forward
                        time:   [2.7807 ms 2.7866 ms 2.7931 ms]
                        thrpt:  [2.9330 Melem/s 2.9397 Melem/s 2.9460 Melem/s]
                 change:
                        time:   [−4.8238% −4.4961% −4.1897%] (p = 0.00 < 0.05)
                        thrpt:  [+4.3729% +4.7078% +5.0683%]
                        Performance has improved.
matched_maps/normalize_only/map/50/reversed
                        time:   [3.4901 ms 3.4949 ms 3.4999 ms]
                        thrpt:  [2.3407 Melem/s 2.3440 Melem/s 2.3472 Melem/s]
                 change:
                        time:   [−4.1187% −3.6609% −3.2247%] (p = 0.00 < 0.05)
                        thrpt:  [+3.3322% +3.8001% +4.2957%]
                        Performance has improved.
regression_maps/normalize_only/map/singleton_no_null
                        time:   [61.587 µs 61.800 µs 62.065 µs]
                        thrpt:  [131.99 Melem/s 132.56 Melem/s 133.01 Melem/s]
                 change:
                        time:   [−93.727% −93.694% −93.653%] (p = 0.00 < 0.05)
                        thrpt:  [+1475.5% +1485.9% +1494.3%]
                        Performance has improved.
regression_maps/normalize_only/map/singleton_sparse_null
                        time:   [91.065 µs 91.335 µs 91.609 µs]
                        thrpt:  [89.424 Melem/s 89.692 Melem/s 89.958 Melem/s]
                 change:
                        time:   [−90.225% −90.176% −90.127%] (p = 0.00 < 0.05)
                        thrpt:  [+912.84% +917.95% +923.03%]
                        Performance has improved.
regression_maps/normalize_only/map/singleton_dense_null
                        time:   [65.004 µs 65.240 µs 65.491 µs]
                        thrpt:  [125.09 Melem/s 125.57 Melem/s 126.02 Melem/s]
                 change:
                        time:   [−93.502% −93.478% −93.453%] (p = 0.00 < 0.05)
                        thrpt:  [+1427.3% +1433.4% +1439.0%]
                        Performance has improved.
regression_maps/normalize_only/map/mixed_no_null
                        time:   [1.2086 ms 1.2133 ms 1.2185 ms]
                        thrpt:  [6.7228 Melem/s 6.7519 Melem/s 6.7780 Melem/s]
                 change:
                        time:   [−22.378% −22.063% −21.720%] (p = 0.00 < 0.05)
                        thrpt:  [+27.746% +28.309% +28.830%]
                        Performance has improved.
regression_maps/normalize_only/map/mixed_sparse_null
                        time:   [1.5175 ms 1.5219 ms 1.5272 ms]
                        thrpt:  [5.3641 Melem/s 5.3826 Melem/s 5.3982 Melem/s]
                 change:
                        time:   [−23.079% −22.818% −22.577%] (p = 0.00 < 0.05)
                        thrpt:  [+29.161% +29.564% +30.003%]
                        Performance has improved.
regression_maps/normalize_only/map/mixed_dense_null
                        time:   [1.2242 ms 1.2262 ms 1.2282 ms]
                        thrpt:  [6.6701 Melem/s 6.6807 Melem/s 6.6919 Melem/s]
                 change:
                        time:   [−21.873% −21.625% −21.382%] (p = 0.00 < 0.05)
                        thrpt:  [+27.197% +27.592% +27.997%]
                        Performance has improved.
regression_maps/normalize_only/map/singleton_long_unicode
                        time:   [370.68 µs 371.39 µs 372.25 µs]
                        thrpt:  [22.007 Melem/s 22.057 Melem/s 22.100 Melem/s]
                 change:
                        time:   [−75.178% −75.120% −75.058%] (p = 0.00 < 0.05)
                        thrpt:  [+300.93% +301.93% +302.87%]
                        Performance has improved.
regression_maps/normalize_only/map/mixed_long_unicode_dense_null
                        time:   [9.5524 ms 9.5952 ms 9.6425 ms]
                        thrpt:  [849.57 Kelem/s 853.76 Kelem/s 857.58 Kelem/s]
                 change:
                        time:   [−2.1073% −1.6521% −1.1734%] (p = 0.00 < 0.05)
                        thrpt:  [+1.1873% +1.6798% +2.1526%]
                        Performance has improved.
spark_map_sort/unsupported_singleton_struct_key
                        time:   [415.54 ns 416.78 ns 418.19 ns]
                        change: [−1.8345% −1.4841% −1.1239%] (p = 0.00 < 0.05)
                        Performance has improved.
matched_maps/hash_only/map/0/forward
                        time:   [4.3630 µs 4.3731 µs 4.3836 µs]
                        thrpt:  [1.8688 Gelem/s 1.8732 Gelem/s 1.8776 Gelem/s]
                 change:
                        time:   [−0.7510% −0.3378% +0.0642%] (p = 0.11 > 0.05)
                        thrpt:  [−0.0641% +0.3390% +0.7567%]
                        No change in performance detected.
matched_maps/hash_only/struct_map_int/0/forward
                        time:   [10.898 µs 10.919 µs 10.942 µs]
                        thrpt:  [748.70 Melem/s 750.25 Melem/s 751.68 Melem/s]
                 change:
                        time:   [−1.2630% −0.8139% −0.3972%] (p = 0.00 < 0.05)
                        thrpt:  [+0.3988% +0.8206% +1.2792%]
                        Change within noise threshold.
matched_maps/hash_only/map/0/reversed
                        time:   [4.3764 µs 4.3885 µs 4.4012 µs]
                        thrpt:  [1.8613 Gelem/s 1.8667 Gelem/s 1.8718 Gelem/s]
                 change:
                        time:   [+1.0082% +1.5165% +2.0324%] (p = 0.00 < 0.05)
                        thrpt:  [−1.9920% −1.4938% −0.9981%]
                        Performance has regressed.
matched_maps/hash_only/struct_map_int/0/reversed
                        time:   [10.916 µs 10.934 µs 10.954 µs]
                        thrpt:  [747.87 Melem/s 749.22 Melem/s 750.47 Melem/s]
                 change:
                        time:   [−0.5759% −0.2119% +0.1258%] (p = 0.24 > 0.05)
                        thrpt:  [−0.1256% +0.2123% +0.5792%]
                        No change in performance detected.
matched_maps/hash_only/map/1/forward
                        time:   [28.791 µs 28.866 µs 28.964 µs]
                        thrpt:  [282.84 Melem/s 283.80 Melem/s 284.53 Melem/s]
                 change:
                        time:   [−1.0099% −0.5986% −0.1905%] (p = 0.01 < 0.05)
                        thrpt:  [+0.1909% +0.6022% +1.0203%]
                        Change within noise threshold.
matched_maps/hash_only/struct_map_int/1/forward
                        time:   [35.460 µs 35.491 µs 35.520 µs]
                        thrpt:  [230.63 Melem/s 230.82 Melem/s 231.02 Melem/s]
                 change:
                        time:   [−0.6842% −0.4241% −0.2207%] (p = 0.00 < 0.05)
                        thrpt:  [+0.2212% +0.4259% +0.6890%]
                        Change within noise threshold.
matched_maps/hash_only/map/1/reversed
                        time:   [28.577 µs 28.599 µs 28.627 µs]
                        thrpt:  [286.17 Melem/s 286.45 Melem/s 286.66 Melem/s]
                 change:
                        time:   [−1.3355% −1.1778% −1.0188%] (p = 0.00 < 0.05)
                        thrpt:  [+1.0293% +1.1918% +1.3536%]
                        Performance has improved.
matched_maps/hash_only/struct_map_int/1/reversed
                        time:   [35.466 µs 35.529 µs 35.608 µs]
                        thrpt:  [230.06 Melem/s 230.57 Melem/s 230.98 Melem/s]
                 change:
                        time:   [−0.4238% −0.0169% +0.4030%] (p = 0.94 > 0.05)
                        thrpt:  [−0.4014% +0.0169% +0.4256%]
                        No change in performance detected.
matched_maps/hash_only/map/10/forward
                        time:   [312.77 µs 313.87 µs 314.99 µs]
                        thrpt:  [26.007 Melem/s 26.100 Melem/s 26.192 Melem/s]
                 change:
                        time:   [+1.4922% +1.9201% +2.3298%] (p = 0.00 < 0.05)
                        thrpt:  [−2.2768% −1.8839% −1.4703%]
                        Performance has regressed.
matched_maps/hash_only/struct_map_int/10/forward
                        time:   [319.75 µs 320.54 µs 321.32 µs]
                        thrpt:  [25.495 Melem/s 25.557 Melem/s 25.620 Melem/s]
                 change:
                        time:   [+1.7003% +2.0730% +2.4640%] (p = 0.00 < 0.05)
                        thrpt:  [−2.4047% −2.0309% −1.6719%]
                        Performance has regressed.
matched_maps/hash_only/map/10/reversed
                        time:   [312.47 µs 313.88 µs 315.26 µs]
                        thrpt:  [25.985 Melem/s 26.099 Melem/s 26.217 Melem/s]
                 change:
                        time:   [+0.9883% +1.4449% +1.9337%] (p = 0.00 < 0.05)
                        thrpt:  [−1.8970% −1.4244% −0.9786%]
                        Change within noise threshold.
matched_maps/hash_only/struct_map_int/10/reversed
                        time:   [319.25 µs 320.43 µs 321.80 µs]
                        thrpt:  [25.457 Melem/s 25.565 Melem/s 25.660 Melem/s]
                 change:
                        time:   [+1.3452% +1.7288% +2.1095%] (p = 0.00 < 0.05)
                        thrpt:  [−2.0659% −1.6995% −1.3273%]
                        Performance has regressed.
matched_maps/hash_only/map/50/forward
                        time:   [2.0042 ms 2.0097 ms 2.0159 ms]
                        thrpt:  [4.0636 Melem/s 4.0762 Melem/s 4.0874 Melem/s]
                 change:
                        time:   [+1.0656% +1.3978% +1.7631%] (p = 0.00 < 0.05)
                        thrpt:  [−1.7326% −1.3785% −1.0544%]
                        Performance has regressed.
matched_maps/hash_only/struct_map_int/50/forward
                        time:   [2.0305 ms 2.0389 ms 2.0478 ms]
                        thrpt:  [4.0004 Melem/s 4.0179 Melem/s 4.0345 Melem/s]
                 change:
                        time:   [+0.8882% +1.4407% +2.0127%] (p = 0.00 < 0.05)
                        thrpt:  [−1.9730% −1.4202% −0.8804%]
                        Change within noise threshold.
matched_maps/hash_only/map/50/reversed
                        time:   [2.0047 ms 2.0092 ms 2.0141 ms]
                        thrpt:  [4.0673 Melem/s 4.0772 Melem/s 4.0865 Melem/s]
                 change:
                        time:   [+0.2653% +0.7010% +1.0815%] (p = 0.00 < 0.05)
                        thrpt:  [−1.0700% −0.6961% −0.2646%]
                        Change within noise threshold.
matched_maps/hash_only/struct_map_int/50/reversed
                        time:   [2.0096 ms 2.0138 ms 2.0180 ms]
                        thrpt:  [4.0595 Melem/s 4.0679 Melem/s 4.0764 Melem/s]
                 change:
                        time:   [+0.2235% +0.5051% +0.7960%] (p = 0.00 < 0.05)
                        thrpt:  [−0.7897% −0.5026% −0.2230%]
                        Change within noise threshold.
matched_maps/normalize_hash/map/0/forward
                        time:   [11.335 µs 11.371 µs 11.406 µs]
                        thrpt:  [718.23 Melem/s 720.45 Melem/s 722.73 Melem/s]
                 change:
                        time:   [−14.722% −14.457% −14.184%] (p = 0.00 < 0.05)
                        thrpt:  [+16.529% +16.900% +17.263%]
                        Performance has improved.
matched_maps/normalize_hash/struct_map_int/0/forward
                        time:   [18.892 µs 18.924 µs 18.958 µs]
                        thrpt:  [432.11 Melem/s 432.89 Melem/s 433.61 Melem/s]
                 change:
                        time:   [−10.748% −10.174% −9.6335%] (p = 0.00 < 0.05)
                        thrpt:  [+10.660% +11.327% +12.042%]
                        Performance has improved.
matched_maps/normalize_hash/map/0/reversed
                        time:   [11.247 µs 11.267 µs 11.291 µs]
                        thrpt:  [725.57 Melem/s 727.05 Melem/s 728.36 Melem/s]
                 change:
                        time:   [−14.686% −14.385% −14.116%] (p = 0.00 < 0.05)
                        thrpt:  [+16.436% +16.802% +17.214%]
                        Performance has improved.
matched_maps/normalize_hash/struct_map_int/0/reversed
                        time:   [18.988 µs 19.045 µs 19.106 µs]
                        thrpt:  [428.76 Melem/s 430.13 Melem/s 431.42 Melem/s]
                 change:
                        time:   [−9.5492% −9.1675% −8.8014%] (p = 0.00 < 0.05)
                        thrpt:  [+9.6508% +10.093% +10.557%]
                        Performance has improved.
matched_maps/normalize_hash/map/1/forward
                        time:   [76.197 µs 76.774 µs 77.396 µs]
                        thrpt:  [105.85 Melem/s 106.70 Melem/s 107.51 Melem/s]
                 change:
                        time:   [−92.851% −92.810% −92.767%] (p = 0.00 < 0.05)
                        thrpt:  [+1282.5% +1290.8% +1298.7%]
                        Performance has improved.
matched_maps/normalize_hash/struct_map_int/1/forward
                        time:   [83.901 µs 84.048 µs 84.205 µs]
                        thrpt:  [97.286 Melem/s 97.469 Melem/s 97.639 Melem/s]
                 change:
                        time:   [−90.597% −90.574% −90.551%] (p = 0.00 < 0.05)
                        thrpt:  [+958.27% +960.90% +963.50%]
                        Performance has improved.
matched_maps/normalize_hash/map/1/reversed
                        time:   [76.270 µs 76.439 µs 76.614 µs]
                        thrpt:  [106.93 Melem/s 107.17 Melem/s 107.41 Melem/s]
                 change:
                        time:   [−92.851% −92.827% −92.805%] (p = 0.00 < 0.05)
                        thrpt:  [+1289.9% +1294.1% +1298.8%]
                        Performance has improved.
matched_maps/normalize_hash/struct_map_int/1/reversed
                        time:   [84.085 µs 84.509 µs 85.038 µs]
                        thrpt:  [96.333 Melem/s 96.936 Melem/s 97.425 Melem/s]
                 change:
                        time:   [−90.526% −90.499% −90.474%] (p = 0.00 < 0.05)
                        thrpt:  [+949.79% +952.56% +955.49%]
                        Performance has improved.
matched_maps/normalize_hash/map/10/forward
                        time:   [2.0270 ms 2.0315 ms 2.0361 ms]
                        thrpt:  [4.0233 Melem/s 4.0325 Melem/s 4.0415 Melem/s]
                 change:
                        time:   [+1.1185% +1.7376% +2.2765%] (p = 0.00 < 0.05)
                        thrpt:  [−2.2259% −1.7079% −1.1061%]
                        Performance has regressed.
matched_maps/normalize_hash/struct_map_int/10/forward
                        time:   [2.0665 ms 2.0722 ms 2.0780 ms]
                        thrpt:  [3.9422 Melem/s 3.9533 Melem/s 3.9642 Melem/s]
                 change:
                        time:   [+12.364% +13.033% +13.678%] (p = 0.00 < 0.05)
                        thrpt:  [−12.032% −11.530% −11.004%]
                        Performance has regressed.
matched_maps/normalize_hash/map/10/reversed
                        time:   [2.2101 ms 2.2173 ms 2.2251 ms]
                        thrpt:  [3.6816 Melem/s 3.6945 Melem/s 3.7066 Melem/s]
                 change:
                        time:   [−5.5889% −5.2497% −4.8778%] (p = 0.00 < 0.05)
                        thrpt:  [+5.1279% +5.5406% +5.9198%]
                        Performance has improved.
matched_maps/normalize_hash/struct_map_int/10/reversed
                        time:   [2.2258 ms 2.2334 ms 2.2430 ms]
                        thrpt:  [3.6523 Melem/s 3.6680 Melem/s 3.6804 Melem/s]
                 change:
                        time:   [+2.1565% +2.5530% +2.9927%] (p = 0.00 < 0.05)
                        thrpt:  [−2.9057% −2.4895% −2.1110%]
                        Performance has regressed.
matched_maps/normalize_hash/map/50/forward
                        time:   [5.1719 ms 5.1889 ms 5.2078 ms]
                        thrpt:  [1.5730 Melem/s 1.5788 Melem/s 1.5839 Melem/s]
                 change:
                        time:   [+1.7916% +2.1807% +2.5893%] (p = 0.00 < 0.05)
                        thrpt:  [−2.5239% −2.1342% −1.7600%]
                        Performance has regressed.
matched_maps/normalize_hash/struct_map_int/50/forward
                        time:   [5.1800 ms 5.1894 ms 5.1986 ms]
                        thrpt:  [1.5758 Melem/s 1.5786 Melem/s 1.5815 Melem/s]
                 change:
                        time:   [+5.5253% +5.8102% +6.0946%] (p = 0.00 < 0.05)
                        thrpt:  [−5.7445% −5.4912% −5.2360%]
                        Performance has regressed.
matched_maps/normalize_hash/map/50/reversed
                        time:   [5.6186 ms 5.6283 ms 5.6380 ms]
                        thrpt:  [1.4530 Melem/s 1.4555 Melem/s 1.4580 Melem/s]
                 change:
                        time:   [−2.6039% −2.3138% −2.0319%] (p = 0.00 < 0.05)
                        thrpt:  [+2.0741% +2.3686% +2.6735%]
                        Performance has improved.
matched_maps/normalize_hash/struct_map_int/50/reversed
                        time:   [5.6384 ms 5.6503 ms 5.6626 ms]
                        thrpt:  [1.4467 Melem/s 1.4498 Melem/s 1.4529 Melem/s]
                 change:
                        time:   [+0.6854% +0.9696% +1.2704%] (p = 0.00 < 0.05)
                        thrpt:  [−1.2545% −0.9603% −0.6807%]
                        Change within noise threshold.
regression_maps/hash_only/map/singleton_no_null
                        time:   [30.584 µs 30.619 µs 30.665 µs]
                        thrpt:  [267.14 Melem/s 267.55 Melem/s 267.86 Melem/s]
                 change:
                        time:   [−3.3627% −3.2429% −3.1270%] (p = 0.00 < 0.05)
                        thrpt:  [+3.2279% +3.3516% +3.4797%]
                        Performance has improved.
regression_maps/hash_only/struct_map_int/singleton_no_null
                        time:   [38.480 µs 38.518 µs 38.565 µs]
                        thrpt:  [212.42 Melem/s 212.68 Melem/s 212.89 Melem/s]
                 change:
                        time:   [−0.3419% +0.0195% +0.3418%] (p = 0.91 > 0.05)
                        thrpt:  [−0.3407% −0.0195% +0.3430%]
                        No change in performance detected.
regression_maps/hash_only/map/singleton_sparse_null
                        time:   [39.316 µs 39.415 µs 39.545 µs]
                        thrpt:  [207.16 Melem/s 207.84 Melem/s 208.36 Melem/s]
                 change:
                        time:   [−0.3449% −0.0583% +0.2812%] (p = 0.71 > 0.05)
                        thrpt:  [−0.2804% +0.0583% +0.3460%]
                        No change in performance detected.
regression_maps/hash_only/struct_map_int/singleton_sparse_null
                        time:   [45.977 µs 46.062 µs 46.205 µs]
                        thrpt:  [177.30 Melem/s 177.85 Melem/s 178.18 Melem/s]
                 change:
                        time:   [−0.9629% −0.7866% −0.5607%] (p = 0.00 < 0.05)
                        thrpt:  [+0.5638% +0.7928% +0.9723%]
                        Change within noise threshold.
regression_maps/hash_only/map/singleton_dense_null
                        time:   [11.959 µs 11.970 µs 11.982 µs]
                        thrpt:  [683.68 Melem/s 684.39 Melem/s 685.00 Melem/s]
                 change:
                        time:   [+1.2826% +2.1471% +2.9539%] (p = 0.00 < 0.05)
                        thrpt:  [−2.8691% −2.1019% −1.2664%]
                        Performance has regressed.
regression_maps/hash_only/struct_map_int/singleton_dense_null
                        time:   [19.858 µs 19.914 µs 19.976 µs]
                        thrpt:  [410.09 Melem/s 411.36 Melem/s 412.53 Melem/s]
                 change:
                        time:   [+1.8669% +2.2146% +2.5411%] (p = 0.00 < 0.05)
                        thrpt:  [−2.4781% −2.1666% −1.8327%]
                        Performance has regressed.
regression_maps/hash_only/map/mixed_no_null
                        time:   [888.15 µs 890.82 µs 893.78 µs]
                        thrpt:  [9.1655 Melem/s 9.1960 Melem/s 9.2236 Melem/s]
                 change:
                        time:   [+0.0827% +0.4649% +0.8444%] (p = 0.02 < 0.05)
                        thrpt:  [−0.8374% −0.4628% −0.0827%]
                        Change within noise threshold.
regression_maps/hash_only/struct_map_int/mixed_no_null
                        time:   [894.61 µs 896.65 µs 899.17 µs]
                        thrpt:  [9.1106 Melem/s 9.1362 Melem/s 9.1570 Melem/s]
                 change:
                        time:   [−0.4492% −0.0878% +0.2901%] (p = 0.64 > 0.05)
                        thrpt:  [−0.2892% +0.0878% +0.4512%]
                        No change in performance detected.
regression_maps/hash_only/map/mixed_sparse_null
                        time:   [904.43 µs 908.01 µs 912.38 µs]
                        thrpt:  [8.9787 Melem/s 9.0220 Melem/s 9.0576 Melem/s]
                 change:
                        time:   [+1.1839% +1.6174% +2.0427%] (p = 0.00 < 0.05)
                        thrpt:  [−2.0018% −1.5917% −1.1701%]
                        Performance has regressed.
regression_maps/hash_only/struct_map_int/mixed_sparse_null
                        time:   [910.80 µs 912.55 µs 914.18 µs]
                        thrpt:  [8.9610 Melem/s 8.9771 Melem/s 8.9943 Melem/s]
                 change:
                        time:   [+0.4299% +0.7725% +1.1411%] (p = 0.00 < 0.05)
                        thrpt:  [−1.1283% −0.7666% −0.4280%]
                        Change within noise threshold.
regression_maps/hash_only/map/mixed_dense_null
                        time:   [533.27 µs 535.25 µs 537.24 µs]
                        thrpt:  [15.248 Melem/s 15.305 Melem/s 15.362 Melem/s]
                 change:
                        time:   [−0.2383% +0.1954% +0.6000%] (p = 0.38 > 0.05)
                        thrpt:  [−0.5964% −0.1950% +0.2389%]
                        No change in performance detected.
regression_maps/hash_only/struct_map_int/mixed_dense_null
                        time:   [540.43 µs 542.07 µs 543.74 µs]
                        thrpt:  [15.066 Melem/s 15.112 Melem/s 15.158 Melem/s]
                 change:
                        time:   [−0.3272% +0.0628% +0.4694%] (p = 0.76 > 0.05)
                        thrpt:  [−0.4673% −0.0627% +0.3283%]
                        No change in performance detected.
regression_maps/hash_only/map/singleton_long_unicode
                        time:   [4.9191 ms 4.9591 ms 5.0018 ms]
                        thrpt:  [1.6378 Melem/s 1.6519 Melem/s 1.6653 Melem/s]
                 change:
                        time:   [+2.0150% +2.8941% +3.8575%] (p = 0.00 < 0.05)
                        thrpt:  [−3.7142% −2.8127% −1.9752%]
                        Performance has regressed.
regression_maps/hash_only/struct_map_int/singleton_long_unicode
                        time:   [4.8787 ms 4.8958 ms 4.9135 ms]
                        thrpt:  [1.6672 Melem/s 1.6733 Melem/s 1.6791 Melem/s]
                 change:
                        time:   [+0.0654% +0.5247% +0.9977%] (p = 0.03 < 0.05)
                        thrpt:  [−0.9878% −0.5219% −0.0654%]
                        Change within noise threshold.
regression_maps/hash_only/map/mixed_long_unicode_dense_null
                        time:   [33.376 ms 33.449 ms 33.528 ms]
                        thrpt:  [244.33 Kelem/s 244.91 Kelem/s 245.45 Kelem/s]
                 change:
                        time:   [+0.7843% +1.0841% +1.3842%] (p = 0.00 < 0.05)
                        thrpt:  [−1.3653% −1.0725% −0.7782%]
                        Change within noise threshold.
regression_maps/hash_only/struct_map_int/mixed_long_unicode_dense_null
                        time:   [33.368 ms 33.442 ms 33.519 ms]
                        thrpt:  [244.40 Kelem/s 244.96 Kelem/s 245.51 Kelem/s]
                 change:
                        time:   [+0.5862% +0.9056% +1.2081%] (p = 0.00 < 0.05)
                        thrpt:  [−1.1937% −0.8974% −0.5828%]
                        Change within noise threshold.
regression_maps/normalize_hash/map/singleton_no_null
                        time:   [93.963 µs 94.169 µs 94.382 µs]
                        thrpt:  [86.796 Melem/s 86.992 Melem/s 87.183 Melem/s]
                 change:
                        time:   [−89.551% −89.513% −89.475%] (p = 0.00 < 0.05)
                        thrpt:  [+850.13% +853.52% +857.02%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/singleton_no_null
                        time:   [102.78 µs 103.20 µs 103.66 µs]
                        thrpt:  [79.030 Melem/s 79.382 Melem/s 79.703 Melem/s]
                 change:
                        time:   [−88.660% −88.608% −88.555%] (p = 0.00 < 0.05)
                        thrpt:  [+773.71% +777.81% +781.81%]
                        Performance has improved.
regression_maps/normalize_hash/map/singleton_sparse_null
                        time:   [130.51 µs 130.70 µs 130.91 µs]
                        thrpt:  [62.576 Melem/s 62.680 Melem/s 62.771 Melem/s]
                 change:
                        time:   [−86.158% −86.087% −86.020%] (p = 0.00 < 0.05)
                        thrpt:  [+615.32% +618.74% +622.41%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/singleton_sparse_null
                        time:   [138.37 µs 138.88 µs 139.49 µs]
                        thrpt:  [58.727 Melem/s 58.988 Melem/s 59.203 Melem/s]
                 change:
                        time:   [−85.360% −85.323% −85.279%] (p = 0.00 < 0.05)
                        thrpt:  [+579.32% +581.33% +583.08%]
                        Performance has improved.
regression_maps/normalize_hash/map/singleton_dense_null
                        time:   [79.458 µs 79.558 µs 79.670 µs]
                        thrpt:  [102.82 Melem/s 102.97 Melem/s 103.10 Melem/s]
                 change:
                        time:   [−91.091% −91.060% −91.033%] (p = 0.00 < 0.05)
                        thrpt:  [+1015.3% +1018.6% +1022.4%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/singleton_dense_null
                        time:   [87.909 µs 88.842 µs 89.915 µs]
                        thrpt:  [91.108 Melem/s 92.209 Melem/s 93.188 Melem/s]
                 change:
                        time:   [−90.147% −90.086% −90.024%] (p = 0.00 < 0.05)
                        thrpt:  [+902.37% +908.71% +914.91%]
                        Performance has improved.
regression_maps/normalize_hash/map/mixed_no_null
                        time:   [2.1902 ms 2.1963 ms 2.2025 ms]
                        thrpt:  [3.7194 Melem/s 3.7300 Melem/s 3.7403 Melem/s]
                 change:
                        time:   [−12.728% −12.379% −12.061%] (p = 0.00 < 0.05)
                        thrpt:  [+13.715% +14.128% +14.585%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/mixed_no_null
                        time:   [2.1737 ms 2.1789 ms 2.1845 ms]
                        thrpt:  [3.7501 Melem/s 3.7598 Melem/s 3.7687 Melem/s]
                 change:
                        time:   [−13.197% −12.944% −12.687%] (p = 0.00 < 0.05)
                        thrpt:  [+14.531% +14.869% +15.203%]
                        Performance has improved.
regression_maps/normalize_hash/map/mixed_sparse_null
                        time:   [2.6175 ms 2.6308 ms 2.6448 ms]
                        thrpt:  [3.0974 Melem/s 3.1138 Melem/s 3.1297 Melem/s]
                 change:
                        time:   [−7.0262% −6.5230% −6.0364%] (p = 0.00 < 0.05)
                        thrpt:  [+6.4242% +6.9782% +7.5572%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/mixed_sparse_null
                        time:   [2.5724 ms 2.5772 ms 2.5820 ms]
                        thrpt:  [3.1727 Melem/s 3.1786 Melem/s 3.1845 Melem/s]
                 change:
                        time:   [−8.7803% −8.5611% −8.3228%] (p = 0.00 < 0.05)
                        thrpt:  [+9.0784% +9.3626% +9.6255%]
                        Performance has improved.
regression_maps/normalize_hash/map/mixed_dense_null
                        time:   [1.8443 ms 1.8474 ms 1.8509 ms]
                        thrpt:  [4.4260 Melem/s 4.4343 Melem/s 4.4419 Melem/s]
                 change:
                        time:   [−13.577% −13.319% −13.059%] (p = 0.00 < 0.05)
                        thrpt:  [+15.021% +15.365% +15.709%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/mixed_dense_null
                        time:   [1.8446 ms 1.8475 ms 1.8505 ms]
                        thrpt:  [4.4268 Melem/s 4.4341 Melem/s 4.4411 Melem/s]
                 change:
                        time:   [−14.546% −14.256% −14.006%] (p = 0.00 < 0.05)
                        thrpt:  [+16.287% +16.626% +17.022%]
                        Performance has improved.
regression_maps/normalize_hash/map/singleton_long_unicode
                        time:   [5.2905 ms 5.3096 ms 5.3310 ms]
                        thrpt:  [1.5367 Melem/s 1.5429 Melem/s 1.5484 Melem/s]
                 change:
                        time:   [−18.613% −18.199% −17.770%] (p = 0.00 < 0.05)
                        thrpt:  [+21.611% +22.248% +22.870%]
                        Performance has improved.
regression_maps/normalize_hash/struct_map_int/singleton_long_unicode
                        time:   [5.2874 ms 5.2992 ms 5.3108 ms]
                        thrpt:  [1.5425 Melem/s 1.5459 Melem/s 1.5493 Melem/s]
                 change:
                        time:   [−19.078% −18.747% −18.422%] (p = 0.00 < 0.05)
                        thrpt:  [+22.581% +23.072% +23.576%]
                        Performance has improved.
regression_maps/normalize_hash/map/mixed_long_unicode_dense_null
                        time:   [43.651 ms 43.752 ms 43.858 ms]
                        thrpt:  [186.79 Kelem/s 187.24 Kelem/s 187.67 Kelem/s]
                 change:
                        time:   [−1.4765% −0.9038% −0.3947%] (p = 0.00 < 0.05)
                        thrpt:  [+0.3962% +0.9121% +1.4986%]
                        Change within noise threshold.
regression_maps/normalize_hash/struct_map_int/mixed_long_unicode_dense_null
                        time:   [43.558 ms 43.625 ms 43.691 ms]
                        thrpt:  [187.50 Kelem/s 187.78 Kelem/s 188.07 Kelem/s]
                 change:
                        time:   [−0.8927% −0.5938% −0.3053%] (p = 0.00 < 0.05)
                        thrpt:  [+0.3062% +0.5974% +0.9007%]
                        Change within noise threshold.
matched_maps/hash_buffer_seed_reset
                        time:   [264.42 ns 268.76 ns 273.31 ns]
                        change: [−2.3217% −0.2880% +1.6503%] (p = 0.78 > 0.05)
                        No change in performance detected.

@github-actions github-actions Bot added enhancement New feature or request performance area:expressions Expression evaluation labels Sep 12, 2026

@sunchao sunchao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness

Reviewed cf22afe92e5d504adf72917a3a12473f9fb3c578 against de1eb4f86c12af0895784c93e1f152c705f6ef0e. I found no P1/P2 issue.

Previously, every nonempty map created a key slice and dispatched Arrow sorting, even when it contained one entry. This change appends that entry's original index directly for supported flat key types. Wider maps still use the same Arrow sort and the same global permutation. The final take, rebased offsets, map validity buffer, field metadata and sorted flag remain intact. Entries physically stored beneath null maps are still processed unless the existing all-null early return applies. Empty batches and already-sorted inputs keep their existing early returns.

On the maintained Spark 4.0 branch, MapSort orders key/value pairs together and preserves the map type in interpreted and generated execution. A singleton has no ordering decision, so retaining its original pair preserves that behavior without altering NaN payloads or signed zero. The locked Arrow 59.3 sources confirm that the eligible flat types use infallible sort implementations. Lists, dictionaries, structs and other excluded types continue through Arrow, including its singleton validation errors. The existing Comet restrictions for nested keys, collated strings and strict floating-point behavior are unchanged. This does not expand Spark compatibility. Spark 3.5 has no corresponding MapSort insertion and Comet still excludes map shuffle keys there. Maintained Spark 3.4/4.1 source branches were unavailable.

Validation

The three new Rust tests supplement nine existing map-sort tests. They check explicit permutations for mixed and singleton slices, nonzero offsets, hidden entries under null maps, nullable values, schema metadata, shared validity buffers, binary types, NaN bits, signed zero, unsupported nested-key errors and early returns. These are direct kernel tests rather than constant-folded SQL expressions.

At the September 12, 22:45 UTC refresh, CI had 56 successful and 10 skipped checks. The Rust job passed all 12 map-sort tests, all 11 Murmur3 tests and 1,418 tests overall. The Spark 4.1 expression job passed 1,384 tests, including the existing sliced-map regression. That integration test checks Spark answers without independently asserting native MapSort execution. Both jobs checked out merge 8e64630b7a87cdb83f838d5afd2c435fc78f9ef4, whose parents are the assigned base and head. All five changed files and inspected supporting paths match the head. Five inherited base files make the complete trees different. I did not run a local product build or benchmark.

Performance

The optimization removes a key-array slice, sort dispatch and local index allocation for each eligible singleton. It retains the global permutation and output take, so this is not a zero-copy result. A preliminary offset scan adds linear work to eligible batches without singletons. The const-specialized fallback avoids adding a singleton branch to their inner sorting loop. Mixed batches stop the preliminary scan at the first singleton.

The shared harness separates normalization, hashing and combined execution. Its added cases include sliced inputs, mixed cardinalities, null maps and values, long Unicode strings, empty maps and unsupported singleton keys. Input construction and correctness checks are untimed. Hash-buffer resets and normalization allocation/drop remain timed. The unsupported-key case measures first-error cost, not batch throughput.

The author reports 19–22x faster matched singleton normalization, 18.4x in an independent confirmation and 13.84x for one combined singleton case. The published tables retain the initial regression flags and subsequent independent/reversed-order measurements. These are author-reported microbenchmarks from a DataFusion 55.0 development cohort. The reviewed head and CI instead lock DataFusion 55.1. I have not independently reproduced those timings or established an exact-head speedup, and they do not measure end-to-end Spark query performance. The audit documentation identifies the development cohort explicitly.

Design

The change optimizes the existing normalization boundary without changing hashing or shuffle defaults. Keeping one global permutation preserves the established behavior for slices and null-map storage. Restricting the shortcut to flat types is justified by the fallible child-ranking paths for nested types. An unconditional singleton shortcut would suppress existing errors. The targeted eligibility check and shared output construction keep the behavioral change small.

Abstraction & complexity

The private const-generic helper gives both paths one loop implementation while removing the extra branch from the fallback specialization. It introduces no new configuration or public API. Benchmark additions reuse the existing shared fixtures and stage definitions, with independent expected permutations outside timing. I found no actionable abstraction or complexity issue.

@andygrove andygrove left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The singleton fast path looks right to me and the 19-22x on normalize_only/map/1 is well clear of any plausible harness noise. I checked the key type allowlist against arrow 59.3 and it lines up exactly with the infallible arms of sort_to_indices, so the correctness argument for skipping the sort holds.

@andygrove
andygrove added this pull request to the merge queue Sep 13, 2026
Merged via the queue into apache:main with commit ca223a8 Sep 13, 2026
66 checks passed
@viirya
viirya deleted the codex/map-sort-singleton-normalization branch September 13, 2026 16:58
@viirya

viirya commented Sep 13, 2026

Copy link
Copy Markdown
Member Author

Thanks @sunchao @andygrove

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:expressions Expression evaluation enhancement New feature or request performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avoid per-row sort dispatch when normalizing singleton maps

3 participants