diff --git a/docs/performance.md b/docs/performance.md index 1c4a530..0ce1d93 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -11,10 +11,11 @@ smaller percentages are slower. Absolute ns/op values are host-specific appear when the kernel grants perf-counter access. Known slow paths (measured in the tables below, by design): -mixed direct/index-grid addition and cross-grid non-integer stores take -the exact rational path (see `docs/internals.md`); `bound` -element-wise loops keep a per-element range check — prefer `bnd::sum`'s -bulk check, which validates the total instead. +arithmetic with a rational-raw operand takes the exact rational path +(the mixed integer/notch-offset add and cross-grid non-integer stores +now have folded integer fast paths, gated on imax-safe spans); +`bound` element-wise loops keep a per-element range check — +prefer `bnd::sum`'s bulk check, which validates the total instead. Warning, results might be unstable: * CPU frequency scaling enabled: CPU 0 between 800.0 and 4,900.0 MHz @@ -26,265 +27,270 @@ Recommendations | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | construct (u8 [0,200]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------------- -| 100.0% | 4.79 | 208,853,867.01 | 4.6% | 98.00 | 17.27 | 5.675 | 12.00 | 0.1% | 0.02 | `native uint8` -| 59.3% | 8.08 | 123,823,079.25 | 1.2% | 162.00 | 29.11 | 5.565 | 21.00 | 0.0% | 0.03 | `native clamped` -| 89.7% | 5.34 | 187,380,982.38 | 0.4% | 124.00 | 19.24 | 6.444 | 17.00 | 0.0% | 0.02 | `checked (exceptions)` -| 35.1% | 13.62 | 73,404,439.78 | 0.7% | 211.00 | 49.11 | 4.297 | 30.00 | 0.0% | 0.05 | `bound` +| 100.0% | 4.65 | 215,140,715.86 | 1.8% | 98.00 | 16.75 | 5.851 | 12.00 | 0.1% | 0.02 | `native uint8` +| 60.4% | 7.70 | 129,875,137.48 | 1.9% | 162.00 | 27.75 | 5.838 | 21.00 | 0.0% | 0.03 | `native clamped` +| 84.7% | 5.49 | 182,222,826.93 | 1.7% | 124.00 | 19.79 | 6.265 | 17.00 | 0.0% | 0.02 | `checked (exceptions)` +| 34.0% | 13.67 | 73,135,681.65 | 0.8% | 211.00 | 49.29 | 4.281 | 30.00 | 0.0% | 0.05 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | add (u8 [0,200]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------- -| 100.0% | 6.45 | 155,028,608.74 | 0.7% | 151.00 | 23.25 | 6.495 | 17.00 | 0.0% | 0.02 | `native uint8` -| 67.5% | 9.56 | 104,591,355.35 | 2.6% | 216.00 | 34.48 | 6.264 | 26.00 | 0.0% | 0.03 | `native clamped` -| 53.7% | 12.01 | 83,290,945.32 | 1.6% | 234.00 | 43.26 | 5.409 | 31.00 | 0.0% | 0.04 | `checked (exceptions)` -| 16.6% | 38.81 | 25,766,674.52 | 0.3% | 541.00 | 139.94 | 3.866 | 78.00 | 0.0% | 0.14 | `bound` +| 100.0% | 6.66 | 150,077,819.97 | 1.1% | 151.00 | 24.02 | 6.286 | 17.00 | 0.0% | 0.02 | `native uint8` +| 66.2% | 10.06 | 99,370,947.54 | 0.6% | 216.00 | 36.21 | 5.965 | 26.00 | 0.0% | 0.04 | `native clamped` +| 59.6% | 11.18 | 89,469,719.90 | 2.9% | 234.00 | 40.21 | 5.819 | 31.00 | 0.0% | 0.04 | `checked (exceptions)` +| 16.9% | 39.40 | 25,377,687.42 | 0.5% | 541.00 | 142.03 | 3.809 | 78.00 | 0.0% | 0.14 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | mul (u8 [0,200]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------- -| 100.0% | 6.44 | 155,375,867.19 | 0.6% | 153.00 | 23.20 | 6.595 | 17.00 | 0.0% | 0.02 | `native uint8` -| 58.9% | 10.93 | 91,450,124.22 | 1.9% | 234.00 | 39.43 | 5.935 | 31.00 | 0.0% | 0.04 | `checked (exceptions)` -| 16.5% | 38.94 | 25,679,549.35 | 0.7% | 543.00 | 140.42 | 3.867 | 78.00 | 0.0% | 0.14 | `bound` +| 100.0% | 6.58 | 152,039,688.44 | 1.9% | 153.00 | 23.72 | 6.451 | 17.00 | 0.0% | 0.02 | `native uint8` +| 54.3% | 12.11 | 82,564,377.86 | 0.8% | 234.00 | 43.68 | 5.358 | 31.00 | 0.0% | 0.04 | `checked (exceptions)` +| 16.8% | 39.09 | 25,583,681.17 | 0.7% | 543.00 | 140.93 | 3.853 | 78.00 | 0.0% | 0.14 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | div (u8 [0,200]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------- -| 100.0% | 6.52 | 153,314,893.08 | 0.4% | 152.00 | 23.51 | 6.464 | 17.00 | 0.0% | 0.02 | `native uint8` -| 12.7% | 51.16 | 19,544,854.96 | 0.3% | 671.40 | 184.50 | 3.639 | 101.70 | 0.3% | 0.18 | `bound a / b (rational result)` -| 13.3% | 49.11 | 20,362,338.31 | 0.4% | 643.40 | 177.13 | 3.632 | 96.70 | 0.3% | 0.18 | `bound div(a, b, truncated)` +| 100.0% | 6.59 | 151,774,759.30 | 1.1% | 152.00 | 23.75 | 6.399 | 17.00 | 0.0% | 0.02 | `native uint8` +| 12.7% | 51.77 | 19,315,003.29 | 0.6% | 671.40 | 186.36 | 3.603 | 101.70 | 0.3% | 0.19 | `bound a / b (rational result)` +| 13.2% | 49.73 | 20,106,743.70 | 0.2% | 643.40 | 179.23 | 3.590 | 96.70 | 0.3% | 0.18 | `bound div(a, b, truncated)` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | increment (u32 [0,200000]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:--------------------------- -| 100.0% | 2.33 | 428,764,463.99 | 1.3% | 43.00 | 8.41 | 5.112 | 7.00 | 0.0% | 0.01 | `native ++` -| 24.2% | 9.65 | 103,676,506.49 | 2.7% | 135.00 | 34.78 | 3.881 | 23.00 | 0.0% | 0.03 | `bound ++` +| 100.0% | 2.32 | 430,595,167.34 | 0.8% | 43.00 | 8.37 | 5.135 | 7.00 | 0.0% | 0.01 | `native ++` +| 22.7% | 10.23 | 97,777,934.09 | 1.2% | 135.00 | 36.88 | 3.660 | 23.00 | 0.0% | 0.04 | `bound ++` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | compound /= (u8 [0,200]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------------------- -| 100.0% | 6.76 | 147,910,961.67 | 0.8% | 154.00 | 24.38 | 6.317 | 17.00 | 0.0% | 0.02 | `native /=` -| 9.6% | 70.38 | 14,207,804.54 | 0.2% | 929.54 | 253.51 | 3.667 | 144.77 | 0.2% | 0.25 | `bound /=` +| 100.0% | 6.98 | 143,281,595.03 | 0.8% | 154.00 | 25.16 | 6.120 | 17.00 | 0.0% | 0.03 | `native /=` +| 9.8% | 71.49 | 13,987,824.04 | 0.6% | 929.54 | 257.62 | 3.608 | 144.77 | 0.2% | 0.26 | `bound /=` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | accumulate 1000 (u32 [0,200000], per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------------------------------------------- -| 100.0% | 0.83 | 1,205,841,410.45 | 4.3% | 24.11 | 2.99 | 8.061 | 1.02 | 0.1% | 0.01 | `native loop` -| 36.6% | 2.26 | 441,760,889.50 | 0.1% | 59.12 | 8.16 | 7.242 | 7.02 | 0.0% | 0.01 | `bound loop` -| 16.1% | 5.15 | 194,223,157.98 | 2.0% | 101.12 | 18.52 | 5.461 | 15.02 | 0.0% | 0.02 | `bound loop` -| 52.6% | 1.58 | 634,701,839.58 | 0.2% | 34.20 | 5.68 | 6.019 | 5.19 | 0.0% | 0.01 | `bnd::sum (bulk check)` -| 108.7% | 0.76 | 1,310,942,695.13 | 0.2% | 23.12 | 2.75 | 8.410 | 1.02 | 0.1% | 0.01 | `std::accumulate native` -| 3.0% | 27.34 | 36,577,510.82 | 0.3% | 329.24 | 98.60 | 3.339 | 48.04 | 0.0% | 0.10 | `std::accumulate bound` +| 100.0% | 0.83 | 1,201,834,570.47 | 3.1% | 24.11 | 3.00 | 8.036 | 1.02 | 0.1% | 0.01 | `native loop` +| 35.6% | 2.34 | 428,101,739.81 | 2.1% | 59.12 | 8.41 | 7.028 | 7.02 | 0.0% | 0.01 | `bound loop` +| 16.2% | 5.15 | 194,354,199.97 | 1.1% | 101.12 | 18.55 | 5.451 | 15.02 | 0.0% | 0.02 | `bound loop` +| 51.4% | 1.62 | 617,772,937.83 | 1.8% | 34.20 | 5.83 | 5.862 | 5.19 | 0.0% | 0.01 | `bnd::sum (bulk check)` +| 108.7% | 0.77 | 1,306,387,591.74 | 0.6% | 23.12 | 2.76 | 8.375 | 1.02 | 0.1% | 0.01 | `std::accumulate native` +| 3.0% | 27.80 | 35,977,317.79 | 1.0% | 329.24 | 100.18 | 3.287 | 48.04 | 0.0% | 0.10 | `std::accumulate bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | add (signed [-100k,100k]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:-------------------------- -| 100.0% | 6.42 | 155,776,555.48 | 0.8% | 150.00 | 23.16 | 6.478 | 17.00 | 0.0% | 0.02 | `native int` -| 16.4% | 39.15 | 25,544,123.16 | 0.6% | 535.00 | 141.08 | 3.792 | 78.00 | 0.0% | 0.14 | `bound` +| 100.0% | 6.55 | 152,777,105.84 | 0.8% | 150.00 | 23.60 | 6.356 | 17.00 | 0.0% | 0.02 | `native int` +| 16.7% | 39.17 | 25,527,591.83 | 0.5% | 535.00 | 141.09 | 3.792 | 78.00 | 0.0% | 0.14 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | mul (signed [-100k,100k]) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:-------------------------- -| 100.0% | 6.44 | 155,378,996.67 | 0.4% | 150.00 | 23.21 | 6.463 | 17.00 | 0.0% | 0.02 | `native int` -| 16.6% | 38.89 | 25,715,504.37 | 0.5% | 537.00 | 140.10 | 3.833 | 78.00 | 0.0% | 0.14 | `bound` +| 100.0% | 6.65 | 150,332,269.40 | 1.9% | 150.00 | 23.99 | 6.253 | 17.00 | 0.0% | 0.02 | `native int` +| 17.0% | 39.11 | 25,568,975.04 | 0.6% | 537.00 | 140.99 | 3.809 | 78.00 | 0.0% | 0.14 | `bound` + +| relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | add (mixed integer + 1/4-notch grids) +|---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:-------------------------------------- +| 100.0% | 5.08 | 196,924,369.64 | 1.6% | 106.00 | 18.29 | 5.795 | 12.00 | 0.1% | 0.02 | `native int<<2 + q2` +| 16.4% | 30.99 | 32,268,013.24 | 0.7% | 415.00 | 111.60 | 3.719 | 60.00 | 0.0% | 0.11 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | accumulate 1000 (signed, per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:-------------------------------------- -| 100.0% | 0.83 | 1,206,231,738.40 | 0.1% | 24.11 | 2.99 | 8.067 | 1.02 | 0.1% | 0.01 | `native loop` -| 36.6% | 2.26 | 441,581,152.97 | 0.1% | 59.12 | 8.17 | 7.237 | 7.02 | 0.0% | 0.01 | `bound loop` +| 100.0% | 0.81 | 1,239,285,915.29 | 0.8% | 24.11 | 2.91 | 8.288 | 1.02 | 0.1% | 0.01 | `native loop` +| 35.5% | 2.27 | 440,098,073.66 | 0.4% | 59.12 | 8.19 | 7.223 | 7.02 | 0.0% | 0.01 | `bound loop` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q8.8 construct |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:--------------- -| 100.0% | 4.48 | 223,015,949.35 | 0.5% | 99.00 | 16.17 | 6.121 | 12.00 | 0.1% | 0.02 | `native int<<8` -| 30.3% | 14.81 | 67,503,135.93 | 1.3% | 224.00 | 53.40 | 4.195 | 32.00 | 0.0% | 0.05 | `bound` -| 30.0% | 14.93 | 66,982,479.15 | 0.8% | 232.00 | 53.82 | 4.311 | 34.00 | 0.0% | 0.05 | `bound` +| 100.0% | 4.69 | 213,223,419.25 | 0.4% | 99.00 | 16.89 | 5.861 | 12.00 | 0.1% | 0.02 | `native int<<8` +| 30.8% | 15.21 | 65,764,322.14 | 1.3% | 224.00 | 54.47 | 4.113 | 32.00 | 0.0% | 0.05 | `bound` +| 31.5% | 14.90 | 67,102,449.60 | 0.8% | 232.00 | 53.73 | 4.318 | 34.00 | 0.0% | 0.05 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q8.8 add |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:--------- -| 100.0% | 6.40 | 156,164,994.18 | 1.2% | 152.00 | 23.08 | 6.584 | 17.00 | 0.0% | 0.02 | `native` -| 18.3% | 35.04 | 28,538,900.87 | 0.5% | 529.00 | 126.28 | 4.189 | 76.00 | 0.0% | 0.13 | `bound` -| 18.3% | 34.91 | 28,645,830.26 | 0.4% | 549.00 | 125.86 | 4.362 | 80.00 | 0.0% | 0.13 | `bound` +| 100.0% | 6.57 | 152,180,696.93 | 1.4% | 152.00 | 23.68 | 6.418 | 17.00 | 0.0% | 0.02 | `native` +| 18.6% | 35.30 | 28,329,291.07 | 0.5% | 529.00 | 127.21 | 4.159 | 76.00 | 0.0% | 0.13 | `bound` +| 19.0% | 34.67 | 28,843,760.37 | 0.5% | 549.00 | 125.00 | 4.392 | 80.00 | 0.0% | 0.12 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q8.8 mul |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:--------- -| 100.0% | 6.47 | 154,566,433.43 | 0.9% | 152.00 | 23.32 | 6.519 | 17.00 | 0.0% | 0.02 | `native (a*b)>>8` -| 17.7% | 36.62 | 27,308,066.18 | 0.4% | 536.00 | 132.00 | 4.061 | 76.00 | 0.0% | 0.13 | `bound` +| 100.0% | 7.06 | 141,598,251.41 | 3.7% | 152.00 | 25.46 | 5.969 | 17.00 | 0.0% | 0.03 | `native (a*b)>>8` +| 19.1% | 36.94 | 27,074,530.42 | 0.4% | 536.00 | 133.16 | 4.025 | 76.00 | 0.0% | 0.13 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q8.8 div (truncated) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:--------------------- -| 100.0% | 6.56 | 152,367,917.27 | 0.4% | 153.00 | 23.66 | 6.466 | 17.00 | 0.0% | 0.02 | `native (a<<8)/b` -| 14.9% | 44.06 | 22,695,939.05 | 0.1% | 609.75 | 158.88 | 3.838 | 90.38 | 0.4% | 0.16 | `bound div(a, b, truncated)` +| 100.0% | 6.89 | 145,197,403.50 | 2.3% | 153.00 | 24.81 | 6.168 | 17.00 | 0.0% | 0.03 | `native (a<<8)/b` +| 15.5% | 44.44 | 22,501,912.32 | 0.6% | 609.75 | 160.14 | 3.808 | 90.38 | 0.4% | 0.16 | `bound div(a, b, truncated)` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q8.8 accumulate 1000 (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------------------------- -| 100.0% | 0.84 | 1,184,480,271.32 | 5.3% | 24.11 | 3.04 | 7.919 | 1.02 | 0.1% | 0.01 | :wavy_dash: `native loop` (Unstable with ~1,308.2 iters. Increase `minEpochIterations` to e.g. 13082) -| 37.1% | 2.27 | 439,808,536.68 | 0.3% | 62.12 | 8.20 | 7.577 | 7.02 | 0.0% | 0.01 | `bound loop` +| 100.0% | 0.87 | 1,150,869,420.52 | 8.5% | 24.11 | 3.13 | 7.693 | 1.02 | 0.1% | 0.01 | :wavy_dash: `native loop` (Unstable with ~1,244.6 iters. Increase `minEpochIterations` to e.g. 12446) +| 37.9% | 2.29 | 436,617,183.39 | 0.6% | 62.12 | 8.26 | 7.520 | 7.02 | 0.0% | 0.01 | `bound loop` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q1.14 signed add |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------- -| 100.0% | 3.83 | 260,925,658.61 | 0.1% | 57.00 | 13.82 | 4.124 | 7.00 | 0.1% | 0.01 | `native int` -| 25.3% | 15.16 | 65,975,846.86 | 0.4% | 222.00 | 54.65 | 4.062 | 33.00 | 0.0% | 0.05 | `bound (raw-level)` +| 100.0% | 3.85 | 259,868,794.81 | 0.2% | 57.00 | 13.88 | 4.107 | 7.00 | 0.1% | 0.01 | `native int` +| 25.4% | 15.13 | 66,072,291.72 | 0.5% | 222.00 | 54.42 | 4.079 | 33.00 | 0.0% | 0.05 | `bound (raw-level)` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q16.16 add |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 6.39 | 156,506,288.28 | 0.3% | 154.00 | 23.04 | 6.685 | 17.00 | 0.0% | 0.02 | `native int64` -| 18.2% | 35.01 | 28,559,471.77 | 0.8% | 525.00 | 126.26 | 4.158 | 76.00 | 0.0% | 0.13 | `bound` +| 100.0% | 6.47 | 154,496,822.97 | 0.8% | 154.00 | 23.34 | 6.599 | 17.00 | 0.0% | 0.02 | `native int64` +| 18.4% | 35.10 | 28,487,171.03 | 0.6% | 525.00 | 126.47 | 4.151 | 76.00 | 0.0% | 0.13 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | assign from double ([-40,60] step 0.5) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:--------------------------------------- -| 100.0% | 74.81 | 13,367,048.72 | 0.4% | 1,419.80 | 269.71 | 5.264 | 225.20 | 0.0% | 0.27 | `truncate (snap)` -| 97.9% | 76.40 | 13,088,736.11 | 0.2% | 1,426.20 | 275.23 | 5.182 | 227.40 | 0.0% | 0.27 | `round_nearest` +| 100.0% | 72.84 | 13,729,613.30 | 0.9% | 1,387.80 | 262.42 | 5.289 | 221.20 | 0.0% | 0.26 | `truncate (snap)` +| 98.6% | 73.89 | 13,533,092.52 | 0.6% | 1,394.20 | 266.20 | 5.237 | 223.40 | 0.0% | 0.26 | `round_nearest` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | Q8.8 store |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 69.89 | 14,307,720.59 | 0.4% | 1,042.39 | 251.94 | 4.137 | 192.48 | 0.3% | 0.25 | `from fraction (rational)` -| 102.6% | 68.12 | 14,680,159.07 | 0.4% | 1,266.32 | 245.63 | 5.155 | 202.48 | 0.0% | 0.25 | `from double` +| 100.0% | 72.25 | 13,841,373.59 | 0.5% | 1,042.39 | 260.43 | 4.003 | 192.48 | 0.3% | 0.26 | `from fraction (rational)` +| 107.4% | 67.27 | 14,865,483.63 | 0.5% | 1,234.34 | 241.76 | 5.106 | 198.48 | 0.0% | 0.24 | `from double` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | cross-grid assign |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------------ -| 100.0% | 12.08 | 82,758,587.38 | 0.6% | 176.00 | 43.58 | 4.039 | 25.00 | 0.0% | 0.04 | `same-grid copy` -| 48.3% | 25.02 | 39,965,034.57 | 0.2% | 359.00 | 90.22 | 3.979 | 50.00 | 0.0% | 0.09 | `integer mapping ([0,100] -> [-500,9000])` -| 7.8% | 154.05 | 6,491,418.44 | 0.5% | 2,143.94 | 555.37 | 3.860 | 379.32 | 0.6% | 0.55 | `non-integer snap (1/10 -> 1/4 grid)` +| 100.0% | 12.16 | 82,207,791.48 | 0.5% | 176.00 | 43.82 | 4.016 | 25.00 | 0.0% | 0.04 | `same-grid copy` +| 47.8% | 25.46 | 39,282,237.90 | 0.9% | 359.00 | 91.41 | 3.928 | 50.00 | 0.0% | 0.09 | `integer mapping ([0,100] -> [-500,9000])` +| 12.3% | 99.26 | 10,074,974.39 | 0.3% | 1,351.03 | 357.53 | 3.779 | 234.58 | 0.6% | 0.36 | `non-integer snap (1/10 -> 1/4 grid)` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | comparison < |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------- -| 100.0% | 6.97 | 143,375,920.36 | 0.6% | 152.00 | 25.15 | 6.043 | 17.00 | 0.0% | 0.02 | `native int16` -| 74.5% | 9.37 | 106,760,857.00 | 1.6% | 202.00 | 33.78 | 5.980 | 28.00 | 0.0% | 0.03 | `bound same grid` -| 60.1% | 11.60 | 86,211,633.65 | 1.2% | 224.97 | 41.82 | 5.379 | 31.98 | 0.1% | 0.04 | `bound index raw, same notch, cross interval` +| 100.0% | 7.02 | 142,405,064.55 | 1.2% | 152.00 | 25.31 | 6.006 | 17.00 | 0.0% | 0.03 | `native int16` +| 74.0% | 9.50 | 105,318,264.38 | 1.9% | 202.00 | 34.23 | 5.901 | 28.00 | 0.0% | 0.03 | `bound same grid` +| 62.6% | 11.21 | 89,188,196.20 | 2.6% | 224.97 | 40.41 | 5.567 | 31.98 | 0.1% | 0.04 | `bound index raw, same notch, cross interval` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | casts (s9k value into u200) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------------------------- -| 100.0% | 5.87 | 170,246,367.49 | 0.4% | 127.98 | 21.18 | 6.042 | 16.98 | 0.0% | 0.02 | `native clamp` -| 13.0% | 45.24 | 22,105,024.37 | 0.4% | 727.77 | 162.95 | 4.466 | 110.99 | 0.0% | 0.16 | `clamp_cast` -| 20.1% | 29.21 | 34,235,596.35 | 0.2% | 440.00 | 105.35 | 4.177 | 63.00 | 0.0% | 0.10 | `checked construction (in range)` +| 100.0% | 5.87 | 170,372,597.98 | 1.2% | 127.98 | 21.15 | 6.051 | 16.98 | 0.0% | 0.02 | `native clamp` +| 12.9% | 45.64 | 21,910,893.35 | 0.2% | 727.77 | 164.48 | 4.425 | 110.99 | 0.0% | 0.16 | `clamp_cast` +| 19.7% | 29.87 | 33,478,817.39 | 0.7% | 440.00 | 107.59 | 4.090 | 63.00 | 0.0% | 0.11 | `checked construction (in range)` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | dot (2D, signed) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------- -| 100.0% | 10.43 | 95,838,323.84 | 1.4% | 255.00 | 37.63 | 6.777 | 27.00 | 0.0% | 0.04 | `native int` -| 11.3% | 92.25 | 10,839,856.52 | 0.2% | 1,226.00 | 332.11 | 3.692 | 176.00 | 0.0% | 0.33 | `bnd::dot` +| 100.0% | 11.40 | 87,699,830.52 | 3.0% | 255.00 | 41.09 | 6.205 | 27.00 | 0.0% | 0.04 | `native int` +| 11.8% | 96.75 | 10,336,119.74 | 0.1% | 1,226.00 | 348.32 | 3.520 | 176.00 | 0.0% | 0.35 | `bnd::dot` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: abs |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 100.0% | 5.07 | 197,121,153.51 | 0.5% | 107.00 | 18.29 | 5.851 | 14.00 | 0.1% | 0.01 | `std double` -| 9.3% | 54.71 | 18,278,297.27 | 0.8% | 1,047.45 | 197.18 | 5.312 | 171.49 | 0.0% | 0.03 | `bnd::math bound` +| 100.0% | 5.11 | 195,660,892.22 | 1.8% | 107.00 | 18.41 | 5.812 | 14.00 | 0.1% | 0.01 | `std double` +| 9.5% | 53.91 | 18,551,050.80 | 0.9% | 1,031.45 | 192.87 | 5.348 | 169.49 | 0.0% | 0.03 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: floor |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------ -| 100.0% | 4.86 | 205,709,235.26 | 0.5% | 103.00 | 17.51 | 5.882 | 15.00 | 0.1% | 0.01 | `std double` -| 7.5% | 64.58 | 15,484,009.48 | 0.7% | 1,218.45 | 232.58 | 5.239 | 203.00 | 0.0% | 0.04 | `bnd::math bound` +| 100.0% | 4.79 | 208,585,795.65 | 0.3% | 103.00 | 17.28 | 5.962 | 15.00 | 0.1% | 0.01 | `std double` +| 7.7% | 62.20 | 16,076,152.09 | 0.6% | 1,202.44 | 224.29 | 5.361 | 200.99 | 0.0% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: round |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------ -| 100.0% | 5.10 | 195,990,397.94 | 0.8% | 119.25 | 18.40 | 6.482 | 18.25 | 0.1% | 0.01 | `std double` -| 8.1% | 63.26 | 15,807,454.22 | 0.3% | 1,219.80 | 228.08 | 5.348 | 202.74 | 0.0% | 0.04 | `bnd::math bound` +| 100.0% | 5.07 | 197,181,265.96 | 0.5% | 119.25 | 18.29 | 6.520 | 18.25 | 0.1% | 0.01 | `std double` +| 8.2% | 62.17 | 16,085,551.52 | 0.7% | 1,203.81 | 224.22 | 5.369 | 200.74 | 0.0% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: sqrt |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 5.00 | 200,196,910.11 | 0.0% | 108.00 | 18.01 | 5.996 | 17.00 | 0.1% | 0.01 | `std double` -| 10.8% | 46.05 | 21,717,635.98 | 0.2% | 670.00 | 166.07 | 4.034 | 122.00 | 0.3% | 0.03 | `bnd::math bound` +| 100.0% | 5.01 | 199,703,431.08 | 0.7% | 108.00 | 18.05 | 5.983 | 17.00 | 0.1% | 0.01 | `std double` +| 10.8% | 46.25 | 21,619,385.63 | 0.8% | 670.00 | 166.73 | 4.018 | 122.00 | 0.3% | 0.03 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: exp2 |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 5.83 | 171,529,114.61 | 3.0% | 141.99 | 21.01 | 6.758 | 17.00 | 0.0% | 0.01 | `std double` -| 7.9% | 73.37 | 13,628,989.06 | 0.3% | 973.98 | 264.56 | 3.682 | 179.00 | 0.2% | 0.04 | `bnd::math bound` +| 100.0% | 6.15 | 162,729,891.46 | 1.4% | 141.99 | 22.15 | 6.410 | 17.00 | 0.0% | 0.01 | `std double` +| 8.3% | 74.26 | 13,466,129.66 | 0.9% | 974.04 | 267.66 | 3.639 | 179.01 | 0.2% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: log2 |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 6.48 | 154,412,329.28 | 0.5% | 153.72 | 23.33 | 6.588 | 17.00 | 0.0% | 0.01 | `std double` -| 9.6% | 67.35 | 14,848,698.21 | 1.0% | 926.56 | 242.84 | 3.816 | 164.02 | 0.2% | 0.04 | `bnd::math bound` +| 100.0% | 6.32 | 158,144,640.23 | 1.2% | 153.72 | 22.79 | 6.746 | 17.00 | 0.0% | 0.01 | `std double` +| 9.3% | 67.91 | 14,725,203.77 | 0.4% | 926.56 | 244.82 | 3.785 | 164.02 | 0.2% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: exp |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 100.0% | 6.62 | 151,036,239.75 | 0.7% | 157.99 | 23.86 | 6.621 | 23.00 | 0.0% | 0.01 | `std double` -| 9.1% | 72.48 | 13,796,636.93 | 0.4% | 965.35 | 261.42 | 3.693 | 177.62 | 0.2% | 0.04 | `bnd::math bound` +| 100.0% | 7.85 | 127,325,054.45 | 6.1% | 157.99 | 28.08 | 5.626 | 23.00 | 0.0% | 0.01 | :wavy_dash: `std double` (Unstable with ~137,790.5 iters. Increase `minEpochIterations` to e.g. 1377905) +| 10.6% | 73.80 | 13,550,877.67 | 1.2% | 965.35 | 266.01 | 3.629 | 177.62 | 0.2% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: log |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 100.0% | 6.71 | 149,109,726.27 | 0.7% | 154.00 | 24.19 | 6.367 | 20.00 | 0.0% | 0.01 | `std double` -| 10.4% | 64.50 | 15,504,331.15 | 0.9% | 914.49 | 232.42 | 3.935 | 162.00 | 0.2% | 0.04 | `bnd::math bound` +| 100.0% | 6.69 | 149,557,726.74 | 0.8% | 154.00 | 24.11 | 6.388 | 20.00 | 0.0% | 0.01 | `std double` +| 10.2% | 65.29 | 15,315,628.59 | 0.8% | 914.50 | 235.36 | 3.885 | 162.00 | 0.2% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: pow10 |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------ -| 100.0% | 11.19 | 89,342,320.72 | 0.4% | 231.98 | 40.36 | 5.748 | 25.00 | 0.0% | 0.01 | `std double` -| 10.5% | 106.53 | 9,387,439.96 | 0.3% | 1,241.77 | 383.96 | 3.234 | 223.40 | 0.2% | 0.06 | `bnd::math bound` +| 100.0% | 11.54 | 86,655,735.47 | 2.2% | 231.98 | 41.65 | 5.570 | 25.00 | 0.0% | 0.01 | `std double` +| 10.7% | 107.49 | 9,302,939.91 | 0.7% | 1,241.79 | 387.37 | 3.206 | 223.40 | 0.2% | 0.06 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: sin |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 100.0% | 7.59 | 131,679,870.62 | 0.6% | 174.52 | 27.38 | 6.374 | 21.64 | 0.0% | 0.01 | `std double` -| 12.3% | 61.86 | 16,164,337.38 | 0.4% | 845.13 | 223.07 | 3.789 | 156.52 | 0.3% | 0.04 | `bnd::math bound` +| 100.0% | 7.53 | 132,782,500.53 | 0.8% | 174.53 | 27.11 | 6.437 | 21.64 | 0.0% | 0.01 | `std double` +| 12.0% | 62.89 | 15,900,369.93 | 1.0% | 845.10 | 226.63 | 3.729 | 156.51 | 0.3% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: cos |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 100.0% | 7.41 | 134,945,240.57 | 0.3% | 176.21 | 26.72 | 6.594 | 22.31 | 0.0% | 0.01 | `std double` -| 11.8% | 62.73 | 15,940,337.47 | 0.2% | 855.21 | 226.24 | 3.780 | 157.97 | 0.2% | 0.04 | `bnd::math bound` +| 100.0% | 7.56 | 132,315,903.27 | 0.9% | 176.22 | 27.24 | 6.469 | 22.31 | 0.0% | 0.01 | `std double` +| 11.8% | 63.94 | 15,639,613.26 | 0.6% | 855.21 | 230.46 | 3.711 | 157.97 | 0.2% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: tan |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- -| 100.0% | 7.97 | 125,446,090.94 | 0.6% | 172.07 | 28.69 | 5.997 | 22.91 | 0.0% | 0.01 | `std double` -| 9.1% | 87.81 | 11,388,315.88 | 0.3% | 1,177.00 | 316.65 | 3.717 | 222.00 | 0.2% | 0.05 | `bnd::math bound` +| 100.0% | 8.01 | 124,794,069.91 | 1.1% | 172.07 | 28.89 | 5.955 | 22.91 | 0.0% | 0.01 | `std double` +| 8.8% | 90.63 | 11,033,499.46 | 1.2% | 1,177.00 | 326.73 | 3.602 | 222.00 | 0.2% | 0.05 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: atan2 |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------ -| 100.0% | 14.13 | 70,772,125.33 | 0.6% | 303.46 | 50.94 | 5.957 | 50.99 | 0.0% | 0.01 | `std double` -| 20.6% | 68.66 | 14,564,404.84 | 0.2% | 994.02 | 247.63 | 4.014 | 176.00 | 0.0% | 0.04 | `bnd::math bound` +| 100.0% | 14.04 | 71,200,068.11 | 0.4% | 303.47 | 50.61 | 5.996 | 50.99 | 0.0% | 0.01 | `std double` +| 20.1% | 69.89 | 14,307,400.65 | 0.9% | 994.02 | 251.96 | 3.945 | 176.00 | 0.0% | 0.04 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: fmod |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 10.22 | 97,866,226.83 | 0.2% | 200.70 | 36.81 | 5.452 | 24.52 | 0.5% | 0.01 | `std double` -| 61.8% | 16.53 | 60,510,490.70 | 0.9% | 285.00 | 59.58 | 4.784 | 39.00 | 0.0% | 0.01 | `bnd::math bound` +| 100.0% | 10.31 | 96,961,159.75 | 1.8% | 200.70 | 37.15 | 5.403 | 24.52 | 0.5% | 0.01 | `std double` +| 62.1% | 16.62 | 60,183,278.02 | 1.8% | 285.00 | 59.90 | 4.758 | 39.00 | 0.0% | 0.01 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: asin |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 7.64 | 130,975,648.50 | 0.4% | 166.54 | 27.54 | 6.048 | 24.30 | 0.0% | 0.01 | `std double` -| 10.1% | 75.59 | 13,229,073.87 | 0.3% | 914.31 | 272.49 | 3.355 | 169.12 | 0.2% | 0.05 | `bnd::math bound` +| 100.0% | 7.92 | 126,281,470.46 | 0.2% | 166.55 | 28.51 | 5.843 | 24.30 | 0.0% | 0.01 | `std double` +| 10.1% | 78.24 | 12,780,625.42 | 1.4% | 914.34 | 281.07 | 3.253 | 169.12 | 0.2% | 0.05 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: tanh |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 11.36 | 88,000,702.18 | 0.1% | 213.05 | 40.98 | 5.198 | 34.63 | 0.0% | 0.01 | `std double` -| 14.6% | 77.99 | 12,821,336.31 | 0.6% | 997.17 | 281.24 | 3.546 | 182.31 | 0.2% | 0.05 | `bnd::math bound` +| 100.0% | 11.49 | 87,063,474.38 | 0.6% | 213.07 | 41.40 | 5.147 | 34.63 | 0.0% | 0.01 | `std double` +| 14.5% | 79.30 | 12,610,260.97 | 0.7% | 997.17 | 285.81 | 3.489 | 182.31 | 0.2% | 0.05 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: cbrt |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------- -| 100.0% | 12.48 | 80,120,612.04 | 0.6% | 240.73 | 45.01 | 5.348 | 40.25 | 0.0% | 0.01 | `std double` -| 11.4% | 109.74 | 9,112,042.11 | 0.3% | 1,226.75 | 395.63 | 3.101 | 226.39 | 0.3% | 0.07 | `bnd::math bound` +| 100.0% | 12.59 | 79,455,119.98 | 1.3% | 240.72 | 45.38 | 5.305 | 40.24 | 0.0% | 0.01 | `std double` +| 11.3% | 110.94 | 9,013,855.84 | 0.2% | 1,226.71 | 399.92 | 3.067 | 226.38 | 0.3% | 0.07 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: hypot |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------ -| 100.0% | 8.88 | 112,592,713.25 | 0.6% | 204.85 | 32.03 | 6.395 | 28.62 | 0.0% | 0.01 | `std double` -| 17.1% | 51.99 | 19,236,276.56 | 0.7% | 779.00 | 187.46 | 4.156 | 135.00 | 0.3% | 0.03 | `bnd::math bound` +| 100.0% | 9.09 | 110,039,752.95 | 0.7% | 204.85 | 32.76 | 6.252 | 28.61 | 0.0% | 0.01 | `std double` +| 17.5% | 51.98 | 19,237,976.00 | 1.4% | 779.00 | 187.29 | 4.159 | 135.00 | 0.3% | 0.03 | `bnd::math bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | math: sin (binary32 / float engine) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------------------------------ -| 100.0% | 6.77 | 147,708,280.10 | 0.7% | 142.38 | 24.39 | 5.837 | 19.63 | 0.0% | 0.01 | `std sinf` -| 9.2% | 73.25 | 13,651,841.57 | 0.6% | 941.61 | 264.13 | 3.565 | 165.70 | 0.2% | 0.04 | `bnd::math::flt::sin (f32 bound)` +| 100.0% | 7.02 | 142,471,819.42 | 1.2% | 142.35 | 25.30 | 5.628 | 19.62 | 0.0% | 0.01 | `std sinf` +| 9.5% | 73.65 | 13,578,265.92 | 1.5% | 941.61 | 265.31 | 3.549 | 165.69 | 0.2% | 0.04 | `bnd::math::flt::sin (f32 bound)` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | sort 10k (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------------------- -| 100.0% | 276.40 | 3,617,937.35 | 0.4% | 3,942.63 | 996.65 | 3.956 | 439.06 | 0.6% | 1.00 | `native int16` -| 80.1% | 345.15 | 2,897,259.73 | 0.3% | 4,892.36 | 1,244.56 | 3.931 | 642.49 | 0.4% | 1.24 | `bound` +| 100.0% | 284.17 | 3,518,990.34 | 0.3% | 3,942.63 | 1,024.06 | 3.850 | 439.06 | 0.6% | 1.02 | `native int16` +| 78.7% | 360.88 | 2,771,026.17 | 1.0% | 4,892.36 | 1,300.45 | 3.762 | 642.49 | 0.4% | 1.30 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | nth_element 10k (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------------------------ -| 100.0% | 5.12 | 195,452,090.46 | 0.6% | 117.79 | 18.45 | 6.384 | 15.90 | 0.1% | 0.02 | `native int16` -| 41.9% | 12.22 | 81,843,664.96 | 1.4% | 249.37 | 44.07 | 5.659 | 44.09 | 0.1% | 0.04 | `bound` +| 100.0% | 4.87 | 205,338,223.43 | 1.0% | 117.79 | 17.56 | 6.708 | 15.90 | 0.1% | 0.02 | `native int16` +| 42.4% | 11.50 | 86,980,488.65 | 2.5% | 249.37 | 41.45 | 6.016 | 44.09 | 0.1% | 0.04 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | partition 10k (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------------------------- -| 100.0% | 1.38 | 722,412,528.65 | 0.5% | 37.62 | 4.98 | 7.550 | 5.21 | 0.0% | 0.01 | `native int16` -| 26.1% | 5.31 | 188,425,793.67 | 0.2% | 94.62 | 19.11 | 4.950 | 17.21 | 0.1% | 0.02 | `bound` +| 100.0% | 1.38 | 722,807,231.12 | 0.6% | 37.62 | 4.99 | 7.541 | 5.21 | 0.0% | 0.01 | `native int16` +| 25.8% | 5.36 | 186,411,640.01 | 1.0% | 94.62 | 19.33 | 4.895 | 17.21 | 0.1% | 0.02 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | find / count 10k (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:------------------------------- -| 100.0% | 2.50 | 400,580,441.06 | 0.7% | 58.94 | 9.00 | 6.549 | 7.86 | 0.0% | 0.01 | `find native` -| 63.2% | 3.95 | 253,001,038.79 | 1.1% | 88.02 | 14.24 | 6.180 | 12.58 | 0.0% | 0.01 | `find bound` -| 75.4% | 3.31 | 302,010,006.02 | 0.1% | 73.02 | 11.94 | 6.113 | 8.00 | 0.0% | 0.01 | `count native` -| 50.0% | 5.00 | 200,166,962.80 | 0.5% | 107.03 | 18.02 | 5.940 | 14.00 | 0.0% | 0.02 | `count bound` +| 100.0% | 2.52 | 396,320,559.92 | 1.0% | 58.94 | 9.11 | 6.472 | 7.86 | 0.0% | 0.01 | `find native` +| 63.2% | 3.99 | 250,364,280.03 | 0.8% | 88.02 | 14.39 | 6.117 | 12.58 | 0.0% | 0.02 | `find bound` +| 75.5% | 3.34 | 299,294,226.80 | 0.4% | 73.02 | 12.04 | 6.062 | 8.00 | 0.0% | 0.01 | `count native` +| 49.4% | 5.11 | 195,636,650.57 | 2.5% | 107.03 | 18.35 | 5.833 | 14.00 | 0.0% | 0.02 | `count bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | min/max_element 10k (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------------------------------- -| 100.0% | 10.22 | 97,850,531.36 | 2.1% | 185.00 | 36.82 | 5.024 | 20.00 | 0.0% | 0.04 | `min_element native` -| 73.4% | 13.92 | 71,823,615.97 | 0.5% | 241.00 | 50.13 | 4.808 | 32.00 | 0.0% | 0.05 | `min_element bound` -| 100.8% | 10.14 | 98,630,059.30 | 0.9% | 185.27 | 36.56 | 5.068 | 20.00 | 0.0% | 0.04 | `max_element native` -| 74.1% | 13.79 | 72,527,466.15 | 0.3% | 241.27 | 49.72 | 4.853 | 32.00 | 0.0% | 0.05 | `max_element bound` +| 100.0% | 10.96 | 91,260,698.84 | 5.5% | 185.00 | 39.50 | 4.684 | 20.00 | 0.0% | 0.04 | :wavy_dash: `min_element native` (Unstable with ~32.7 iters. Increase `minEpochIterations` to e.g. 327) +| 76.8% | 14.28 | 70,051,383.82 | 3.1% | 241.00 | 51.45 | 4.684 | 32.00 | 0.0% | 0.05 | `min_element bound` +| 106.7% | 10.27 | 97,384,014.07 | 2.5% | 185.27 | 37.02 | 5.004 | 20.00 | 0.0% | 0.04 | `max_element native` +| 77.4% | 14.15 | 70,660,907.96 | 0.7% | 241.27 | 51.01 | 4.730 | 32.00 | 0.0% | 0.05 | `max_element bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | lower_bound 10k |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------------- -| 100.0% | 147.19 | 6,793,829.64 | 0.3% | 2,332.12 | 530.82 | 4.393 | 246.29 | 1.1% | 0.53 | `native int16` -| 69.8% | 210.86 | 4,742,543.32 | 0.2% | 3,190.29 | 759.13 | 4.203 | 422.57 | 0.8% | 0.76 | `bound` +| 100.0% | 149.55 | 6,686,831.18 | 1.1% | 2,332.12 | 538.61 | 4.330 | 246.29 | 1.1% | 0.54 | `native int16` +| 69.7% | 214.61 | 4,659,515.42 | 0.7% | 3,190.29 | 773.38 | 4.125 | 422.57 | 0.8% | 0.77 | `bound` | relative | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | transform v+1 10k (per element) |---------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:-------------------------------- -| 100.0% | 1.33 | 751,642,478.01 | 0.2% | 38.02 | 4.79 | 7.930 | 3.00 | 0.0% | 0.01 | `native uint8` -| 22.4% | 5.93 | 168,718,036.97 | 0.4% | 102.02 | 21.38 | 4.773 | 14.00 | 0.1% | 0.02 | `bound u255 (16-bit storage)` -| 22.9% | 5.81 | 172,078,999.32 | 1.3% | 102.02 | 20.95 | 4.869 | 14.00 | 0.1% | 0.02 | `bound u254 (8-bit storage)` +| 100.0% | 1.34 | 747,849,447.22 | 0.6% | 38.02 | 4.82 | 7.888 | 3.00 | 0.0% | 0.01 | `native uint8` +| 22.3% | 5.99 | 166,972,693.50 | 1.8% | 102.02 | 21.58 | 4.727 | 14.00 | 0.1% | 0.02 | `bound u255 (16-bit storage)` +| 22.9% | 5.84 | 171,098,796.47 | 1.0% | 102.02 | 20.94 | 4.872 | 14.00 | 0.1% | 0.02 | `bound u254 (8-bit storage)` diff --git a/include/bound/detail/addition.hpp b/include/bound/detail/addition.hpp index 1768157..2b9e69a 100644 --- a/include/bound/detail/addition.hpp +++ b/include/bound/detail/addition.hpp @@ -45,6 +45,47 @@ namespace bnd::detail result, return_type_for>; + // Mixed integer-aligned / notch-offset fast path: with a unit-numerator + // result notch 1/d, both operand offsets in result-notch units are exact + // integer math — (to_value − Lower)·d for the integer-aligned operand, + // raw·widen for the notch-offset one (offsets compose because + // Lower = Lower + Lower). Gated on an index-raw result and + // the result slot count fitting imax so no intermediate can overflow + // (each operand contribution ≤ its own span/N ≤ the result slot count). + static constexpr bool mixed_offset_ok = []{ + if constexpr (rational_raw || rational_raw || rational_raw + || fp_raw || fp_raw // double raws: no integer offset + || fp_raw || !index_raw + || (IsIntegerAligned && IsIntegerAligned) + || (index_raw && index_raw) + || Notch == 0 || Notch.Numerator != 1) + return false; + else + { + constexpr auto span = Upper - Lower; + if (!span.has_value()) + return false; + const auto slots = *span / Notch; + return slots.has_value() + && (*slots).Numerator + <= static_cast(std::numeric_limits::max()); + } + }(); + + // One operand's offset in result-notch units (see mixed_offset_ok). + // Defined inline (MSVC and constrained partial specializations). + template + static constexpr imax mixed_offset_units(X const& x, imax widen) + { + if constexpr (IsIntegerAligned) + { + constexpr imax den = static_cast(abs_den(Notch.Denominator)); + return (to_value(x) - LowerImax) * den; + } + else + return raw_imax(x) * widen; + } + // Result notch is gcd(NL, NR); scale each raw up to it before adding — // lhs_widen = NL/Nresult, rhs_widen = NR/Nresult (exact, Nresult divides both). // Guard the continuous-grid case (Notch == 0): the rational divide-by-zero @@ -83,14 +124,21 @@ namespace bnd::detail else res = result::from_raw(rational::add_unchecked(lhs, rhs)); } + else if constexpr (mixed_offset_ok) + { + // Mixed integer-aligned / notch-offset operands, pure integer offsets + // (see mixed_offset_ok above). + res = result::from_raw(raw_cast(mixed_offset_units(lhs, lhs_widen) + + mixed_offset_units(rhs, rhs_widen))); + } else if constexpr (rational_raw || rational_raw || !((IsIntegerAligned && IsIntegerAligned) || (index_raw && index_raw))) { - // Rational store: a rational-raw operand, or a direct integer bound mixed - // with a fractional notch-offset one (where neither to_value nor offset-widen - // is exact — to_value would truncate the fractional operand). Compute the - // exact rational sum and convert to result's raw via raw_from_offset. + // Rational store: a rational-raw operand, or a mix the integer fast + // paths can't express exactly (non-unit result notch numerator, or a + // slot count past imax). Compute the exact rational sum and convert to + // result's raw via raw_from_offset. auto sum = rational::add_unchecked(lhs,rhs); res = result::from_raw(raw_from_offset( ((sum - Lower) / Notch).value().Numerator)); diff --git a/include/bound/detail/assignment.hpp b/include/bound/detail/assignment.hpp index d4235b8..146d4e3 100644 --- a/include/bound/detail/assignment.hpp +++ b/include/bound/detail/assignment.hpp @@ -551,6 +551,61 @@ namespace bnd::detail && !fp_raw && !fp_raw && abs_den(Factor.Denominator) == 1 && abs_den(Offset.Denominator) == 1; + // Non-integer mapping folded to one integer multiply-add: + // Offset + Factor·raw = (o_s·f_d + raw·f_n·o_d) / (o_d·f_d) + // with every coefficient compile-time. round_quotient is invariant under + // fraction reduction, so rounding the unreduced pair is bit-identical to + // reducing through the two rational ops first. ok gates on every product + // (including the worst-case runtime numerator over R's raw range) + // provably fitting imax; mul/add/den are zeroed when not ok. + struct affine_map_t { imax mul; imax add; imax den; bool ok; }; + static constexpr affine_map_t affine_map = []{ + constexpr affine_map_t no{0, 0, 0, false}; + if constexpr (rational_raw || rational_raw || fp_raw || fp_raw + || Notch == 0 || is_integer_mapping) + return no; + else + { + constexpr umax cap = static_cast(std::numeric_limits::max()); + if (Factor.Numerator > cap || Offset.Numerator > cap) + return no; + const imax f_n = static_cast(Factor.Numerator); // Factor > 0 + const imax f_d = abs_den(Factor.Denominator); + const imax o_s = (Offset.Denominator < 0) + ? -static_cast(Offset.Numerator) + : static_cast(Offset.Numerator); + const imax o_d = abs_den(Offset.Denominator); + affine_map_t m{0, 0, 0, true}; + if (mul_overflow(f_n, o_d, &m.mul) || mul_overflow(o_s, f_d, &m.add) + || mul_overflow(o_d, f_d, &m.den)) + return no; + // worst-case |numerator| over R's raw range + const imax rmax = std::max(RawHi < 0 ? -RawHi : RawHi, + RawLo < 0 ? -RawLo : RawLo); + imax term, num; + if (mul_overflow(rmax, m.mul, &term) + || add_overflow(term, m.add < 0 ? -m.add : m.add, &num)) + return no; + // round_quotient equivalence: rounding is reduction-invariant, but + // its value-index-vs-offset branch CHOICE keys on m·di + num fitting + // imax — mirror those checks for the unreduced den so both forms + // take the same branch (ties on negatives differ across branches). + constexpr auto zl = (Lower / Notch).value_or(rational{0}); + if (abs_den(zl.Denominator) == 1) + { + if (zl.Numerator > cap) + return no; + const imax mbias = (zl.Denominator < 0) + ? -static_cast(zl.Numerator) + : static_cast(zl.Numerator); + imax mdi, total; + if (mul_overflow(mbias, m.den, &mdi) || add_overflow(mdi, num, &total)) + return no; + } + return m; + } + }(); + // Map rhs.Raw into L's raw space (requires is_integer_mapping). The // Offset/Factor formula assumes offset encoding both sides; for direct // storage, subtract Lower first (R-value → R-offset) and add Lower @@ -668,6 +723,19 @@ namespace bnd::detail else lhs = L::from_raw(raw_cast(map_raw(rhs.raw()))); } + else if constexpr (affine_map.ok) + { + // Folded non-integer mapping: one multiply-add, then the same + // round_quotient (invariant under reduction — bit-identical to the + // rational chain below). + const imax num = affine_map.add + + static_cast(rhs.raw()) * affine_map.mul; + const umax q = round_quotient( + static_cast(num < 0 ? -num : num), + static_cast(affine_map.den)); + lhs = L::from_raw(num < 0 ? raw_from_offset(-static_cast(q)) + : raw_from_offset(q)); + } else { rational rat = *(Offset + *(Factor * rhs.raw())); diff --git a/single_include/bound/bound.hpp b/single_include/bound/bound.hpp index 70365b4..3c5ddec 100644 --- a/single_include/bound/bound.hpp +++ b/single_include/bound/bound.hpp @@ -5324,6 +5324,61 @@ namespace bnd::detail && !fp_raw && !fp_raw && abs_den(Factor.Denominator) == 1 && abs_den(Offset.Denominator) == 1; + // Non-integer mapping folded to one integer multiply-add: + // Offset + Factor·raw = (o_s·f_d + raw·f_n·o_d) / (o_d·f_d) + // with every coefficient compile-time. round_quotient is invariant under + // fraction reduction, so rounding the unreduced pair is bit-identical to + // reducing through the two rational ops first. ok gates on every product + // (including the worst-case runtime numerator over R's raw range) + // provably fitting imax; mul/add/den are zeroed when not ok. + struct affine_map_t { imax mul; imax add; imax den; bool ok; }; + static constexpr affine_map_t affine_map = []{ + constexpr affine_map_t no{0, 0, 0, false}; + if constexpr (rational_raw || rational_raw || fp_raw || fp_raw + || Notch == 0 || is_integer_mapping) + return no; + else + { + constexpr umax cap = static_cast(std::numeric_limits::max()); + if (Factor.Numerator > cap || Offset.Numerator > cap) + return no; + const imax f_n = static_cast(Factor.Numerator); // Factor > 0 + const imax f_d = abs_den(Factor.Denominator); + const imax o_s = (Offset.Denominator < 0) + ? -static_cast(Offset.Numerator) + : static_cast(Offset.Numerator); + const imax o_d = abs_den(Offset.Denominator); + affine_map_t m{0, 0, 0, true}; + if (mul_overflow(f_n, o_d, &m.mul) || mul_overflow(o_s, f_d, &m.add) + || mul_overflow(o_d, f_d, &m.den)) + return no; + // worst-case |numerator| over R's raw range + const imax rmax = std::max(RawHi < 0 ? -RawHi : RawHi, + RawLo < 0 ? -RawLo : RawLo); + imax term, num; + if (mul_overflow(rmax, m.mul, &term) + || add_overflow(term, m.add < 0 ? -m.add : m.add, &num)) + return no; + // round_quotient equivalence: rounding is reduction-invariant, but + // its value-index-vs-offset branch CHOICE keys on m·di + num fitting + // imax — mirror those checks for the unreduced den so both forms + // take the same branch (ties on negatives differ across branches). + constexpr auto zl = (Lower / Notch).value_or(rational{0}); + if (abs_den(zl.Denominator) == 1) + { + if (zl.Numerator > cap) + return no; + const imax mbias = (zl.Denominator < 0) + ? -static_cast(zl.Numerator) + : static_cast(zl.Numerator); + imax mdi, total; + if (mul_overflow(mbias, m.den, &mdi) || add_overflow(mdi, num, &total)) + return no; + } + return m; + } + }(); + // Map rhs.Raw into L's raw space (requires is_integer_mapping). The // Offset/Factor formula assumes offset encoding both sides; for direct // storage, subtract Lower first (R-value → R-offset) and add Lower @@ -5441,6 +5496,19 @@ namespace bnd::detail else lhs = L::from_raw(raw_cast(map_raw(rhs.raw()))); } + else if constexpr (affine_map.ok) + { + // Folded non-integer mapping: one multiply-add, then the same + // round_quotient (invariant under reduction — bit-identical to the + // rational chain below). + const imax num = affine_map.add + + static_cast(rhs.raw()) * affine_map.mul; + const umax q = round_quotient( + static_cast(num < 0 ? -num : num), + static_cast(affine_map.den)); + lhs = L::from_raw(num < 0 ? raw_from_offset(-static_cast(q)) + : raw_from_offset(q)); + } else { rational rat = *(Offset + *(Factor * rhs.raw())); @@ -5956,6 +6024,47 @@ namespace bnd::detail result, return_type_for>; + // Mixed integer-aligned / notch-offset fast path: with a unit-numerator + // result notch 1/d, both operand offsets in result-notch units are exact + // integer math — (to_value − Lower)·d for the integer-aligned operand, + // raw·widen for the notch-offset one (offsets compose because + // Lower = Lower + Lower). Gated on an index-raw result and + // the result slot count fitting imax so no intermediate can overflow + // (each operand contribution ≤ its own span/N ≤ the result slot count). + static constexpr bool mixed_offset_ok = []{ + if constexpr (rational_raw || rational_raw || rational_raw + || fp_raw || fp_raw // double raws: no integer offset + || fp_raw || !index_raw + || (IsIntegerAligned && IsIntegerAligned) + || (index_raw && index_raw) + || Notch == 0 || Notch.Numerator != 1) + return false; + else + { + constexpr auto span = Upper - Lower; + if (!span.has_value()) + return false; + const auto slots = *span / Notch; + return slots.has_value() + && (*slots).Numerator + <= static_cast(std::numeric_limits::max()); + } + }(); + + // One operand's offset in result-notch units (see mixed_offset_ok). + // Defined inline (MSVC and constrained partial specializations). + template + static constexpr imax mixed_offset_units(X const& x, imax widen) + { + if constexpr (IsIntegerAligned) + { + constexpr imax den = static_cast(abs_den(Notch.Denominator)); + return (to_value(x) - LowerImax) * den; + } + else + return raw_imax(x) * widen; + } + // Result notch is gcd(NL, NR); scale each raw up to it before adding — // lhs_widen = NL/Nresult, rhs_widen = NR/Nresult (exact, Nresult divides both). // Guard the continuous-grid case (Notch == 0): the rational divide-by-zero @@ -5994,14 +6103,21 @@ namespace bnd::detail else res = result::from_raw(rational::add_unchecked(lhs, rhs)); } + else if constexpr (mixed_offset_ok) + { + // Mixed integer-aligned / notch-offset operands, pure integer offsets + // (see mixed_offset_ok above). + res = result::from_raw(raw_cast(mixed_offset_units(lhs, lhs_widen) + + mixed_offset_units(rhs, rhs_widen))); + } else if constexpr (rational_raw || rational_raw || !((IsIntegerAligned && IsIntegerAligned) || (index_raw && index_raw))) { - // Rational store: a rational-raw operand, or a direct integer bound mixed - // with a fractional notch-offset one (where neither to_value nor offset-widen - // is exact — to_value would truncate the fractional operand). Compute the - // exact rational sum and convert to result's raw via raw_from_offset. + // Rational store: a rational-raw operand, or a mix the integer fast + // paths can't express exactly (non-unit result notch numerator, or a + // slot count past imax). Compute the exact rational sum and convert to + // result's raw via raw_from_offset. auto sum = rational::add_unchecked(lhs,rhs); res = result::from_raw(raw_from_offset( ((sum - Lower) / Notch).value().Numerator)); diff --git a/tests/bench.cpp b/tests/bench.cpp index cbb7e1c..3418bd8 100644 --- a/tests/bench.cpp +++ b/tests/bench.cpp @@ -319,6 +319,24 @@ static void bench_signed() }); finish(mul); + // mixed integer-grid + quarter-notch-grid add (Tier-3 fast path): the + // native pairing is the same math on a hand-scaled 1/4 fixed-point lattice. + using whole = bound<{0, 100}, unsafe>; + using quarters = bound<{{0, 1}, notch<1, 4>}, unsafe>; + auto mixed = group("add (mixed integer + 1/4-notch grids)"); + mixed.run("native int<<2 + q2", [&] { + ++i; + std::int32_t a = in.a[i & kMask] << 2, b = static_cast(i & 3); + doNotOptimizeAway(a + b); + }); + mixed.run("bound", [&] { + ++i; + whole a(in.a[i & kMask]); + quarters b = quarters::from_raw(static_cast(i & 3)); + doNotOptimizeAway((a + b).raw()); + }); + finish(mixed); + constexpr std::size_t SZ = 1000; std::vector iv(SZ); std::vector bv(SZ); @@ -893,10 +911,11 @@ int main(int argc, char** argv) "— the ratios are the stable signal. Instruction/cycle/branch columns\n" "appear when the kernel grants perf-counter access.\n\n" "Known slow paths (measured in the tables below, by design):\n" - "mixed direct/index-grid addition and cross-grid non-integer stores take\n" - "the exact rational path (see `docs/internals.md`); `bound`\n" - "element-wise loops keep a per-element range check — prefer `bnd::sum`'s\n" - "bulk check, which validates the total instead.\n\n"; + "arithmetic with a rational-raw operand takes the exact rational path\n" + "(the mixed integer/notch-offset add and cross-grid non-integer stores\n" + "now have folded integer fast paths, gated on imax-safe spans);\n" + "`bound` element-wise loops keep a per-element range check —\n" + "prefer `bnd::sum`'s bulk check, which validates the total instead.\n\n"; bench_scalar_u8(); bench_compound(); diff --git a/tests/test_cross_grid.cpp b/tests/test_cross_grid.cpp index de9a25c..c8591b7 100644 --- a/tests/test_cross_grid.cpp +++ b/tests/test_cross_grid.cpp @@ -137,3 +137,33 @@ TEST_CASE("regression: fractional + integer-direct keeps the fraction", "[cross] REQUIRE(static_cast(Frac{5.25} - Int{3}) == rational{9u, 4}); // 2.25 REQUIRE(static_cast(Int{4} - Frac{1.5}) == rational{5u, 2}); // 2.5 } + +//--------------------------------------------------------------------------- +// 2026-07 Tier-3 fast paths: pin that the integer folds stay engaged for the +// shapes they were built for — and stay OUT of the fp-raw shapes (a double +// raw has no integer offset; test_real_exact caught exactly that during +// development). +//--------------------------------------------------------------------------- +TEST_CASE("tier-3 integer fast paths stay engaged (and fp stays excluded)", + "[cross][arith][perf-paths]") +{ + using whole = bound<{0, 100}>; + using quarters = bound<{{0, 1}, notch<1, 4>}>; + STATIC_REQUIRE(detail::addition::mixed_offset_ok); + + using tenths = bound<{{0, 100}, notch<1, 10>}>; + using quarter_grid = bound<{{0, 100}, notch<1, 4>}, round_nearest>; + STATIC_REQUIRE(detail::assignment::affine_map.ok); + + // fp-backed operands must not take the integer offset path. + using coarse_real = bound<{{0, (umax{1} << 40)}, notch<1, 2>}, real>; + using fine_real = bound<{{0, 1}, notch<1, (1u << 20)>}, real>; + if constexpr (detail::fp_raw) + STATIC_REQUIRE_FALSE(detail::addition::mixed_offset_ok); + + // value check across a negative Lower, at compile time (constexpr path). + using signed_whole = bound<{-50, 50}>; + using eighths = bound<{{-2, 2}, notch<1, 8>}>; + STATIC_REQUIRE(rational{signed_whole{-7} + eighths{-0.625_b}} + == rational{umax{61}, imax{-8}}); +}