Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|
UsingStringNatively | 151.8 ns | 32.19 | 1.76 | 1.00 | 0.00 | 0.0153 | 256 B | 1.00 |
UsingValueObjectAsStruct | 184.8 ns | 12.19 | 0.67 | 1.22 | 0.02 | 0.0153 | 256 B | 1.00 |
There’s a minor performance overhead, but these measurements are incredibly small. Also, there’s no memory overhead. |
BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
ShortRun : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
Job=ShortRun IterationCount=3 LaunchCount=1
WarmupCount=3
Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated |
---|---|---|---|---|---|---|---|
UsingIntNatively | 14.55 ns | 1.443 ns | 0.079 ns | 1.00 | 0.00 | - | - |
UsingValueObjectStruct | 14.88 ns | 3.639 ns | 0.199 ns | 1.02 | 0.02 | - | - |
There is no discernible difference between using a native int and a VO struct; both are pretty much the same in terms of speed and memory. | |||||||
The next most common scenario is using a VO class to represent a native String . These results are: |
BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.102
``` ini
BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
ShortRun : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
Job=ShortRun IterationCount=3 LaunchCount=1
WarmupCount=3
(to run these yourself: dotnet run -c Release --framework net8.0 -- --job short --filter *
in the Vogen.Benchmarks
folder)
As mentioned previously, the goal of Vogen is to achieve similar performance compared to using primitives themselves.
Here’s a benchmark comparing a validated Value Object with an underlying type of int
vs. using an int
natively (primitively 🤓)
| Method | Mean | Error | StdDev | Ratio | Allocated |
|------------------------ |---------:|---------:|---------:|------:|----------:|
| UsingIntNatively | 17.04 ns | 0.253 ns | 0.014 ns | 1.00 | - |
| UsingValueObjectStruct | 19.76 ns | 2.463 ns | 0.135 ns | 1.16 | - |
There's hardly any speed overhead, and no memory overhead.
The next most common scenario is using a VO to represent a string:
| Method | Mean | Error | StdDev | Ratio | Allocated |
|------------------------- |---------:|---------:|--------:|------:|----------:|
| UsingStringNatively | 204.4 ns | 8.09 ns | 0.44 ns | 1.00 | 256 B |
(to run these yourself: dotnet run -c Release --framework net8.0 -- --job short --filter *
in the Vogen.Benchmarks
folder)
As mentioned previously, the goal of Vogen is to achieve very similar performance compare to using primitives themselves.
Here’s a benchmark comparing the use of a validated value object with underlying type of int vs using an int natively (primitively 🤓)
// --- catches argument / parameter expressions
// error VOG010: Type 'CustomerId' cannot be constructed with 'new'
Task t = Task.FromResult(new());
// error VOG009: Type 'CustomerId' cannot be constructed with default
void Process(CustomerId customerId = default) { }
One of the main goals of Vogen is to achieve almost the same speed and memory performance as using
primitives directly.
Put another way, if your decimal
primitive represents an Account Balance, then there
is extremely low overhead of using an AccountBalance
Value Object instead.
Please see the performance metrics for more information.
void Process(CustomerId customerId = default) { } // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
One of the main goals of this project is to achieve almost the same speed and memory performance as using primitives directly.
Put another way, if your decimal
primitive represents an Account Balance, then there is extremely low overhead of
using an AccountBalance
value object instead. Please see the performance metrics below.
Top-Level Directory Explanations
samples/ - This directory contains example projects that demonstrate the usage of the Vogen library. Each subdirectory represents a different example, and contains the necessary files and configurations for that example.
src/ - This directory contains the source code for the project. It includes the Vogen library itself, as well as any shared types and code fixers.
src/obj/ - This directory contains object files generated during the compilation process.
src/Vogen/ - This subdirectory contains the core Vogen library code. It includes subdirectories for diagnostics, extensions, generators, properties, rules, suppressors, templates, and binaries and object files.
tests/ - This directory contains unit tests and benchmarks for the project. It includes subdirectories for analyzer tests, consumer tests, snapshot tests, and Vogen benchmarks.
tests/AnalyzerTests/ - This subdirectory contains unit tests for the analyzer component of the Vogen library.
tests/ConsumerTests/ - This subdirectory contains unit tests for the consumer-side components of the Vogen library.
tests/SnapshotTests/ - This subdirectory contains snapshot tests, which test the output of the code generation and serialization components of the Vogen library.
tests/Vogen.Benchmarks/ - This subdirectory contains benchmarks for the performance of the Vogen library.