From 5ab4c5a6b5be8278a2d37a591bff5bd0a3b7069f Mon Sep 17 00:00:00 2001 From: Turnerj Date: Sun, 1 Oct 2023 21:23:11 +1030 Subject: [PATCH] Improve change tracking performance for single ops --- .../Infrastructure/EntityEntryContainer.cs | 8 +++- .../Mapping/EntityDefinition.cs | 25 +++++++++++- .../EntityCollectionGetEntryBenchmark.cs | 40 +++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/MongoFramework.Benchmarks/EntityCollectionGetEntryBenchmark.cs diff --git a/src/MongoFramework/Infrastructure/EntityEntryContainer.cs b/src/MongoFramework/Infrastructure/EntityEntryContainer.cs index 7002aba1..c2d8f024 100644 --- a/src/MongoFramework/Infrastructure/EntityEntryContainer.cs +++ b/src/MongoFramework/Infrastructure/EntityEntryContainer.cs @@ -46,11 +46,15 @@ public EntityEntry GetEntry(TCollectionBase entity) var entityDefinition = EntityMapping.GetOrCreateDefinition(collectionType); var entityId = entityDefinition.GetIdValue(entity); var defaultIdValue = entityDefinition.GetDefaultId(); + var isDefaultId = Equals(entityId, defaultIdValue); foreach (var entry in entries) { - if (Equals(entityId, defaultIdValue) && ReferenceEquals(entry.Entity, entity)) + if (isDefaultId) { - return entry; + if (ReferenceEquals(entry.Entity, entity)) + { + return entry; + } } else { diff --git a/src/MongoFramework/Infrastructure/Mapping/EntityDefinition.cs b/src/MongoFramework/Infrastructure/Mapping/EntityDefinition.cs index da034cff..175aa032 100644 --- a/src/MongoFramework/Infrastructure/Mapping/EntityDefinition.cs +++ b/src/MongoFramework/Infrastructure/Mapping/EntityDefinition.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq.Expressions; using System.Reflection; namespace MongoFramework.Infrastructure.Mapping; @@ -25,9 +26,31 @@ public sealed record PropertyDefinition public PropertyInfo PropertyInfo { get; init; } public string ElementName { get; init; } + private Func getValueDelegate; public object GetValue(object entity) { - return PropertyInfo.GetValue(entity); + if (getValueDelegate is null) + { + // Effectively results in the following expression + // object t => (object)(({PropertyType})t).{PropertyName} + var parameter = Expression.Parameter(typeof(object), "t"); + var lambda = Expression.Lambda>( + Expression.Convert( + Expression.MakeMemberAccess( + Expression.Convert( + parameter, + PropertyInfo.DeclaringType + ), + PropertyInfo + ), + typeof(object) + ), + parameter + ); + getValueDelegate = lambda.Compile(); + } + + return getValueDelegate(entity); } public void SetValue(object entity, object value) diff --git a/tests/MongoFramework.Benchmarks/EntityCollectionGetEntryBenchmark.cs b/tests/MongoFramework.Benchmarks/EntityCollectionGetEntryBenchmark.cs new file mode 100644 index 00000000..88fd9cab --- /dev/null +++ b/tests/MongoFramework.Benchmarks/EntityCollectionGetEntryBenchmark.cs @@ -0,0 +1,40 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using MongoFramework.Infrastructure; + +namespace MongoFramework.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net60), MemoryDiagnoser] +public class EntityEntryContainerBenchmark +{ + [Params(100, 1_000, 10_000)] + public int EntryCount; + + private EntityEntryContainer Container; + private TestEntity[] Entities; + + private class TestEntity + { + public string Id { get; set; } + } + + [GlobalSetup] + public void Setup() + { + Container = new EntityEntryContainer(); + Entities = new TestEntity[EntryCount]; + for (var i = 0; i < Entities.Length; i++) + { + Entities[i] = new TestEntity(); + } + } + + [Benchmark] + public void SetEntityState() + { + for (var i = 0; i < Entities.Length; i++) + { + Container.SetEntityState(Entities[i], EntityEntryState.Added); + } + } +}