Skip to content

Commit a0b3e57

Browse files
committed
Improve unmarshalling performance
1 parent 495d402 commit a0b3e57

9 files changed

Lines changed: 95 additions & 15 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.4484)
3+
Intel Core Ultra 9 185H, 1 CPU, 22 logical and 16 physical cores
4+
.NET SDK 9.0.107
5+
[Host] : .NET 8.0.17 (8.0.1725.26602), X64 RyuJIT AVX2
6+
DefaultJob : .NET 8.0.17 (8.0.1725.26602), X64 RyuJIT AVX2
7+
8+
9+
Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
10+
----------------------------- |-----------:|----------:|----------:|-------:|-------:|----------:|
11+
Unmarshall_Person_DTO | 681.6 ns | 6.95 ns | 6.50 ns | 0.0553 | - | 696 B |
12+
Amazon_Unmarshall_Person_DTO | 6,131.7 ns | 42.92 ns | 38.05 ns | 0.9155 | - | 11610 B |
13+
Marshall_Person_DTO | 694.6 ns | 13.15 ns | 12.30 ns | 0.3052 | 0.0038 | 3840 B |
14+
Amazon_Marshall_Person_DTO | 5,824.9 ns | 116.27 ns | 264.80 ns | 0.9460 | - | 12076 B |
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
```
2+
3+
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.4484)
4+
Intel Core Ultra 9 185H, 1 CPU, 22 logical and 16 physical cores
5+
.NET SDK 9.0.107
6+
[Host] : .NET 8.0.17 (8.0.1725.26602), X64 RyuJIT AVX2
7+
DefaultJob : .NET 8.0.17 (8.0.1725.26602), X64 RyuJIT AVX2
8+
9+
10+
```
11+
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
12+
|----------------------------- |-----------:|----------:|----------:|-------:|-------:|----------:|
13+
| Unmarshall_Person_DTO | 681.6 ns | 6.95 ns | 6.50 ns | 0.0553 | - | 696 B |
14+
| Amazon_Unmarshall_Person_DTO | 6,131.7 ns | 42.92 ns | 38.05 ns | 0.9155 | - | 11610 B |
15+
| Marshall_Person_DTO | 694.6 ns | 13.15 ns | 12.30 ns | 0.3052 | 0.0038 | 3840 B |
16+
| Amazon_Marshall_Person_DTO | 5,824.9 ns | 116.27 ns | 264.80 ns | 0.9460 | - | 12076 B |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,LargeAddressAware,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Gen0,Gen1,Allocated
2+
Unmarshall_Person_DTO,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,681.6 ns,6.95 ns,6.50 ns,0.0553,0.0000,696 B
3+
Amazon_Unmarshall_Person_DTO,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,"6,131.7 ns",42.92 ns,38.05 ns,0.9155,0.0000,11610 B
4+
Marshall_Person_DTO,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,694.6 ns,13.15 ns,12.30 ns,0.3052,0.0038,3840 B
5+
Amazon_Marshall_Person_DTO,DefaultJob,False,Default,Default,Default,Default,Default,Default,1111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,"5,824.9 ns",116.27 ns,264.80 ns,0.9460,0.0000,12076 B
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html>
2+
<html lang='en'>
3+
<head>
4+
<meta charset='utf-8' />
5+
<title>DynamoDBGenerator.SourceGenerator.Benchmarks.Benchmarks.Marshalling.ComparisonBenchmarks-20250706-135846</title>
6+
7+
<style type="text/css">
8+
table { border-collapse: collapse; display: block; width: 100%; overflow: auto; }
9+
td, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }
10+
tr { background-color: #fff; border-top: 1px solid #ccc; }
11+
tr:nth-child(even) { background: #f8f8f8; }
12+
</style>
13+
</head>
14+
<body>
15+
<pre><code>
16+
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.4484)
17+
Intel Core Ultra 9 185H, 1 CPU, 22 logical and 16 physical cores
18+
.NET SDK 9.0.107
19+
[Host] : .NET 8.0.17 (8.0.1725.26602), X64 RyuJIT AVX2
20+
DefaultJob : .NET 8.0.17 (8.0.1725.26602), X64 RyuJIT AVX2
21+
</code></pre>
22+
<pre><code></code></pre>
23+
24+
<table>
25+
<thead><tr><th>Method </th><th>Mean</th><th>Error</th><th>StdDev</th><th>Gen0</th><th>Gen1</th><th>Allocated</th>
26+
</tr>
27+
</thead><tbody><tr><td>Unmarshall_Person_DTO</td><td>681.6 ns</td><td>6.95 ns</td><td>6.50 ns</td><td>0.0553</td><td>-</td><td>696 B</td>
28+
</tr><tr><td>Amazon_Unmarshall_Person_DTO</td><td>6,131.7 ns</td><td>42.92 ns</td><td>38.05 ns</td><td>0.9155</td><td>-</td><td>11610 B</td>
29+
</tr><tr><td>Marshall_Person_DTO</td><td>694.6 ns</td><td>13.15 ns</td><td>12.30 ns</td><td>0.3052</td><td>0.0038</td><td>3840 B</td>
30+
</tr><tr><td>Amazon_Marshall_Person_DTO</td><td>5,824.9 ns</td><td>116.27 ns</td><td>264.80 ns</td><td>0.9460</td><td>-</td><td>12076 B</td>
31+
</tr></tbody></table>
32+
</body>
33+
</html>

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ The source generator will look for attributes and implement interfaces that exis
3939
Here's a quick summary about how this library performs with a quick example of marshalling and unmarshalling a simple
4040
DTO object.
4141

42-
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
43-
|------------------------------|-----------:|---------:|---------:|-------:|-------:|----------:|
44-
| Unmarshall_Person_DTO | 821.6 ns | 8.43 ns | 7.47 ns | 0.0553 | - | 696 B |
45-
| Amazon_Unmarshall_Person_DTO | 6,639.1 ns | 79.64 ns | 74.50 ns | 0.9155 | - | 11609 B |
46-
| Marshall_Person_DTO | 689.6 ns | 4.24 ns | 3.76 ns | 0.3052 | 0.0038 | 3840 B |
47-
| Amazon_Marshall_Person_DTO | 5,611.1 ns | 20.94 ns | 17.49 ns | 0.9460 | - | 12076 B |
42+
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
43+
|------------------------------|-----------:|----------:|----------:|-------:|-------:|----------:|
44+
| Unmarshall_Person_DTO | 681.6 ns | 6.95 ns | 6.50 ns | 0.0553 | - | 696 B |
45+
| Amazon_Unmarshall_Person_DTO | 6,131.7 ns | 42.92 ns | 38.05 ns | 0.9155 | - | 11610 B |
46+
| Marshall_Person_DTO | 694.6 ns | 13.15 ns | 12.30 ns | 0.3052 | 0.0038 | 3840 B |
47+
| Amazon_Marshall_Person_DTO | 5,824.9 ns | 116.27 ns | 264.80 ns | 0.9460 | - | 12076 B |
4848

4949
## Features:
5050

src/DynamoDBGenerator.SourceGenerator/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public static class AttributeValueUtilityFactory
9797
public const string FromNullableStringSet = $"{ClassName}.FromNullableStringSet";
9898
public const string FromNumberSet = $"{ClassName}.FromNumberSet";
9999
public const string FromNullableNumberSet = $"{ClassName}.FromNullableNumberSet";
100-
public const string ToNullableStringSet = $"{ClassName}.ToNullableStringSet";
100+
public const string GetAttributeValueOrNull = $"{ClassName}.GetAttributeValueOrNull";
101101
}
102102
public static class ExceptionHelper
103103
{

src/DynamoDBGenerator.SourceGenerator/Generations/UnMarshaller.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ internal static IEnumerable<string> CreateClass(DynamoDBMarshallerArguments[] ar
5050
private static CodeFactory CreateCode(TypeIdentifier typeIdentifier, Func<ITypeSymbol, DynamoDbDataMember[]> fn, MarshallerOptions options)
5151
{
5252
var assignments = fn(typeIdentifier.TypeSymbol)
53-
.Select(x => (DDB: x, MethodCall: InvokeUnmarshallMethod(x.DataMember.TypeIdentifier, $"{Dict}.GetValueOrDefault(\"{x.AttributeName}\")", $"\"{x.DataMember.Name}\"", options), x.DataMember.Name))
53+
.Select(x => (DDB: x, MethodCall: InvokeUnmarshallMethod(x.DataMember.TypeIdentifier, $"{AttributeValueUtilityFactory.GetAttributeValueOrNull}({Dict}, \"{x.AttributeName}\")", $"\"{x.DataMember.Name}\"", options), x.DataMember.Name))
5454
.ToArray();
5555

5656
var blockBody =

src/DynamoDBGenerator/Internal/MarshallHelper.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Diagnostics.CodeAnalysis;
44
using System.Linq;
55
using System.Numerics;
6+
using System.Runtime.CompilerServices;
67
using System.Runtime.InteropServices;
78
using Amazon.DynamoDBv2.Model;
89

@@ -29,6 +30,16 @@ public static class MarshallHelper
2930
: new AttributeValue { M = dict };
3031
}
3132

33+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
34+
public static AttributeValue? GetAttributeValueOrNull(Dictionary<string, AttributeValue> dict, string key)
35+
{
36+
ref readonly var idRef = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key);
37+
38+
return Unsafe.IsNullRef(in idRef)
39+
? null
40+
: idRef;
41+
}
42+
3243
public static AttributeValue FromDictionary<T, TArgument>(
3344
IEnumerable<KeyValuePair<string, T>> dictionary,
3445
TArgument argument,
@@ -505,7 +516,7 @@ public static TResult[] ToArray<TResult, TArgument>(
505516
var span = CollectionsMarshal.AsSpan(attributeValues);
506517
if (span.Length is 0)
507518
return [];
508-
519+
509520
var elements = new TResult[span.Length];
510521
for (var i = 0; i < span.Length; i++)
511522
elements[i] = resultSelector(span[i], argument, $"{dataMember}[{i}]");

tests/DynamoDBGenerator.SourceGenerator.Benchmarks/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ Intel Core Ultra 9 185H, 1 CPU, 22 logical and 16 physical cores
1414
1515
1616
```
17-
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
18-
|----------------------------- |-----------:|---------:|---------:|-------:|-------:|----------:|
19-
| Unmarshall_Person_DTO | 821.6 ns | 8.43 ns | 7.47 ns | 0.0553 | - | 696 B |
20-
| Amazon_Unmarshall_Person_DTO | 6,639.1 ns | 79.64 ns | 74.50 ns | 0.9155 | - | 11609 B |
21-
| Marshall_Person_DTO | 689.6 ns | 4.24 ns | 3.76 ns | 0.3052 | 0.0038 | 3840 B |
22-
| Amazon_Marshall_Person_DTO | 5,611.1 ns | 20.94 ns | 17.49 ns | 0.9460 | - | 12076 B |
17+
18+
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
19+
|------------------------------|-----------:|----------:|----------:|-------:|-------:|----------:|
20+
| Unmarshall_Person_DTO | 681.6 ns | 6.95 ns | 6.50 ns | 0.0553 | - | 696 B |
21+
| Amazon_Unmarshall_Person_DTO | 6,131.7 ns | 42.92 ns | 38.05 ns | 0.9155 | - | 11610 B |
22+
| Marshall_Person_DTO | 694.6 ns | 13.15 ns | 12.30 ns | 0.3052 | 0.0038 | 3840 B |
23+
| Amazon_Marshall_Person_DTO | 5,824.9 ns | 116.27 ns | 264.80 ns | 0.9460 | - | 12076 B |

0 commit comments

Comments
 (0)