Skip to content

Commit 5e86101

Browse files
author
Christian Donn Relacion Sarmago (Synapxe)
committedMay 30, 2024
Test out source generated mappings
FirelyTeam#2794
1 parent 776e52d commit 5e86101

18 files changed

+724
-83
lines changed
 

‎Hl7.Fhir.sln

+8
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hl7.Fhir.STU3.Tests", "src\
103103
EndProject
104104
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Hl7.Fhir.Shims.Base", "src\Hl7.Fhir.Shims.Base\Hl7.Fhir.Shims.Base.shproj", "{150A59A2-371D-4747-8B08-C8E6340EC962}"
105105
EndProject
106+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hl7.Fhir.Model.SourceGeneration", "src\Hl7.Fhir.Model.SourceGeneration\Hl7.Fhir.Model.SourceGeneration.csproj", "{BFEC5DED-1666-4F15-8483-963C4261DB63}"
107+
EndProject
106108
Global
107109
GlobalSection(SolutionConfigurationPlatforms) = preSolution
108110
Debug|Any CPU = Debug|Any CPU
@@ -296,6 +298,12 @@ Global
296298
{CFF0DDA5-5155-4144-B426-91C7623D08E7}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
297299
{CFF0DDA5-5155-4144-B426-91C7623D08E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
298300
{CFF0DDA5-5155-4144-B426-91C7623D08E7}.Release|Any CPU.Build.0 = Release|Any CPU
301+
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
302+
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Debug|Any CPU.Build.0 = Debug|Any CPU
303+
{BFEC5DED-1666-4F15-8483-963C4261DB63}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
304+
{BFEC5DED-1666-4F15-8483-963C4261DB63}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
305+
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Release|Any CPU.ActiveCfg = Release|Any CPU
306+
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Release|Any CPU.Build.0 = Release|Any CPU
299307
EndGlobalSection
300308
GlobalSection(SolutionProperties) = preSolution
301309
HideSolutionNode = FALSE
+154-47
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,173 @@
11
using BenchmarkDotNet.Attributes;
22
using Hl7.Fhir.Introspection;
33
using Hl7.Fhir.Model;
4+
using Hl7.Fhir.Utility;
45
using System;
6+
using System.Linq;
57

6-
namespace Firely.Sdk.Benchmarks
8+
namespace Firely.Sdk.Benchmarks;
9+
10+
[MemoryDiagnoser]
11+
public class ModelInspectorBenchmarks
712
{
8-
[MemoryDiagnoser]
9-
public class ModelInspectorBenchmarks
13+
[GlobalSetup]
14+
public void BenchmarkSetup()
1015
{
11-
[GlobalSetup]
12-
public void BenchmarkSetup()
13-
{
14-
// PropertyInfoExtensions.NoCodeGenSupport = true;
15-
}
16+
// PropertyInfoExtensions.NoCodeGenSupport = true;
1617

18+
var inspector = ScanAssemblies();
19+
Console.WriteLine($"ScanAssemblies: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
1720

18-
internal Type[] PopularResources = new Type[]
19-
{
20-
typeof(Observation), typeof(Patient), typeof(Organization),
21-
typeof(Procedure), typeof(StructureDefinition), typeof(MedicationRequest),
22-
typeof(ValueSet), typeof(Questionnaire), typeof(Appointment),
23-
typeof(OperationOutcome)
24-
};
25-
26-
[Benchmark]
27-
public void ScanAssemblies()
28-
{
29-
// Make sure we work uncached initially on each run
30-
//ModelInspector.Clear();
31-
//ClassMapping.Clear();
21+
inspector = ImportTypeAllResources();
22+
Console.WriteLine($"ImportTypeAllResources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
23+
24+
inspector = NewWithSourceGenMappings();
25+
Console.WriteLine($"NewWithSourceGenMappings: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
26+
27+
inspector = NewWithTypesAllResources();
28+
Console.WriteLine($"NewWithTypesAllResources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
29+
30+
inspector = ImportType4Resources();
31+
Console.WriteLine($"ImportType4Resources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
32+
33+
inspector = NewWithTypes4Resources();
34+
Console.WriteLine($"NewWithTypes4Resources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
35+
}
36+
37+
internal static readonly Type[] PopularResources = new Type[]
38+
{
39+
typeof(Observation), typeof(Patient), typeof(Organization),
40+
typeof(Procedure), typeof(StructureDefinition), typeof(MedicationRequest),
41+
typeof(ValueSet), typeof(Questionnaire), typeof(Appointment),
42+
typeof(OperationOutcome)
43+
};
44+
45+
46+
internal static readonly Type[] TestResources =
47+
[
48+
typeof(CapabilityStatement), typeof(Appointment), typeof(OperationDefinition),
49+
];
3250

33-
_ = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
51+
//[IterationSetup]
52+
//[IterationCleanup]
53+
public void ResetCache()
54+
{
55+
// Make sure we work uncached initially on each run
56+
ModelInspector.Clear();
57+
ClassMapping.Clear();
58+
EnumMapping.Clear();
59+
}
60+
61+
[Benchmark]
62+
public ModelInspector ScanAssemblies()
63+
{
64+
ResetCache();
65+
return ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
66+
}
67+
68+
[Benchmark]
69+
public ModelInspector ImportTypeAllResources()
70+
{
71+
ResetCache();
72+
var inspector = new ModelInspector(Hl7.Fhir.Specification.FhirRelease.R5);
73+
foreach (var t in ModelInfo.GenerateAllFhirTypes())
74+
{
75+
inspector.ImportType(t);
3476
}
3577

36-
[Benchmark]
37-
public void GetPropertiesPopular()
78+
return inspector;
79+
}
80+
81+
[Benchmark]
82+
public ModelInspector ImportType4Resources()
83+
{
84+
ResetCache();
85+
var inspector = new ModelInspector(Hl7.Fhir.Specification.FhirRelease.R5); //ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
86+
foreach (var t in TestResources)
3887
{
39-
// Make sure we work uncached initially on each run
40-
//ModelInspector.Clear();
41-
//ClassMapping.Clear();
42-
43-
var inspector = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
44-
foreach (var t in PopularResources)
45-
{
46-
var mapping = inspector.FindClassMapping(t);
47-
_ = mapping.PropertyMappings;
48-
}
88+
inspector.ImportType(t);
4989
}
5090

51-
//[Benchmark]
52-
//public void GetPropertiesAll()
53-
//{
54-
// // Make sure we work uncached initially on each run
55-
// ModelInspector.Clear();
56-
// ClassMapping.Clear();
91+
return inspector;
92+
}
5793

58-
// var inspector = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
59-
// foreach (var m in inspector.ClassMappings)
60-
// {
61-
// _ = m.PropertyMappings;
62-
// }
63-
//}
94+
[Benchmark]
95+
public ModelInspector NewWithSourceGenMappings()
96+
{
97+
FhirReleaseParser.Parse(ModelInfo.Version);
98+
ResetCache();
99+
return new ModelInspector(ModelInfo.GenerateAllClassMappings(), ModelInfo.GenerateAllEnumMappings()) { FhirRelease = Hl7.Fhir.Specification.FhirRelease.R5 };
100+
}
101+
102+
[Benchmark]
103+
public ModelInspector NewWithTypesAllResources()
104+
{
105+
FhirReleaseParser.Parse(ModelInfo.Version);
106+
ResetCache();
107+
return ModelInspector.ForTypes(ModelInfo.Version, ModelInfo.GenerateAllFhirTypes());
108+
}
64109

110+
[Benchmark]
111+
public ModelInspector NewWithTypes4Resources()
112+
{
113+
ResetCache();
114+
var inspector = ModelInspector.ForTypes(ModelInfo.Version, [
115+
typeof(CapabilityStatement),
116+
typeof(CapabilityStatement.ConditionalDeleteStatus),
117+
typeof(CapabilityStatement.ConditionalReadStatus),
118+
typeof(CapabilityStatement.DocumentMode),
119+
typeof(CapabilityStatement.EventCapabilityMode),
120+
typeof(CapabilityStatement.ReferenceHandlingPolicy),
121+
typeof(CapabilityStatement.ResourceVersionPolicy),
122+
typeof(CapabilityStatement.RestfulCapabilityMode),
123+
typeof(CapabilityStatement.SystemRestfulInteraction),
124+
typeof(CapabilityStatement.TypeRestfulInteraction),
125+
typeof(CapabilityStatement.DocumentComponent),
126+
typeof(CapabilityStatement.EndpointComponent),
127+
typeof(CapabilityStatement.ImplementationComponent),
128+
typeof(CapabilityStatement.MessagingComponent),
129+
typeof(CapabilityStatement.OperationComponent),
130+
typeof(CapabilityStatement.ResourceComponent),
131+
typeof(CapabilityStatement.ResourceInteractionComponent),
132+
typeof(CapabilityStatement.RestComponent),
133+
typeof(CapabilityStatement.SearchParamComponent),
134+
typeof(CapabilityStatement.SecurityComponent),
135+
typeof(CapabilityStatement.SoftwareComponent),
136+
typeof(CapabilityStatement.SupportedMessageComponent),
137+
typeof(CapabilityStatement.SystemInteractionComponent),
138+
typeof(Appointment),
139+
typeof(Appointment.AppointmentStatus),
140+
typeof(Appointment.IANATimezones),
141+
typeof(Appointment.ParticipationStatus),
142+
typeof(Appointment.WeekOfMonth),
143+
typeof(Appointment.MonthlyTemplateComponent),
144+
typeof(Appointment.ParticipantComponent),
145+
typeof(Appointment.RecurrenceTemplateComponent),
146+
typeof(Appointment.WeeklyTemplateComponent),
147+
typeof(Appointment.YearlyTemplateComponent),
148+
typeof(OperationDefinition),
149+
typeof(OperationDefinition.BindingComponent),
150+
typeof(OperationDefinition.OperationKind),
151+
typeof(OperationDefinition.OperationParameterScope),
152+
typeof(OperationDefinition.OverloadComponent),
153+
typeof(OperationDefinition.ParameterComponent),
154+
typeof(OperationDefinition.ReferencedFromComponent),
155+
]);
156+
return inspector;
65157
}
158+
159+
//[Benchmark]
160+
//public void GetPropertiesAll()
161+
//{
162+
// // Make sure we work uncached initially on each run
163+
// ModelInspector.Clear();
164+
// ClassMapping.Clear();
165+
166+
// var inspector = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
167+
// foreach (var m in inspector.ClassMappings)
168+
// {
169+
// _ = m.PropertyMappings;
170+
// }
171+
//}
172+
66173
}

‎src/Hl7.Fhir.Base/Introspection/ClassMapping.cs

+11-11
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static bool TryCreate(Type type, [NotNullWhen(true)]out ClassMapping? res
106106
return true;
107107
}
108108

109-
private ClassMapping(string name, Type nativeType, FhirRelease release)
109+
internal ClassMapping(string name, Type nativeType, FhirRelease release)
110110
{
111111
Name = name;
112112
NativeType = nativeType;
@@ -138,29 +138,29 @@ private ClassMapping(string name, Type nativeType, FhirRelease release)
138138
/// <summary>
139139
/// The .NET class that implements the FHIR datatype/resource
140140
/// </summary>
141-
public Type NativeType { get; private set; }
141+
public Type NativeType { get; internal set; }
142142

143143
/// <summary>
144144
/// Is <c>true</c> when this class represents a Resource datatype.
145145
/// </summary>
146-
public bool IsResource { get; private set; } = false;
146+
public bool IsResource { get; internal set; } = false;
147147

148148
/// <summary>
149149
/// Is <c>true</c> when this class represents a FHIR primitive
150150
/// </summary>
151151
/// <remarks>This is different from a .NET primitive, as FHIR primitives are complex types with a primitive value.</remarks>
152-
public bool IsFhirPrimitive { get; private set; } = false;
152+
public bool IsFhirPrimitive { get; internal set; } = false;
153153

154154
/// <summary>
155155
/// The element is of an atomic .NET type, not a FHIR generated POCO.
156156
/// </summary>
157-
public bool IsPrimitive { get; private set; } = false;
157+
public bool IsPrimitive { get; internal set; } = false;
158158

159159
/// <summary>
160160
/// Is <c>true</c> when this class represents a code with a required binding.
161161
/// </summary>
162162
/// <remarks>See <see cref="Name"></see>.</remarks>
163-
public bool IsCodeOfT { get; private set; } = false;
163+
public bool IsCodeOfT { get; internal set; } = false;
164164

165165
/// <summary>
166166
/// Indicates whether this class represents the nested complex type for a backbone element.
@@ -171,25 +171,25 @@ private ClassMapping(string name, Type nativeType, FhirRelease release)
171171
/// <summary>
172172
/// Indicates whether this class represents the nested complex type for a backbone element.
173173
/// </summary>
174-
public bool IsBackboneType { get; private set; } = false;
174+
public bool IsBackboneType { get; internal set; } = false;
175175

176176

177177
/// <summary>
178178
/// If this is a backbone type (<see cref="IsBackboneType"/>), then this contains the path
179179
/// in the StructureDefinition where the backbone was defined first.
180180
/// </summary>
181-
public string? DefinitionPath { get; private set; }
181+
public string? DefinitionPath { get; internal set; }
182182

183183
/// <summary>
184184
/// Indicates whether this class can be used for binding.
185185
/// </summary>
186-
public bool IsBindable { get; private set; }
186+
public bool IsBindable { get; internal set; }
187187

188188
/// <summary>
189189
/// The canonical for the StructureDefinition defining this type
190190
/// </summary>
191191
/// <remarks>Will be null for backbone types.</remarks>
192-
public string? Canonical { get; private set; }
192+
public string? Canonical { get; internal set; }
193193

194194
// This list is created lazily. This not only improves initial startup time of
195195
// applications but also ensures circular references between types will not cause loops.
@@ -212,7 +212,7 @@ private PropertyMappingCollection propertyMappings
212212
/// The collection of zero or more <see cref="ValidationAttribute"/> (or subclasses) declared
213213
/// on this class.
214214
/// </summary>
215-
public ValidationAttribute[] ValidationAttributes { get; private set; } = Array.Empty<ValidationAttribute>();
215+
public ValidationAttribute[] ValidationAttributes { get; internal set; } = Array.Empty<ValidationAttribute>();
216216

217217
/// <summary>
218218
/// Holds a reference to a property that represents the value of a FHIR Primitive. This

‎src/Hl7.Fhir.Base/Introspection/ClassMappingCollection.cs

+10-5
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,24 @@
1111
using System;
1212
using System.Collections.Concurrent;
1313
using System.Collections.Generic;
14+
using System.Linq;
1415

1516
namespace Hl7.Fhir.Introspection
1617
{
1718
internal class ClassMappingCollection
1819
{
1920
public ClassMappingCollection()
2021
{
21-
// Nothing
22+
_byName = new(StringComparer.OrdinalIgnoreCase);
23+
_byType = new();
24+
_byCanonical = new();
2225
}
2326

2427
public ClassMappingCollection(IEnumerable<ClassMapping> mappings)
2528
{
26-
AddRange(mappings);
29+
_byName = new(mappings.Select(static x => new KeyValuePair<string, ClassMapping>(x.Name, x)), StringComparer.OrdinalIgnoreCase);
30+
_byType = new(mappings.Select(static x => new KeyValuePair<Type, ClassMapping>(x.NativeType, x)));
31+
_byCanonical = new(mappings.Where(static x => x.Canonical is not null).Select(static x => new KeyValuePair<string, ClassMapping>(x.Canonical!, x)));
2732
}
2833

2934
/// <summary>
@@ -53,19 +58,19 @@ public void AddRange(IEnumerable<ClassMapping> mappings)
5358
/// List of the class mappings, keyed by name.
5459
/// </summary>
5560
public IReadOnlyDictionary<string, ClassMapping> ByName => _byName;
56-
private readonly ConcurrentDictionary<string, ClassMapping> _byName = new(StringComparer.OrdinalIgnoreCase);
61+
private readonly ConcurrentDictionary<string, ClassMapping> _byName;
5762

5863
/// <summary>
5964
/// List of the class mappings, keyed by canonical.
6065
/// </summary>
6166
public IReadOnlyDictionary<string, ClassMapping> ByCanonical => _byCanonical;
62-
private readonly ConcurrentDictionary<string, ClassMapping> _byCanonical = new();
67+
private readonly ConcurrentDictionary<string, ClassMapping> _byCanonical;
6368

6469
/// <summary>
6570
/// List of the class mappings, keyed by canonical.
6671
/// </summary>
6772
public IReadOnlyDictionary<Type, ClassMapping> ByType => _byType;
68-
public readonly ConcurrentDictionary<Type, ClassMapping> _byType = new();
73+
public readonly ConcurrentDictionary<Type, ClassMapping> _byType;
6974
}
7075
}
7176

‎src/Hl7.Fhir.Base/Introspection/EnumMapping.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public static bool TryCreate(Type type, [NotNullWhen(true)] out EnumMapping? res
6060
return true;
6161
}
6262

63-
private EnumMapping(string name, string? canonical, Type nativeType, FhirRelease release, string? defaultCodeSystem)
63+
internal EnumMapping(string name, string? canonical, Type nativeType, FhirRelease release, string? defaultCodeSystem)
6464
{
6565
Name = name;
6666
Canonical = canonical;

0 commit comments

Comments
 (0)
Please sign in to comment.