From dabddbcb56e6f1f11d01995a8ef72e418f4f905d Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 27 Oct 2026 13:54:30 -0400 Subject: [PATCH] Use individual comparison operators instead of operator<=> This gives better codegen --- ConflictSet.cpp | 33 ++++++++++++++++++++++++++++++--- README.md | 42 +++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 553fb11..8d6c2eb 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -116,13 +116,40 @@ struct InternalVersionT { constexpr InternalVersionT() = default; constexpr explicit InternalVersionT(int64_t value) : value(value) {} constexpr int64_t toInt64() const { return value; } // GCOVR_EXCL_LINE - constexpr auto operator<=>(const InternalVersionT &rhs) const { + constexpr bool operator<(const InternalVersionT &rhs) const { #if USE_64_BIT - return value <=> rhs.value; + return value < rhs.value; #else // Maintains ordering after overflow, as long as the full-precision versions // are within `kMaxCorrectVersionWindow` of eachother. - return int32_t(value - rhs.value) <=> 0; + return int32_t(value - rhs.value) < 0; +#endif + } + constexpr bool operator<=(const InternalVersionT &rhs) const { +#if USE_64_BIT + return value <= rhs.value; +#else + // Maintains ordering after overflow, as long as the full-precision versions + // are within `kMaxCorrectVersionWindow` of eachother. + return int32_t(value - rhs.value) <= 0; +#endif + } + constexpr bool operator>=(const InternalVersionT &rhs) const { +#if USE_64_BIT + return value >= rhs.value; +#else + // Maintains ordering after overflow, as long as the full-precision versions + // are within `kMaxCorrectVersionWindow` of eachother. + return int32_t(value - rhs.value) >= 0; +#endif + } + constexpr bool operator>(const InternalVersionT &rhs) const { +#if USE_64_BIT + return value > rhs.value; +#else + // Maintains ordering after overflow, as long as the full-precision versions + // are within `kMaxCorrectVersionWindow` of eachother. + return int32_t(value - rhs.value) > 0; #endif } constexpr bool operator==(const InternalVersionT &) const = default; diff --git a/README.md b/README.md index 54073d9..b650154 100644 --- a/README.md +++ b/README.md @@ -19,30 +19,30 @@ InstalledDir: /usr/lib/llvm-21/bin | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 165.35 | 6,047,750.85 | 0.1% | 3,107.03 | 608.25 | 5.108 | 558.59 | 0.0% | 1.98 | `point reads` -| 161.75 | 6,182,478.30 | 0.2% | 3,036.76 | 594.75 | 5.106 | 539.35 | 0.0% | 1.94 | `prefix reads` -| 250.88 | 3,985,953.69 | 0.4% | 3,722.71 | 922.20 | 4.037 | 692.00 | 0.0% | 2.98 | `range reads` -| 358.34 | 2,790,617.48 | 0.2% | 4,523.64 | 1,317.11 | 3.435 | 720.22 | 2.1% | 4.28 | `point writes` -| 349.16 | 2,864,048.29 | 0.9% | 4,484.57 | 1,279.36 | 3.505 | 705.00 | 1.9% | 4.16 | `prefix writes` -| 192.06 | 5,206,715.53 | 0.0% | 2,224.10 | 706.55 | 3.148 | 377.17 | 3.3% | 2.30 | `range writes` -| 408.59 | 2,447,415.85 | 0.9% | 6,855.96 | 1,503.60 | 4.560 | 1,227.82 | 1.4% | 0.05 | `monotonic increasing point writes` -| 136,536.00 | 7,324.08 | 2.0% | 812,045.00 | 501,448.67 | 1.619 | 151,246.43 | 0.9% | 0.01 | `worst case for radix tree` -| 37.84 | 26,426,456.12 | 0.8% | 701.00 | 139.27 | 5.034 | 102.00 | 0.0% | 0.01 | `create and destroy` +| 168.06 | 5,950,172.07 | 0.0% | 3,107.03 | 615.58 | 5.047 | 558.59 | 0.0% | 2.01 | `point reads` +| 164.93 | 6,063,229.09 | 0.1% | 3,036.76 | 604.27 | 5.025 | 539.35 | 0.0% | 1.97 | `prefix reads` +| 242.64 | 4,121,298.49 | 0.1% | 3,722.71 | 891.62 | 4.175 | 692.00 | 0.0% | 2.90 | `range reads` +| 361.34 | 2,767,480.63 | 0.0% | 4,523.64 | 1,328.61 | 3.405 | 720.22 | 2.1% | 4.31 | `point writes` +| 349.52 | 2,861,056.44 | 0.1% | 4,484.57 | 1,279.55 | 3.505 | 705.00 | 1.9% | 4.19 | `prefix writes` +| 194.68 | 5,136,536.22 | 0.1% | 2,224.10 | 712.83 | 3.120 | 377.17 | 3.3% | 2.33 | `range writes` +| 416.48 | 2,401,076.73 | 1.0% | 6,855.96 | 1,522.78 | 4.502 | 1,227.82 | 1.4% | 0.05 | `monotonic increasing point writes` +| 131,614.80 | 7,597.93 | 1.0% | 812,044.75 | 484,589.00 | 1.676 | 151,246.50 | 0.1% | 0.01 | `worst case for radix tree` +| 38.24 | 26,148,048.33 | 1.5% | 701.00 | 140.19 | 5.001 | 102.00 | 0.0% | 0.01 | `create and destroy` ## Radix tree (this implementation) | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 12.44 | 80,368,377.55 | 0.2% | 237.48 | 45.74 | 5.192 | 31.23 | 0.8% | 0.15 | `point reads` -| 14.59 | 68,537,989.62 | 0.6% | 291.66 | 53.69 | 5.432 | 40.16 | 0.5% | 0.17 | `prefix reads` -| 33.91 | 29,487,389.82 | 0.1% | 759.22 | 124.80 | 6.084 | 101.27 | 0.2% | 0.41 | `range reads` -| 19.54 | 51,172,096.42 | 0.0% | 369.51 | 71.92 | 5.138 | 47.92 | 0.5% | 0.23 | `point writes` -| 37.87 | 26,402,961.91 | 0.1% | 662.02 | 139.34 | 4.751 | 100.26 | 0.4% | 0.45 | `prefix writes` -| 39.11 | 25,569,264.79 | 0.1% | 725.90 | 143.87 | 5.046 | 111.09 | 0.1% | 0.48 | `range writes` -| 77.70 | 12,869,910.98 | 0.5% | 1,430.41 | 286.41 | 4.994 | 271.78 | 0.1% | 0.01 | `monotonic increasing point writes` -| 319,309.00 | 3,131.76 | 0.5% | 4,181,645.50 | 1,178,098.50 | 3.549 | 728,511.00 | 0.1% | 0.01 | `worst case for radix tree` -| 97.89 | 10,215,764.97 | 0.3% | 1,775.00 | 360.36 | 4.926 | 288.00 | 0.0% | 0.01 | `create and destroy` +| 12.15 | 82,299,987.93 | 0.3% | 233.49 | 44.55 | 5.241 | 31.23 | 0.7% | 0.15 | `point reads` +| 14.31 | 69,893,290.91 | 0.2% | 283.68 | 52.40 | 5.414 | 40.16 | 0.4% | 0.17 | `prefix reads` +| 33.44 | 29,901,162.28 | 0.2% | 714.70 | 122.88 | 5.816 | 100.97 | 0.2% | 0.40 | `range reads` +| 19.73 | 50,684,375.53 | 0.1% | 369.51 | 72.61 | 5.089 | 47.92 | 0.5% | 0.24 | `point writes` +| 38.10 | 26,248,730.55 | 0.0% | 662.02 | 140.17 | 4.723 | 100.26 | 0.3% | 0.45 | `prefix writes` +| 39.76 | 25,152,283.94 | 0.0% | 725.90 | 146.25 | 4.964 | 111.09 | 0.1% | 0.48 | `range writes` +| 80.73 | 12,387,533.79 | 4.3% | 1,424.45 | 296.99 | 4.796 | 271.47 | 0.1% | 0.01 | `monotonic increasing point writes` +| 310,683.00 | 3,218.71 | 2.9% | 3,866,495.00 | 1,131,201.00 | 3.418 | 713,374.00 | 0.1% | 0.01 | `worst case for radix tree` +| 101.32 | 9,869,495.84 | 0.9% | 1,775.00 | 372.75 | 4.762 | 288.00 | 0.0% | 0.01 | `create and destroy` # "Real data" test @@ -51,13 +51,13 @@ Point queries only. Gc ratio is the ratio of time spent doing garbage collection ## skip list ``` -Check: 4.7186 seconds, 345.557 MB/s, Add: 3.3305 seconds, 168.339 MB/s, Gc ratio: 38.6509%, Peak idle memory: 5.51852e+06 +Check: 4.65794 seconds, 350.058 MB/s, Add: 3.30748 seconds, 169.511 MB/s, Gc ratio: 38.2502%, Peak idle memory: 5.51852e+06 ``` ## radix tree ``` -Check: 0.992421 seconds, 1643 MB/s, Add: 1.21864 seconds, 460.066 MB/s, Gc ratio: 38.8025%, Peak idle memory: 2.0226e+06 +Check: 0.984726 seconds, 1655.84 MB/s, Add: 1.22965 seconds, 455.946 MB/s, Gc ratio: 38.8292%, Peak idle memory: 2.0226e+06 ``` ## hash table @@ -65,6 +65,6 @@ Check: 0.992421 seconds, 1643 MB/s, Add: 1.21864 seconds, 460.066 MB/s, Gc ratio (The hash table implementation doesn't work on range queries, and its purpose is to provide an idea of how fast point queries can be) ``` -Check: 0.855247 seconds, 1906.52 MB/s, Add: 0.63138 seconds, 887.98 MB/s, Gc ratio: 41.2278%, Peak idle memory: 0 +Check: 0.864098 seconds, 1886.99 MB/s, Add: 0.636882 seconds, 880.31 MB/s, Gc ratio: 41.0631%, Peak idle memory: 0 ```