-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use getrandom on Linux for Guid generation for a 12% speed boost. #111287
base: main
Are you sure you want to change the base?
Conversation
Since Linux 3.17, the syscall `getrandom` can be used to generate bytes from the same entropy source as `/dev/urandom`. This makes it suitable for cryptographic random number generation, something our Guid generator needs. If `getrandom` is not available because the LibC provided is too old - or if the syscall itself is not available, it will fall back to the previous method of reading from `/dev/urandom`. `EPERM` is treated like `ENOSYS` - some older Linux kernels returned this when the syscall is not available. The syscall can also be blocked by seccomp. That would be weird, and Docker permits it by default, but if anyone has explicitly gone out of their way to block it, it falls back to using the file descriptor mechanism. The use of the syscall implementation instead of the file device offers some performance improvements. Anywhere from 10% to 15% in benchmarks. This does require glibc 2.25. For non-portable builds, that's largely fine. For Microsoft builds, those are (currently) being done on Ubuntu Xenial, which has 2.23. However, it is expected that with .NET 10, the glibc floor is going to change to 2.27 with dotnet#109939. If that happens, then the Microsoft portable builds will pick this up as well. This PR does _not_ change the user-facing `RandomNumberGenerator` that will continue to use `RAND_bytes` from OpenSSL. | Method | Toolchain | Mean | Error | StdDev | Ratio | |--------------- |---------- |---------:|--------:|--------:|------:| | NewGuid | branch | 265.1 ns | 0.24 ns | 0.22 ns | 0.87 | | NewGuid | main | 304.6 ns | 0.16 ns | 0.15 ns | 1.00 | | | | | | | | | CreateVersion7 | branch | 293.2 ns | 0.20 ns | 0.18 ns | 0.88 | | CreateVersion7 | main | 335.0 ns | 0.27 ns | 0.23 ns | 1.00 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we extract the new code-path into a static
function that returns true if getrandom was used and false if the device random should be used? I'd like to get rid of the goto if we can.
src/native/minipal/random.c
Outdated
static bool sMissingGetRandomSysCall = false; | ||
|
||
if (!sMissingGetRandomSysCall) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static bool sMissingGetRandomSysCall = false; | |
if (!sMissingGetRandomSysCall) | |
static short sGetRandomSupport = -1; | |
if (sGetRandomSupport == -1) | |
{ | |
sGetRandomSupport = syscall(SYS_getrandom, nullptr, 0, GRND_NONBLOCK) == -1 && errno == ENOSYS ? 0 : 1; | |
} | |
if (sGetRandomSupport == 1) |
This run-time introspection will only run once; the first time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this better than using the getrandom
provided by the C runtime?
This looks good. I missed that you already handled ENOSYS. :) It will also improve #31271 since it also ends up calling minipal implementation. @vcsjones, I posted a benchmark with managed impl. here, if case you want to try out #31271 (comment). |
Since Linux 3.17, the syscall
getrandom
can be used to generate bytes from the same entropy source as/dev/urandom
. This makes it suitable for cryptographic random number generation, something our Guid generator needs.If
getrandom
is not available because the LibC provided is too old - or if the syscall itself is not available, it will fall back to the previous method of reading from/dev/urandom
.EPERM
is treated likeENOSYS
- some older Linux kernels returned this when the syscall is not available. The syscall can also be blocked by seccomp. That would be weird, and Docker permits it by default, but if anyone has explicitly gone out of their way to block it, it falls back to using the file descriptor mechanism.The use of the syscall implementation instead of the file device offers some performance improvements. Anywhere from 10% to 15% in benchmarks.
This does require glibc 2.25. For non-portable builds, that's largely fine. For Microsoft builds, those are (currently) being done on Ubuntu Xenial, which has 2.23. However, it is expected that with .NET 10, the glibc floor is going to change to 2.27 with #109939. When that happens, then the Microsoft portable builds will pick this up as well.
This PR does not change the user-facing
RandomNumberGenerator
that will continue to useRAND_bytes
from OpenSSL.