Skip to content
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

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

cd21h
Copy link

@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
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants