Skip to content

Commit

Permalink
Test out source generated mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Donn Relacion Sarmago (Synapxe) committed May 30, 2024
1 parent 776e52d commit 5e86101
Show file tree
Hide file tree
Showing 18 changed files with 724 additions and 83 deletions.
8 changes: 8 additions & 0 deletions Hl7.Fhir.sln
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hl7.Fhir.STU3.Tests", "src\
EndProject
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -296,6 +298,12 @@ Global
{CFF0DDA5-5155-4144-B426-91C7623D08E7}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
{CFF0DDA5-5155-4144-B426-91C7623D08E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CFF0DDA5-5155-4144-B426-91C7623D08E7}.Release|Any CPU.Build.0 = Release|Any CPU
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFEC5DED-1666-4F15-8483-963C4261DB63}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
{BFEC5DED-1666-4F15-8483-963C4261DB63}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFEC5DED-1666-4F15-8483-963C4261DB63}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
201 changes: 154 additions & 47 deletions src/Benchmarks/ModelInspectorBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,66 +1,173 @@
using BenchmarkDotNet.Attributes;
using Hl7.Fhir.Introspection;
using Hl7.Fhir.Model;
using Hl7.Fhir.Utility;
using System;
using System.Linq;

namespace Firely.Sdk.Benchmarks
namespace Firely.Sdk.Benchmarks;

[MemoryDiagnoser]
public class ModelInspectorBenchmarks
{
[MemoryDiagnoser]
public class ModelInspectorBenchmarks
[GlobalSetup]
public void BenchmarkSetup()
{
[GlobalSetup]
public void BenchmarkSetup()
{
// PropertyInfoExtensions.NoCodeGenSupport = true;
}
// PropertyInfoExtensions.NoCodeGenSupport = true;

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

internal Type[] PopularResources = new Type[]
{
typeof(Observation), typeof(Patient), typeof(Organization),
typeof(Procedure), typeof(StructureDefinition), typeof(MedicationRequest),
typeof(ValueSet), typeof(Questionnaire), typeof(Appointment),
typeof(OperationOutcome)
};

[Benchmark]
public void ScanAssemblies()
{
// Make sure we work uncached initially on each run
//ModelInspector.Clear();
//ClassMapping.Clear();
inspector = ImportTypeAllResources();
Console.WriteLine($"ImportTypeAllResources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");

inspector = NewWithSourceGenMappings();
Console.WriteLine($"NewWithSourceGenMappings: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");

inspector = NewWithTypesAllResources();
Console.WriteLine($"NewWithTypesAllResources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");

inspector = ImportType4Resources();
Console.WriteLine($"ImportType4Resources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");

inspector = NewWithTypes4Resources();
Console.WriteLine($"NewWithTypes4Resources: Types: {inspector.ClassMappings.Count}, Enums: {inspector.EnumMappings.Count()}");
}

internal static readonly Type[] PopularResources = new Type[]
{
typeof(Observation), typeof(Patient), typeof(Organization),
typeof(Procedure), typeof(StructureDefinition), typeof(MedicationRequest),
typeof(ValueSet), typeof(Questionnaire), typeof(Appointment),
typeof(OperationOutcome)
};


internal static readonly Type[] TestResources =
[
typeof(CapabilityStatement), typeof(Appointment), typeof(OperationDefinition),
];

_ = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
//[IterationSetup]
//[IterationCleanup]
public void ResetCache()
{
// Make sure we work uncached initially on each run
ModelInspector.Clear();
ClassMapping.Clear();
EnumMapping.Clear();
}

[Benchmark]
public ModelInspector ScanAssemblies()
{
ResetCache();
return ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
}

[Benchmark]
public ModelInspector ImportTypeAllResources()
{
ResetCache();
var inspector = new ModelInspector(Hl7.Fhir.Specification.FhirRelease.R5);
foreach (var t in ModelInfo.GenerateAllFhirTypes())
{
inspector.ImportType(t);
}

[Benchmark]
public void GetPropertiesPopular()
return inspector;
}

[Benchmark]
public ModelInspector ImportType4Resources()
{
ResetCache();
var inspector = new ModelInspector(Hl7.Fhir.Specification.FhirRelease.R5); //ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
foreach (var t in TestResources)
{
// Make sure we work uncached initially on each run
//ModelInspector.Clear();
//ClassMapping.Clear();

var inspector = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
foreach (var t in PopularResources)
{
var mapping = inspector.FindClassMapping(t);
_ = mapping.PropertyMappings;
}
inspector.ImportType(t);
}

//[Benchmark]
//public void GetPropertiesAll()
//{
// // Make sure we work uncached initially on each run
// ModelInspector.Clear();
// ClassMapping.Clear();
return inspector;
}

// var inspector = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
// foreach (var m in inspector.ClassMappings)
// {
// _ = m.PropertyMappings;
// }
//}
[Benchmark]
public ModelInspector NewWithSourceGenMappings()
{
FhirReleaseParser.Parse(ModelInfo.Version);
ResetCache();
return new ModelInspector(ModelInfo.GenerateAllClassMappings(), ModelInfo.GenerateAllEnumMappings()) { FhirRelease = Hl7.Fhir.Specification.FhirRelease.R5 };
}

[Benchmark]
public ModelInspector NewWithTypesAllResources()
{
FhirReleaseParser.Parse(ModelInfo.Version);
ResetCache();
return ModelInspector.ForTypes(ModelInfo.Version, ModelInfo.GenerateAllFhirTypes());
}

[Benchmark]
public ModelInspector NewWithTypes4Resources()
{
ResetCache();
var inspector = ModelInspector.ForTypes(ModelInfo.Version, [
typeof(CapabilityStatement),
typeof(CapabilityStatement.ConditionalDeleteStatus),
typeof(CapabilityStatement.ConditionalReadStatus),
typeof(CapabilityStatement.DocumentMode),
typeof(CapabilityStatement.EventCapabilityMode),
typeof(CapabilityStatement.ReferenceHandlingPolicy),
typeof(CapabilityStatement.ResourceVersionPolicy),
typeof(CapabilityStatement.RestfulCapabilityMode),
typeof(CapabilityStatement.SystemRestfulInteraction),
typeof(CapabilityStatement.TypeRestfulInteraction),
typeof(CapabilityStatement.DocumentComponent),
typeof(CapabilityStatement.EndpointComponent),
typeof(CapabilityStatement.ImplementationComponent),
typeof(CapabilityStatement.MessagingComponent),
typeof(CapabilityStatement.OperationComponent),
typeof(CapabilityStatement.ResourceComponent),
typeof(CapabilityStatement.ResourceInteractionComponent),
typeof(CapabilityStatement.RestComponent),
typeof(CapabilityStatement.SearchParamComponent),
typeof(CapabilityStatement.SecurityComponent),
typeof(CapabilityStatement.SoftwareComponent),
typeof(CapabilityStatement.SupportedMessageComponent),
typeof(CapabilityStatement.SystemInteractionComponent),
typeof(Appointment),
typeof(Appointment.AppointmentStatus),
typeof(Appointment.IANATimezones),
typeof(Appointment.ParticipationStatus),
typeof(Appointment.WeekOfMonth),
typeof(Appointment.MonthlyTemplateComponent),
typeof(Appointment.ParticipantComponent),
typeof(Appointment.RecurrenceTemplateComponent),
typeof(Appointment.WeeklyTemplateComponent),
typeof(Appointment.YearlyTemplateComponent),
typeof(OperationDefinition),
typeof(OperationDefinition.BindingComponent),
typeof(OperationDefinition.OperationKind),
typeof(OperationDefinition.OperationParameterScope),
typeof(OperationDefinition.OverloadComponent),
typeof(OperationDefinition.ParameterComponent),
typeof(OperationDefinition.ReferencedFromComponent),
]);
return inspector;
}

//[Benchmark]
//public void GetPropertiesAll()
//{
// // Make sure we work uncached initially on each run
// ModelInspector.Clear();
// ClassMapping.Clear();

// var inspector = ModelInspector.ForAssembly(typeof(ModelInfo).Assembly);
// foreach (var m in inspector.ClassMappings)
// {
// _ = m.PropertyMappings;
// }
//}

}
22 changes: 11 additions & 11 deletions src/Hl7.Fhir.Base/Introspection/ClassMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public static bool TryCreate(Type type, [NotNullWhen(true)]out ClassMapping? res
return true;
}

private ClassMapping(string name, Type nativeType, FhirRelease release)
internal ClassMapping(string name, Type nativeType, FhirRelease release)
{
Name = name;
NativeType = nativeType;
Expand Down Expand Up @@ -138,29 +138,29 @@ private ClassMapping(string name, Type nativeType, FhirRelease release)
/// <summary>
/// The .NET class that implements the FHIR datatype/resource
/// </summary>
public Type NativeType { get; private set; }
public Type NativeType { get; internal set; }

/// <summary>
/// Is <c>true</c> when this class represents a Resource datatype.
/// </summary>
public bool IsResource { get; private set; } = false;
public bool IsResource { get; internal set; } = false;

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

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

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

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


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

/// <summary>
/// Indicates whether this class can be used for binding.
/// </summary>
public bool IsBindable { get; private set; }
public bool IsBindable { get; internal set; }

/// <summary>
/// The canonical for the StructureDefinition defining this type
/// </summary>
/// <remarks>Will be null for backbone types.</remarks>
public string? Canonical { get; private set; }
public string? Canonical { get; internal set; }

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

/// <summary>
/// Holds a reference to a property that represents the value of a FHIR Primitive. This
Expand Down
15 changes: 10 additions & 5 deletions src/Hl7.Fhir.Base/Introspection/ClassMappingCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,24 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Hl7.Fhir.Introspection
{
internal class ClassMappingCollection
{
public ClassMappingCollection()
{
// Nothing
_byName = new(StringComparer.OrdinalIgnoreCase);
_byType = new();
_byCanonical = new();
}

public ClassMappingCollection(IEnumerable<ClassMapping> mappings)
{
AddRange(mappings);
_byName = new(mappings.Select(static x => new KeyValuePair<string, ClassMapping>(x.Name, x)), StringComparer.OrdinalIgnoreCase);
_byType = new(mappings.Select(static x => new KeyValuePair<Type, ClassMapping>(x.NativeType, x)));
_byCanonical = new(mappings.Where(static x => x.Canonical is not null).Select(static x => new KeyValuePair<string, ClassMapping>(x.Canonical!, x)));
}

/// <summary>
Expand Down Expand Up @@ -53,19 +58,19 @@ public void AddRange(IEnumerable<ClassMapping> mappings)
/// List of the class mappings, keyed by name.
/// </summary>
public IReadOnlyDictionary<string, ClassMapping> ByName => _byName;
private readonly ConcurrentDictionary<string, ClassMapping> _byName = new(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, ClassMapping> _byName;

/// <summary>
/// List of the class mappings, keyed by canonical.
/// </summary>
public IReadOnlyDictionary<string, ClassMapping> ByCanonical => _byCanonical;
private readonly ConcurrentDictionary<string, ClassMapping> _byCanonical = new();
private readonly ConcurrentDictionary<string, ClassMapping> _byCanonical;

/// <summary>
/// List of the class mappings, keyed by canonical.
/// </summary>
public IReadOnlyDictionary<Type, ClassMapping> ByType => _byType;
public readonly ConcurrentDictionary<Type, ClassMapping> _byType = new();
public readonly ConcurrentDictionary<Type, ClassMapping> _byType;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Hl7.Fhir.Base/Introspection/EnumMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static bool TryCreate(Type type, [NotNullWhen(true)] out EnumMapping? res
return true;
}

private EnumMapping(string name, string? canonical, Type nativeType, FhirRelease release, string? defaultCodeSystem)
internal EnumMapping(string name, string? canonical, Type nativeType, FhirRelease release, string? defaultCodeSystem)
{
Name = name;
Canonical = canonical;
Expand Down
Loading

0 comments on commit 5e86101

Please sign in to comment.