Skip to content

Commit 2aff9df

Browse files
authored
Update benchmarks and library versions (#9)
- Updated README.md with new benchmark results and library versions, including NUlid (1.7.2 to 1.7.3) and BenchmarkDotNet (0.14.0 to 0.15.2). - Added a new benchmark method for `GuidV7` - Modified ByteAether.Benchmarks.csproj and ByteAether.Ulid.Tests.csproj to update package versions and add conditional references. - Updated ByteAether.Ulid.csproj to newer versions of System.Memory (4.6.3) and System.Runtime.CompilerServices.Unsafe (6.1.2).
1 parent 33b5bfb commit 2aff9df

File tree

5 files changed

+82
-69
lines changed

5 files changed

+82
-69
lines changed

README.md

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -339,87 +339,89 @@ var deserializedObject = JsonConvert.DeserializeObject<MyObject>(json, settings)
339339
## Benchmarking
340340
To ensure the performance and efficiency of this ULID implementation, benchmarking was conducted using [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet).
341341

342-
For comparison, [NetUlid](https://github.com/ultimicro/netulid) 2.1.0, [Ulid](https://github.com/Cysharp/Ulid) 1.3.4 and [NUlid](https://github.com/RobThree/NUlid) 1.7.2 implementations were benchmarked alongside.
342+
For comparison, [NetUlid](https://github.com/ultimicro/netulid) 2.1.0, [Ulid](https://github.com/Cysharp/Ulid) 1.3.4 and [NUlid](https://github.com/RobThree/NUlid) 1.7.3 implementations were benchmarked alongside ByteAether.Ulid v1.1.1.
343343

344344
Benchmark scenarios also include comparisons against `Guid`, where functionality overlaps, such as creation, parsing, and byte conversions.
345345

346346
The following benchmarks were performed:
347347
```
348-
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update)
348+
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5965/22H2/2022Update)
349349
AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores
350-
.NET SDK 9.0.101
351-
[Host] : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
352-
DefaultJob : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
350+
.NET SDK 9.0.301
351+
[Host] : .NET 9.0.6 (9.0.625.26613), X64 RyuJIT AVX2
352+
DefaultJob : .NET 9.0.6 (9.0.625.26613), X64 RyuJIT AVX2
353353
354354
Job=DefaultJob
355355
356356
| Type | Method | Mean | Error | Gen0 | Allocated |
357357
|---------------- |--------------- |------------:|----------:|-------:|----------:|
358-
| Generate | ByteAetherUlid | 52.6567 ns | 0.1117 ns | - | - |
359-
| Generate | NetUlid *(1) | 156.6746 ns | 0.5405 ns | 0.0095 | 80 B |
360-
| Generate | NUlid *(2) | 72.7365 ns | 0.4196 ns | 0.0124 | 104 B |
361-
362-
| GenerateNonMono | ByteAetherUlid | 92.5597 ns | 0.0494 ns | - | - |
363-
| GenerateNonMono | Ulid *(3,4) | 43.4534 ns | 0.0650 ns | - | - |
364-
| GenerateNonMono | NUlid | 111.1453 ns | 0.1737 ns | 0.0124 | 104 B |
365-
| GenerateNonMono | Guid *(5) | 46.5859 ns | 0.0876 ns | - | - |
366-
367-
| FromByteArray | ByteAetherUlid | 0.0191 ns | 0.0017 ns | - | - |
368-
| FromByteArray | NetUlid | 0.6626 ns | 0.0047 ns | - | - |
369-
| FromByteArray | Ulid | 6.8877 ns | 0.0116 ns | - | - |
370-
| FromByteArray | NUlid | 10.6222 ns | 0.0247 ns | - | - |
371-
| FromByteArray | Guid | 0.0266 ns | 0.0058 ns | - | - |
372-
373-
| FromGuid | ByteAetherUlid | 1.4260 ns | 0.0023 ns | - | - |
374-
| FromGuid | NetUlid | 5.1155 ns | 0.0325 ns | 0.0048 | 40 B |
375-
| FromGuid | Ulid | 1.6749 ns | 0.0022 ns | - | - |
376-
| FromGuid | NUlid | 14.0285 ns | 0.0584 ns | 0.0048 | 40 B |
377-
378-
| FromString | ByteAetherUlid | 14.4420 ns | 0.0361 ns | - | - |
379-
| FromString | NetUlid | 26.7656 ns | 0.0810 ns | - | - |
380-
| FromString | Ulid | 14.8101 ns | 0.0499 ns | - | - |
381-
| FromString | NUlid | 85.7495 ns | 0.5573 ns | 0.0324 | 272 B |
382-
| FromString | Guid | 22.3398 ns | 0.1021 ns | - | - |
383-
384-
| ToByteArray | ByteAetherUlid | 3.9530 ns | 0.0337 ns | 0.0048 | 40 B |
385-
| ToByteArray | NetUlid | 11.4354 ns | 0.0585 ns | 0.0048 | 40 B |
386-
| ToByteArray | Ulid | 4.2924 ns | 0.0246 ns | 0.0048 | 40 B |
387-
| ToByteArray | NUlid | 6.5988 ns | 0.0367 ns | 0.0048 | 40 B |
388-
389-
| ToGuid | ByteAetherUlid | 0.2557 ns | 0.0062 ns | - | - |
390-
| ToGuid | NetUlid | 11.8685 ns | 0.0424 ns | 0.0048 | 40 B |
391-
| ToGuid | Ulid | 0.7235 ns | 0.0012 ns | - | - |
392-
| ToGuid | NUlid | 11.7772 ns | 0.0501 ns | 0.0048 | 40 B |
393-
394-
| ToString | ByteAetherUlid | 19.9519 ns | 0.2485 ns | 0.0095 | 80 B |
395-
| ToString | NetUlid | 22.4069 ns | 0.1745 ns | 0.0095 | 80 B |
396-
| ToString | Ulid | 20.9287 ns | 0.1969 ns | 0.0095 | 80 B |
397-
| ToString | NUlid | 52.9361 ns | 0.0952 ns | 0.0430 | 360 B |
398-
| ToString | Guid | 11.5112 ns | 0.0107 ns | 0.0115 | 96 B |
399-
400-
| CompareTo | ByteAetherUlid | 0.0205 ns | 0.0073 ns | - | - |
401-
| CompareTo | NetUlid | 3.2167 ns | 0.0156 ns | - | - |
402-
| CompareTo | Ulid | 1.9634 ns | 0.0077 ns | - | - |
403-
| CompareTo | NUlid | 8.9502 ns | 0.0789 ns | 0.0048 | 40 B |
358+
| Generate | ByteAetherUlid | 52.5884 ns | 0.1624 ns | - | - |
359+
| Generate | NetUlid *(1) | 157.7909 ns | 0.2937 ns | 0.0095 | 80 B |
360+
| Generate | NUlid *(2) | 59.5673 ns | 0.1030 ns | - | - |
361+
362+
| GenerateNonMono | ByteAetherUlid | 94.7806 ns | 0.1406 ns | - | - |
363+
| GenerateNonMono | Ulid *(3,4) | 43.7608 ns | 0.0845 ns | - | - |
364+
| GenerateNonMono | NUlid | 98.1727 ns | 0.2122 ns | - | - |
365+
| GenerateNonMono | Guid *(5) | 46.5160 ns | 0.0599 ns | - | - |
366+
| GenerateNonMono | GuidV7 *(3,5) | 81.2190 ns | 0.1536 ns | - | - |
367+
368+
| FromByteArray | ByteAetherUlid | 0.2563 ns | 0.0075 ns | - | - |
369+
| FromByteArray | NetUlid | 0.6812 ns | 0.0086 ns | - | - |
370+
| FromByteArray | Ulid | 6.9435 ns | 0.0100 ns | - | - |
371+
| FromByteArray | NUlid | 1.9263 ns | 0.0133 ns | - | - |
372+
| FromByteArray | Guid | 0.0224 ns | 0.0044 ns | - | - |
373+
374+
| FromGuid | ByteAetherUlid | 1.4399 ns | 0.0089 ns | - | - |
375+
| FromGuid | NetUlid | 4.6075 ns | 0.0257 ns | 0.0048 | 40 B |
376+
| FromGuid | Ulid | 1.4525 ns | 0.0102 ns | - | - |
377+
| FromGuid | NUlid | 4.7050 ns | 0.0204 ns | - | - |
378+
379+
| FromString | ByteAetherUlid | 14.5609 ns | 0.0296 ns | - | - |
380+
| FromString | NetUlid | 26.9962 ns | 0.2493 ns | - | - |
381+
| FromString | Ulid | 15.5439 ns | 0.3153 ns | - | - |
382+
| FromString | NUlid | 57.0196 ns | 0.0652 ns | 0.0124 | 104 B |
383+
| FromString | Guid | 23.1006 ns | 0.2679 ns | - | - |
384+
385+
| ToByteArray | ByteAetherUlid | 3.2943 ns | 0.0420 ns | 0.0048 | 40 B |
386+
| ToByteArray | NetUlid | 9.1148 ns | 0.1869 ns | 0.0048 | 40 B |
387+
| ToByteArray | Ulid | 3.2581 ns | 0.0123 ns | 0.0048 | 40 B |
388+
| ToByteArray | NUlid | 6.4877 ns | 0.0203 ns | 0.0048 | 40 B |
389+
390+
| ToGuid | ByteAetherUlid | 0.2531 ns | 0.0065 ns | - | - |
391+
| ToGuid | NetUlid | 11.6821 ns | 0.0379 ns | 0.0048 | 40 B |
392+
| ToGuid | Ulid | 0.7169 ns | 0.0073 ns | - | - |
393+
| ToGuid | NUlid | 0.2614 ns | 0.0060 ns | - | - |
394+
395+
| ToString | ByteAetherUlid | 19.4590 ns | 0.1668 ns | 0.0095 | 80 B |
396+
| ToString | NetUlid | 20.8832 ns | 0.1329 ns | 0.0095 | 80 B |
397+
| ToString | Ulid | 20.1657 ns | 0.0962 ns | 0.0095 | 80 B |
398+
| ToString | NUlid | 26.3051 ns | 0.1416 ns | 0.0095 | 80 B |
399+
| ToString | Guid | 11.9987 ns | 0.1148 ns | 0.0115 | 96 B |
400+
401+
| CompareTo | ByteAetherUlid | 0.0118 ns | 0.0072 ns | - | - |
402+
| CompareTo | NetUlid | 2.7883 ns | 0.0012 ns | - | - |
403+
| CompareTo | Ulid | 1.8580 ns | 0.0111 ns | - | - |
404+
| CompareTo | NUlid | 8.7885 ns | 0.0321 ns | 0.0048 | 40 B |
404405
405406
| Equals | ByteAetherUlid | 0.0000 ns | 0.0000 ns | - | - |
406-
| Equals | NetUlid | 1.1434 ns | 0.0083 ns | - | - |
407-
| Equals | Ulid | 0.0000 ns | 0.0000 ns | - | - |
408-
| Equals | NUlid | 16.8454 ns | 0.1009 ns | 0.0095 | 80 B |
409-
| Equals | Guid | 0.0131 ns | 0.0037 ns | - | - |
407+
| Equals | NetUlid | 0.8809 ns | 0.0121 ns | - | - |
408+
| Equals | Ulid | 0.0120 ns | 0.0036 ns | - | - |
409+
| Equals | NUlid | 0.0133 ns | 0.0029 ns | - | - |
410+
| Equals | Guid | 0.0000 ns | 0.0000 ns | - | - |
410411
411412
| GetHashCode | ByteAetherUlid | 0.0000 ns | 0.0000 ns | - | - |
412-
| GetHashCode | NetUlid | 9.7179 ns | 0.0457 ns | - | - |
413+
| GetHashCode | NetUlid | 9.7114 ns | 0.0392 ns | - | - |
413414
| GetHashCode | Ulid | 0.0000 ns | 0.0000 ns | - | - |
414-
| GetHashCode | NUlid | 12.9616 ns | 0.1051 ns | 0.0048 | 40 B |
415-
| GetHashCode | Guid | 0.0152 ns | 0.0016 ns | - | - |
415+
| GetHashCode | NUlid | 7.7445 ns | 0.0400 ns | - | - |
416+
| GetHashCode | Guid | 0.0134 ns | 0.0028 ns | - | - |
417+
416418
```
417419
All competitive libraries deviate from the official ULID specification in various ways or have other drawbacks:
418420
1. `NetUlid`: Can only maintain monotonicity in the scope of a single thread.
419421
2. `NUlid`: Requires special configuration to enable monotonic generation. You have to write your own wrapper with state.
420-
3. `Ulid`: Does not implement monotonicity.
422+
3. `Ulid` & `GuidV7`: Does not implement monotonicity.
421423
4. `Ulid`: This library uses a cryptographically non-secure `XOR-Shift` random value generation. Only the initial seed is generated by a cryptographically secure generator.
422-
5. `Guid`: [The Guid documentation explicitly states](https://learn.microsoft.com/en-us/dotnet/api/system.guid.newguid?view=net-9.0#remarks) that its random component may not be generated using a cryptographically secure random number generator (RNG), and that `Guid` values should not be used for cryptographic purposes.
424+
5. `Guid` & `GuidV7`: [The Guid documentation explicitly states](https://learn.microsoft.com/en-us/dotnet/api/system.guid.newguid?view=net-9.0#remarks) that its random component may not be generated using a cryptographically secure random number generator (RNG), and that `Guid` values should not be used for cryptographic purposes.
423425

424426
Both `NetUlid` and `NUlid`, which do provide monotonicity, may randomly throw `OverflowException`, when stars align against you. (Random-part overflow)
425427

src/ByteAether.Ulid.Benchmarks/ByteAether.Benchmarks.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
11+
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
1212
<PackageReference Include="NetUlid" Version="2.1.0" />
13-
<PackageReference Include="NUlid" Version="1.7.2" />
13+
<PackageReference Include="NUlid" Version="1.7.3" />
1414
<PackageReference Include="Ulid" Version="1.3.4" />
1515
</ItemGroup>
1616

src/ByteAether.Ulid.Benchmarks/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public class GenerateNonMono
5050

5151
[Benchmark]
5252
public System.Guid Guid() => System.Guid.NewGuid();
53+
54+
[Benchmark]
55+
public System.Guid GuidV7() => System.Guid.CreateVersion7();
5356
}
5457

5558
[MemoryDiagnoser]

src/ByteAether.Ulid.Tests/ByteAether.Ulid.Tests.csproj

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,24 @@
2121
</PropertyGroup>
2222

2323
<ItemGroup>
24-
<PackageReference Include="coverlet.collector" Version="6.0.3">
24+
<PackageReference Include="coverlet.collector" Version="6.0.4">
2525
<PrivateAssets>all</PrivateAssets>
2626
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2727
</PackageReference>
28-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
29-
<PackageReference Include="xunit" Version="2.9.2" />
30-
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
28+
29+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" Condition="'$(TargetFramework)' == 'net6.0' Or '$(TargetFramework)' == 'net7.0'" />
30+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" Condition="'$(TargetFramework)' != 'net6.0' And '$(TargetFramework)' != 'net7.0'" />
31+
32+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2" Condition="'$(TargetFramework)' == 'net6.0' Or '$(TargetFramework)' == 'net7.0'">
3133
<PrivateAssets>all</PrivateAssets>
3234
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3335
</PackageReference>
36+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1" Condition="'$(TargetFramework)' != 'net6.0' And '$(TargetFramework)' != 'net7.0'">
37+
<PrivateAssets>all</PrivateAssets>
38+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
39+
</PackageReference>
40+
41+
<PackageReference Include="xunit" Version="2.9.3" />
3442
</ItemGroup>
3543

3644
<ItemGroup>

src/ByteAether.Ulid/ByteAether.Ulid.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
</PropertyGroup>
3030

3131
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
32-
<PackageReference Include="System.Memory" Version="4.6.0" />
33-
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
32+
<PackageReference Include="System.Memory" Version="4.6.3" />
33+
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
3434
</ItemGroup>
3535

3636
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
37-
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
37+
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
3838
</ItemGroup>
3939

4040
<ItemGroup>

0 commit comments

Comments
 (0)