RFR: 8305092: Improve Thread.sleep(millis, nanos) for sub-millisecond granularity
Roger Riggs
rriggs at openjdk.org
Wed Mar 29 16:57:48 UTC 2023
On Wed, 29 Mar 2023 11:28:53 GMT, Aleksey Shipilev <shade at openjdk.org> wrote:
> Java API has the `Thread.sleep(millis, nanos)` method exposed to users. The documentation for that method clearly says the precision and accuracy are dependent on the underlying system behavior. However, it always rounds up `nanos` to 1ms when doing the actual sleep. This means users cannot do the micro-second precision sleeps, even when the underlying platform allows it. Sub-millisecond sleeps are useful to build interesting primitives, like the rate limiters that run with >1000 RPS.
>
> When faced with this, some users reach for more awkward APIs like `java.util.concurrent.locks.LockSupport.parkNanos`. The use of that API for sleeps is not in line with its intent, and while it "seems to work", it might have interesting interactions with other uses of `LockSupport`. Additionally, these "sleeps" are no longer visible to monitoring tools as "normal sleeps", e.g. as `Thread.sleep` events. Therefore, it would be prudent to improve current `Thread.sleep(millis, nanos)` for sub-millisecond granularity.
>
> Fortunately, the underlying code is almost ready for this, at least on POSIX side. I skipped Windows paths, because its timers are still no good. Note that on both Linux and MacOS timers oversleep by about 50us. I have a few ideas how to improve the accuracy for them, which would be a topic for a separate PR.
>
> Motivational improvements on new benchmark below:
>
> m6g.9xlarge (Linux, Xeon, x86_64):
>
>
> Benchmark (sleep) Mode Cnt Score Error Units
>
> # ========= no regressions for Thread.sleep(millis)
>
> # Baseline
> ThreadSleep.millis 0 avgt 5 463.606 ± 8.256 ns/op
> ThreadSleep.millis 1 avgt 5 459.434 ± 0.453 ns/op
> ThreadSleep.millis 10 avgt 5 462.828 ± 1.283 ns/op
> ThreadSleep.millis 100 avgt 5 459.495 ± 3.244 ns/op
> ThreadSleep.millis 1000 avgt 5 457.962 ± 5.769 ns/op
> ThreadSleep.millis 10000 avgt 5 457.960 ± 1.195 ns/op
> ThreadSleep.millis 100000 avgt 5 459.197 ± 3.054 ns/op
> ThreadSleep.millis 1000000 avgt 5 1058342.873 ± 304.169 ns/op
> ThreadSleep.millis 10000000 avgt 5 10059945.148 ± 1642.411 ns/op
> ThreadSleep.millis 100000000 avgt 5 100062181.340 ± 1863.186 ns/op
> ThreadSleep.millis 1000000000 avgt 5 1000070015.600 ± 11379.631 ns/op
>
> # Patched
> ThreadSleep.millis 0 avgt 5 461.295 ± 6.700 ns/op
> ThreadSleep.millis 1 avgt 5 463.383 ± 3.065 ns/op
> ThreadSleep.millis 10 avgt 5 468.109 ± 1.607 ns/op
> ThreadSleep.millis 100 avgt 5 467.316 ± 0.270 ns/op
> ThreadSleep.millis 1000 avgt 5 461.999 ± 2.341 ns/op
> ThreadSleep.millis 10000 avgt 5 465.075 ± 0.324 ns/op
> ThreadSleep.millis 100000 avgt 5 462.588 ± 5.321 ns/op
> ThreadSleep.millis 1000000 avgt 5 1058285.440 ± 409.376 ns/op
> ThreadSleep.millis 10000000 avgt 5 10060437.490 ± 177.754 ns/op
> ThreadSleep.millis 100000000 avgt 5 100060665.980 ± 1167.514 ns/op
> ThreadSleep.millis 1000000000 avgt 5 1000071917.300 ± 17438.906 ns/op
>
> # ========= Significant improvements for Thread.sleep(millis, nanos)
>
> # Baseline
> ThreadSleep.millisNanos 0 avgt 5 462.831 ± 0.251 ns/op
> ThreadSleep.millisNanos 1 avgt 5 1058708.668 ± 960.683 ns/op
> ThreadSleep.millisNanos 10 avgt 5 1058364.414 ± 197.700 ns/op
> ThreadSleep.millisNanos 100 avgt 5 1058451.889 ± 561.979 ns/op
> ThreadSleep.millisNanos 1000 avgt 5 1058372.010 ± 96.867 ns/op
> ThreadSleep.millisNanos 10000 avgt 5 1058354.100 ± 297.148 ns/op
> ThreadSleep.millisNanos 100000 avgt 5 1058485.092 ± 1093.340 ns/op
> ThreadSleep.millisNanos 1000000 avgt 5 1059004.036 ± 1899.950 ns/op
> ThreadSleep.millisNanos 10000000 avgt 5 10059733.302 ± 1725.908 ns/op
> ThreadSleep.millisNanos 100000000 avgt 5 100063327.840 ± 6244.805 ns/op
> ThreadSleep.millisNanos 1000000000 avgt 5 1000074542.300 ± 17898.173 ns/op
>
> # Patched
> ThreadSleep.millisNanos 0 avgt 5 467.968 ± 9.202 ns/op
> ThreadSleep.millisNanos 1 avgt 5 579.448 ± 1.363 ns/op
> ThreadSleep.millisNanos 10 avgt 5 577.656 ± 0.278 ns/op
> ThreadSleep.millisNanos 100 avgt 5 58092.815 ± 2410.401 ns/op
> ThreadSleep.millisNanos 1000 avgt 5 58592.249 ± 189.232 ns/op
> ThreadSleep.millisNanos 10000 avgt 5 67668.688 ± 591.365 ns/op
> ThreadSleep.millisNanos 100000 avgt 5 158143.164 ± 2281.540 ns/op
> ThreadSleep.millisNanos 1000000 avgt 5 1058508.682 ± 155.767 ns/op
> ThreadSleep.millisNanos 10000000 avgt 5 10059979.272 ± 1095.207 ns/op
> ThreadSleep.millisNanos 100000000 avgt 5 100066844.180 ± 4287.440 ns/op
> ThreadSleep.millisNanos 1000000000 avgt 5 1000075313.100 ± 9665.656 ns/op
>
>
> m6g.4xlarge (Linux, Graviton, AArch64):
>
>
> # ========= no regressions for Thread.sleep(millis)
>
> # Baseline
> ThreadSleep.millis 0 avgt 5 429.403 ± 24.947 ns/op
> ThreadSleep.millis 1 avgt 5 427.533 ± 7.969 ns/op
> ThreadSleep.millis 10 avgt 5 438.464 ± 10.277 ns/op
> ThreadSleep.millis 100 avgt 5 425.626 ± 3.371 ns/op
> ThreadSleep.millis 1000 avgt 5 426.465 ± 4.501 ns/op
> ThreadSleep.millis 10000 avgt 5 427.235 ± 6.916 ns/op
> ThreadSleep.millis 100000 avgt 5 430.311 ± 4.073 ns/op
> ThreadSleep.millis 1000000 avgt 5 1054808.881 ± 469.409 ns/op
> ThreadSleep.millis 10000000 avgt 5 10055497.206 ± 2123.591 ns/op
> ThreadSleep.millis 100000000 avgt 5 100059355.520 ± 11597.426 ns/op
> ThreadSleep.millis 1000000000 avgt 5 1000073145.400 ± 7659.244 ns/op
>
> # Patched
> ThreadSleep.millis 0 avgt 5 429.535 ± 2.597 ns/op
> ThreadSleep.millis 1 avgt 5 425.209 ± 3.620 ns/op
> ThreadSleep.millis 10 avgt 5 432.416 ± 9.350 ns/op
> ThreadSleep.millis 100 avgt 5 432.867 ± 3.978 ns/op
> ThreadSleep.millis 1000 avgt 5 426.627 ± 1.064 ns/op
> ThreadSleep.millis 10000 avgt 5 427.180 ± 4.691 ns/op
> ThreadSleep.millis 100000 avgt 5 438.554 ± 4.271 ns/op
> ThreadSleep.millis 1000000 avgt 5 1054693.226 ± 323.240 ns/op
> ThreadSleep.millis 10000000 avgt 5 10056408.240 ± 1759.211 ns/op
> ThreadSleep.millis 100000000 avgt 5 100062449.780 ± 15133.808 ns/op
> ThreadSleep.millis 1000000000 avgt 5 1000066918.500 ± 15817.822 ns/op
>
> # ========= Significant improvements for Thread.sleep(millis, nanos)
>
> # Baseline
> ThreadSleep.millisNanos 0 avgt 5 439.163 ± 5.559 ns/op
> ThreadSleep.millisNanos 1 avgt 5 1054751.231 ± 431.842 ns/op
> ThreadSleep.millisNanos 10 avgt 5 1054640.725 ± 525.901 ns/op
> ThreadSleep.millisNanos 100 avgt 5 1054807.619 ± 1009.104 ns/op
> ThreadSleep.millisNanos 1000 avgt 5 1054894.829 ± 1946.551 ns/op
> ThreadSleep.millisNanos 10000 avgt 5 1054765.444 ± 701.873 ns/op
> ThreadSleep.millisNanos 100000 avgt 5 1054707.758 ± 619.054 ns/op
> ThreadSleep.millisNanos 1000000 avgt 5 1054802.230 ± 1294.035 ns/op
> ThreadSleep.millisNanos 10000000 avgt 5 10055265.430 ± 1184.281 ns/op
> ThreadSleep.millisNanos 100000000 avgt 5 100057484.300 ± 7960.939 ns/op
> ThreadSleep.millisNanos 1000000000 avgt 5 1000062244.700 ± 20244.067 ns/op
>
> # Patched
> ThreadSleep.millisNanos 0 avgt 5 426.482 ± 10.284 ns/op
> ThreadSleep.millisNanos 1 avgt 5 565.819 ± 1.098 ns/op
> ThreadSleep.millisNanos 10 avgt 5 564.108 ± 3.493 ns/op
> ThreadSleep.millisNanos 100 avgt 5 53698.839 ± 942.506 ns/op
> ThreadSleep.millisNanos 1000 avgt 5 55205.929 ± 416.951 ns/op
> ThreadSleep.millisNanos 10000 avgt 5 64206.709 ± 382.213 ns/op
> ThreadSleep.millisNanos 100000 avgt 5 154477.045 ± 125.316 ns/op
> ThreadSleep.millisNanos 1000000 avgt 5 1054889.472 ± 2152.765 ns/op
> ThreadSleep.millisNanos 10000000 avgt 5 10055273.582 ± 691.628 ns/op
> ThreadSleep.millisNanos 100000000 avgt 5 100058261.300 ± 5222.946 ns/op
> ThreadSleep.millisNanos 1000000000 avgt 5 1000065434.000 ± 30213.137 ns/op
>
>
> MacBook 2021 (MacOS X, M1, AArch64):
>
>
> # ========= no regressions for Thread.sleep(millis)
>
> # Baseline
> Benchmark (sleep) Mode Cnt Score Error Units
> ThreadSleep.millis 0 avgt 5 555,871 ± 525,544 ns/op
> ThreadSleep.millis 1 avgt 5 405,761 ± 153,981 ns/op
> ThreadSleep.millis 10 avgt 5 403,906 ± 279,245 ns/op
> ThreadSleep.millis 100 avgt 5 402,787 ± 223,658 ns/op
> ThreadSleep.millis 1000 avgt 5 439,250 ± 374,150 ns/op
> ThreadSleep.millis 10000 avgt 5 435,769 ± 352,067 ns/op
> ThreadSleep.millis 100000 avgt 5 1853,664 ± 7254,716 ns/op
> ThreadSleep.millis 1000000 avgt 5 1364788,214 ± 66446,869 ns/op
> ThreadSleep.millis 10000000 avgt 5 11819681,019 ± 473810,685 ns/op
> ThreadSleep.millis 100000000 avgt 5 103536901,660 ± 1423282,032 ns/op
> ThreadSleep.millis 1000000000 avgt 5 1002995262,600 ± 6576388,880 ns/op
>
> # Patched
> ThreadSleep.millis 0 avgt 5 662,597 ± 459,888 ns/op
> ThreadSleep.millis 1 avgt 5 433,561 ± 214,850 ns/op
> ThreadSleep.millis 10 avgt 5 388,614 ± 65,199 ns/op
> ThreadSleep.millis 100 avgt 5 436,823 ± 636,901 ns/op
> ThreadSleep.millis 1000 avgt 5 337,046 ± 129,440 ns/op
> ThreadSleep.millis 10000 avgt 5 366,557 ± 510,494 ns/op
> ThreadSleep.millis 100000 avgt 5 278,339 ± 102,443 ns/op
> ThreadSleep.millis 1000000 avgt 5 1520092,133 ± 26217,972 ns/op
> ThreadSleep.millis 10000000 avgt 5 13116509,001 ± 855508,621 ns/op
> ThreadSleep.millis 100000000 avgt 5 107010708,340 ± 1667080,292 ns/op
> ThreadSleep.millis 1000000000 avgt 5 1005412095,700 ± 6141267,753 ns/op
>
> # ========= Significant improvements for Thread.sleep(millis, nanos)
>
> # Baseline
> ThreadSleep.millisNanos 0 avgt 5 245,942 ± 44,338 ns/op
> ThreadSleep.millisNanos 1 avgt 5 1313354,886 ± 71820,989 ns/op
> ThreadSleep.millisNanos 10 avgt 5 1310358,568 ± 42731,583 ns/op
> ThreadSleep.millisNanos 100 avgt 5 1335221,078 ± 122031,471 ns/op
> ThreadSleep.millisNanos 1000 avgt 5 1313977,625 ± 30683,810 ns/op
> ThreadSleep.millisNanos 10000 avgt 5 1553321,472 ± 75181,324 ns/op
> ThreadSleep.millisNanos 100000 avgt 5 1553317,573 ± 132998,558 ns/op
> ThreadSleep.millisNanos 1000000 avgt 5 1544944,015 ± 41587,129 ns/op
> ThreadSleep.millisNanos 10000000 avgt 5 13228565,376 ± 902384,381 ns/op
> ThreadSleep.millisNanos 100000000 avgt 5 106866740,840 ± 2518606,524 ns/op
> ThreadSleep.millisNanos 1000000000 avgt 5 1006669020,800 ± 10961093,568 ns/op
>
> # Patched
> ThreadSleep.millisNanos 0 avgt 5 394,716 ± 310,886 ns/op
> ThreadSleep.millisNanos 1 avgt 5 9945,543 ± 905,136 ns/op
> ThreadSleep.millisNanos 10 avgt 5 9863,605 ± 557,909 ns/op
> ThreadSleep.millisNanos 100 avgt 5 2454,862 ± 323,279 ns/op
> ThreadSleep.millisNanos 1000 avgt 5 3834,507 ± 2427,033 ns/op
> ThreadSleep.millisNanos 10000 avgt 5 16667,415 ± 508,814 ns/op
> ThreadSleep.millisNanos 100000 avgt 5 154202,589 ± 258,406 ns/op
> ThreadSleep.millisNanos 1000000 avgt 5 1557886,136 ± 192080,832 ns/op
> ThreadSleep.millisNanos 10000000 avgt 5 13077385,237 ± 887650,194 ns/op
> ThreadSleep.millisNanos 100000000 avgt 5 106225257,480 ± 3288589,410 ns/op
> ThreadSleep.millisNanos 1000000000 avgt 5 1006589558,200 ± 3677828,811 ns/op
>
>
> Additional testing:
> - [x] New regression test
> - [x] New benchmark
> - [x] Linux x86_64 `tier1`
> - [x] Linux AArch64 `tier1`
@shipilev Given that Github includes the entire problem description in every email, can you move the benchmark stats to a comment and out of the description. Thanks.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/13225#issuecomment-1488962749
More information about the core-libs-dev
mailing list