Skip to content

Eliminate memory allocations in GuidCombGenerator under .NET 8+ #3610

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

Merged
merged 5 commits into from
Mar 20, 2025

Conversation

cd21h
Copy link
Contributor

@cd21h cd21h commented Sep 25, 2024

Replace memory allocations with stack allocations in GuidComboGenerator under .NET 8+

ToByteArray() and GetBytes() are replaced with non-allocating equivalents.

@hazzik
Copy link
Member

hazzik commented Sep 30, 2024

What is performance gain here?

@hazzik
Copy link
Member

hazzik commented Sep 30, 2024

I would just go with this implementation:

protected static Guid GenerateComb(Guid guid, DateTime utcNow)
{
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
	Span<byte> guidArray = stackalloc byte[16];
	guid.TryWriteBytes(guidArray);
#else
	var guidArray = guid.ToByteArray();
#endif
	// Get the days and milliseconds which will be used to build the byte string 
	var ts = new TimeSpan(utcNow.Ticks - BaseDateTicks);
	var days = ts.Days;
	guidArray[10] = (byte) (days >> 8);
	guidArray[11] = (byte) days;
			
	// Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
	var msecs = (long) (utcNow.TimeOfDay.TotalMilliseconds / 3.333333);
	guidArray[12] = (byte) (msecs >> 24);
	guidArray[13] = (byte) (msecs >> 16);
	guidArray[14] = (byte) (msecs >> 8);
	guidArray[15] = (byte) msecs;

	return new Guid(guidArray);
}

@cd21h
Copy link
Contributor Author

cd21h commented Oct 1, 2024

What is performance gain here?
@hazzik

Optimized1 - proposed version in PR
Optimized2 - your version

// * Summary *

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22621.4169/22H2/2022Update/SunValley2)
11th Gen Intel Core i7-11800H 2.30GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.100-preview.4.24267.66
  [Host]   : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 8.0 : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 9.0 : .NET 9.0.0 (9.0.24.26619), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI


| Method         | Job      | Runtime  | Mean      | Error     | StdDev    | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|--------------- |--------- |--------- |----------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| OriginalMethod | .NET 8.0 | .NET 8.0 | 27.930 ns | 0.5818 ns | 0.7565 ns |  1.00 |    0.04 | 0.0083 |     104 B |        1.00 |
| Optimized1     | .NET 8.0 | .NET 8.0 |  6.973 ns | 0.1096 ns | 0.1025 ns |  0.25 |    0.01 |      - |         - |        0.00 |
| Optimized2     | .NET 8.0 | .NET 8.0 |  5.481 ns | 0.0431 ns | 0.0382 ns |  0.20 |    0.01 |      - |         - |        0.00 |
|                |          |          |           |           |           |       |         |        |           |             |
| OriginalMethod | .NET 9.0 | .NET 9.0 | 25.708 ns | 0.2555 ns | 0.2134 ns |  1.00 |    0.01 | 0.0083 |     104 B |        1.00 |
| Optimized1     | .NET 9.0 | .NET 9.0 |  8.416 ns | 0.0594 ns | 0.0496 ns |  0.33 |    0.00 |      - |         - |        0.00 |
| Optimized2     | .NET 9.0 | .NET 9.0 |  5.687 ns | 0.0257 ns | 0.0201 ns |  0.22 |    0.00 |      - |         - |        0.00 |

// * Hints *
Outliers
  GuidCombGeneratorBenchmark.OriginalMethod: .NET 8.0 -> 1 outlier  was  removed (32.05 ns)
  GuidCombGeneratorBenchmark.Optimized2: .NET 8.0     -> 1 outlier  was  removed (7.44 ns)
  GuidCombGeneratorBenchmark.OriginalMethod: .NET 9.0 -> 2 outliers were removed (28.37 ns, 29.39 ns)
  GuidCombGeneratorBenchmark.Optimized1: .NET 9.0     -> 2 outliers were removed, 3 outliers were detected (10.00 ns, 10.23 ns, 10.27 ns)
  GuidCombGeneratorBenchmark.Optimized2: .NET 9.0     -> 3 outliers were removed (7.47 ns..7.66 ns)

// * Legends *
  Mean        : Arithmetic mean of all measurements
  Error       : Half of 99.9% confidence interval
  StdDev      : Standard deviation of all measurements
  Ratio       : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD     : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Gen0        : GC Generation 0 collects per 1000 operations
  Allocated   : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  Alloc Ratio : Allocated memory ratio distribution ([Current]/[Baseline])
  1 ns        : 1 Nanosecond (0.000000001 sec)

@hazzik hazzik changed the title Micro optimization: Eliminate memory allocations in GuidCombGenerator under .NET 8+ Eliminate memory allocations in GuidCombGenerator under .NET 8+ Nov 7, 2024
@hazzik hazzik added this to the 5.6 milestone Mar 19, 2025
@hazzik hazzik merged commit 916c6dc into nhibernate:master Mar 20, 2025
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants