runtime: Guid.NewGuid() is 10x slower on Linux compared to Windows

Guid.NewGuid() is 10 times slower on Linux compared to Windows.

Slower diff/base Windows Median (ns) Linux Median (ns) Modality
System.Tests.Perf_Guid.NewGuid 9.52 77.99 742.10

The contributor who wants to work on this issue should:

  1. Run this simple benchmark from dotnet/performance repository and confirm the problem
git clone https://github.com/dotnet/performance.git
python3 ./performance/scripts/benchmarks_ci.py -f netcoreapp5.0 --filter System.Tests.Perf_Guid.NewGuid
  1. Build CoreCLR locally: https://github.com/dotnet/performance/blob/master/docs/profiling-workflow-corefx.md#Build
  2. Create a small repro app: https://github.com/dotnet/performance/blob/master/docs/profiling-workflow-corefx.md#repro
  3. Use PerfCollect to identify issue https://github.com/dotnet/performance/blob/master/docs/profiling-workflow-corefx.md#PerfCollect
  4. Solve the issue

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Comments: 30 (30 by maintainers)

Most upvoted comments

Feel free to reopen if there is work here.

I think there is a work here:

  • First, we should look into using OS APIs with less fixed overhead than reading from /dev/urandom. It should be faster to call getrandom syscall on Linux (started in #1433, but never finished) or SecRandomCopyBytes on OSX.
  • If it is not good enough, we may consider generating the random numbers in batches as suggested above, but such strategy comes with tradeoffs.

@iSazonov You’d continue to use the Framework-provided APIs for generating GUIDs. What I was suggesting is that if you were willing to take the hit of generating a bunch (16? 32?) upfront in a non-perf-critical part of your code, then you could drain that cache in the perf-critical part of your code. There’s no need to roll your own crypto.

If you don’t copy and paste something like that or take over the behavior 1:1 I don’t see any license problems here.

That’s not how licensing works. Any derivative of the work is tainted, even if it’s because it influenced the code you later wrote. That’s why many folks adopt a “cooling off” period after seeing such licensed code, in order to avoid accidentally using something they may have remembered from it.

That code also isn’t going to be very relevant. The kernel needs to maintain its own source of cryptographic entropy, something we wouldn’t want to even attempt replicating.

@deeprobin The links you have shared are into GPL licensed projects. GPL license is incompatible with the license of this project. Please refrain from looking at GPL licensed code for areas you would like to contribute for next time.

I still believe that the next thing to do here is to switch from reading urandom to the getrandom syscall (https://github.com/dotnet/runtime/issues/13628#issuecomment-699559874) to reduce the overhead.

Our Guid.NewGuid() implementation on Windows delegates to CoCreateGuid, which as an implementation detail creates GUIDs in batches and caches them. Our Guid.NewGuid() implementation on non-Windows creates new GUIDs on-demand without caching. That probably explains the perf difference.

Edit: Apparently the implementation detail has changed since the last time I looked at it. Both Windows and non-Windows call the PRNG directly every time, at least on latest Win10.