From eb44e9d5b50a37109b7a47a741be3facfa6cc57d Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Wed, 21 Aug 2024 10:09:32 -0700 Subject: [PATCH 1/3] Optimize `EntitySetState` --- .../Orm/Internals/EntitySetState.cs | 167 ++++++------------ Orm/Xtensive.Orm/Orm/KeyMapping.cs | 13 +- 2 files changed, 57 insertions(+), 123 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs b/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs index c7b50b73ba..8bcf3aa17a 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Threading; using Xtensive.Caching; -using Xtensive.Core; using KeyCache = Xtensive.Caching.ICache; namespace Xtensive.Orm.Internals @@ -22,33 +21,24 @@ public sealed class EntitySetState : TransactionalStateContainer, IEnumerable, IInvalidatable { - private class BackupedState - { - public bool IsLoaded { get; private set; } - public long? TotalItemCount { get; private set; } - public IEnumerable AddedKeys { get; private set; } - public IEnumerable RemovedKeys { get; private set; } - - public BackupedState(EntitySetState state) - { - IsLoaded = state.IsLoaded; - TotalItemCount = state.TotalItemCount; - AddedKeys = state.addedKeys.Values.ToList(); - RemovedKeys = state.removedKeys.Values.ToList(); - } - } + private readonly record struct BackedUpState + ( + bool IsLoaded, + long? TotalItemCount, + IEnumerable AddedKeys, + IEnumerable RemovedKeys + ); - private readonly EntitySetBase owner; private readonly bool isDisconnected; - private Guid lastManualPrefetchId; + private Guid lastManualPrefetchId = Guid.Empty; private bool isLoaded; private long? totalItemCount; - private int version; - private IDictionary addedKeys; - private IDictionary removedKeys; + private volatile int version = int.MinValue; + private HashSet addedKeys = new(); + private HashSet removedKeys = new(); - private BackupedState previousState; + private BackedUpState? previousState; public KeyCache FetchedKeys { @@ -71,8 +61,7 @@ public long? TotalItemCount /// /// Gets the number of cached items. /// - public long CachedItemCount - => FetchedItemsCount - RemovedItemsCount + AddedItemsCount; + public long CachedItemCount => FetchedItemsCount - RemovedItemsCount + AddedItemsCount; /// /// Gets the number of fetched keys. @@ -138,16 +127,8 @@ public void Update(IEnumerable keys, long? count) /// /// The key. /// Check result. - public bool Contains(Key key) - { - if (removedKeys.ContainsKey(key)) { - return false; - } - if (addedKeys.ContainsKey(key)) { - return true; - } - return FetchedKeys.ContainsKey(key); - } + public bool Contains(Key key) => + !removedKeys.Contains(key) && (addedKeys.Contains(key) || FetchedKeys.ContainsKey(key)); /// /// Registers the specified fetched key in cached state. @@ -162,15 +143,13 @@ public bool Contains(Key key) public void Add(Key key) { if (!removedKeys.Remove(key)) { - addedKeys[key] = key; + addedKeys.Add(key); } if (TotalItemCount != null) { TotalItemCount++; } - unchecked { - _ = Interlocked.Add(ref version, 1); - } + _ = Interlocked.Increment(ref version); Rebind(); } @@ -181,15 +160,13 @@ public void Add(Key key) public void Remove(Key key) { if (!addedKeys.Remove(key)) { - removedKeys[key] = key; + removedKeys.Add(key); } if (TotalItemCount!=null) { TotalItemCount--; } - unchecked { - _ = Interlocked.Add(ref version, 1); - } + _ = Interlocked.Increment(ref version); Rebind(); } @@ -205,13 +182,13 @@ public bool ApplyChanges() InitializeFetchedKeys(); foreach (var currentFetchedKey in currentFetchedKeys) { - if (!removedKeys.ContainsKey(currentFetchedKey)) { + if (!removedKeys.Contains(currentFetchedKey)) { FetchedKeys.Add(currentFetchedKey); } } foreach (var addedKey in addedKeys) { - FetchedKeys.Add(addedKey.Value); + FetchedKeys.Add(addedKey); } InitializeDifferenceCollections(); Rebind(); @@ -226,17 +203,15 @@ public bool ApplyChanges() public void CancelChanges() { InitializeDifferenceCollections(); - unchecked { - _ = Interlocked.Add(ref version, 1); - } + _ = Interlocked.Increment(ref version); Rebind(); } internal void RollbackState() { - if (previousState != null) { - TotalItemCount = previousState.TotalItemCount; - IsLoaded = previousState.IsLoaded; + if (previousState is { } prev) { + TotalItemCount = prev.TotalItemCount; + IsLoaded = prev.IsLoaded; var fetchedKeys = FetchedKeys; InitializeFetchedKeys(); @@ -246,35 +221,26 @@ internal void RollbackState() FetchedKeys.Add(fetchedKey); } - foreach (var addedKey in previousState.AddedKeys) { + foreach (var addedKey in prev.AddedKeys) { if (fetchedKeys.ContainsKey(addedKey)) { FetchedKeys.Remove(addedKey); } - addedKeys.Add(addedKey, addedKey); + addedKeys.Add(addedKey); } - foreach (var removedKey in previousState.RemovedKeys) { + foreach (var removedKey in prev.RemovedKeys) { if (!FetchedKeys.ContainsKey(removedKey)) { FetchedKeys.Add(removedKey); } - removedKeys.Add(removedKey, removedKey); + removedKeys.Add(removedKey); } } } internal void RemapKeys(KeyMapping mapping) { - var oldAddedKeys = addedKeys; - var oldRemovedKeys = removedKeys; - InitializeDifferenceCollections(); - - foreach (var oldAddedKey in oldAddedKeys) { - var newKey = mapping.TryRemapKey(oldAddedKey.Key); - addedKeys.Add(newKey, newKey); - } - foreach (var oldRemovedKey in oldRemovedKeys) { - var newKey = mapping.TryRemapKey(oldRemovedKey.Key); - removedKeys.Add(newKey, newKey); - } + var remapper = mapping.TryRemapKey; + addedKeys = addedKeys.Select(remapper).ToHashSet(); + removedKeys = removedKeys.Select(remapper).ToHashSet(); } internal bool ShouldUseForcePrefetch(Guid? currentPrefetchOperation) @@ -299,11 +265,7 @@ internal bool ShouldUseForcePrefetch(Guid? currentPrefetchOperation) } } - if (isDisconnected) { - return true; - } - - return false; + return isDisconnected; } internal void SetLastManualPrefetchId(Guid? prefetchOperationId) @@ -332,44 +294,28 @@ protected override void Invalidate() public IEnumerator GetEnumerator() { var versionSnapshot = version; - using (var fetchedKeysEnumerator = FetchedKeys.GetEnumerator()) { - while (true) { - if (versionSnapshot != version) { - throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); - } - - if (!fetchedKeysEnumerator.MoveNext()) { - break; - } - - var fetchedKey = fetchedKeysEnumerator.Current; - if (!removedKeys.ContainsKey(fetchedKey)) { - yield return fetchedKey; - } + foreach (var fetchedKey in FetchedKeys) { + if (versionSnapshot != version) { + throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); + } + if (!removedKeys.Contains(fetchedKey)) { + yield return fetchedKey; } } - using (var addedKeysEnumerator = addedKeys.GetEnumerator()) { - while (true) { - if (versionSnapshot != version) { - throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); - } - - if (!addedKeysEnumerator.MoveNext()) { - break; - } - - var addedKey = addedKeysEnumerator.Current; - yield return addedKey.Value; + if (versionSnapshot != version) { + throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); + } + foreach (var addedKey in addedKeys) { + if (versionSnapshot != version) { + throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); } + yield return addedKey; } } /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion @@ -386,16 +332,14 @@ private void UpdateSyncedState(IEnumerable keys, long? count) public void UpdateCachedState(IEnumerable syncronizedKeys, long? count) { FetchedKeys.Clear(); - var becameRemovedOnSever = new HashSet(removedKeys.Keys); + HashSet becameRemovedOnSever = new(removedKeys); foreach (var key in syncronizedKeys) { if (!addedKeys.Remove(key)) { _ = becameRemovedOnSever.Remove(key); } FetchedKeys.Add(key); } - foreach (var removedOnServer in becameRemovedOnSever) { - _ = removedKeys.Remove(removedOnServer); - } + removedKeys.ExceptWith(becameRemovedOnSever); TotalItemCount = count.HasValue ? FetchedKeys.Count - removedKeys.Count + AddedItemsCount @@ -409,15 +353,16 @@ private void EnsureFetchedKeysIsNotNull() } } - private void BackupState() => previousState = new BackupedState(this); + private void BackupState() => + previousState = new(IsLoaded, TotalItemCount, addedKeys.ToList(), removedKeys.ToList()); private void InitializeFetchedKeys() => FetchedKeys = new LruCache(WellKnown.EntitySetCacheSize, cachedKey => cachedKey); private void InitializeDifferenceCollections() { - addedKeys = new Dictionary(); - removedKeys = new Dictionary(); + addedKeys = new(); + removedKeys = new(); } // Constructors @@ -426,11 +371,7 @@ internal EntitySetState(EntitySetBase entitySet) : base(entitySet.Session) { InitializeFetchedKeys(); - InitializeDifferenceCollections(); - owner = entitySet; - version = int.MinValue; isDisconnected = entitySet.Session.IsDisconnected; - lastManualPrefetchId = Guid.Empty; } } -} +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/KeyMapping.cs b/Orm/Xtensive.Orm/Orm/KeyMapping.cs index e9e304e5d9..b9a54df6df 100644 --- a/Orm/Xtensive.Orm/Orm/KeyMapping.cs +++ b/Orm/Xtensive.Orm/Orm/KeyMapping.cs @@ -6,13 +6,11 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Runtime.Serialization; using System.Security; using Xtensive.Core; - namespace Xtensive.Orm { /// @@ -20,7 +18,7 @@ namespace Xtensive.Orm /// to actual (storage) instances. /// [Serializable] - public sealed class KeyMapping : ISerializable + public readonly struct KeyMapping : ISerializable { /// /// Gets the key map. @@ -34,13 +32,8 @@ public sealed class KeyMapping : ISerializable /// /// The key to remap. /// The mapped storage . - public Key TryRemapKey(Key key) - { - Key remappedKey; - if (key!=null && Map.TryGetValue(key, out remappedKey)) - return remappedKey; - return key; - } + public Key TryRemapKey(Key key) => + key!=null && Map.TryGetValue(key, out var remappedKey) ? remappedKey : key; /// /// Remaps the keys of cached entities From 9478034451a783f641bdde32ea5a59a10737dcdd Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Wed, 21 Aug 2024 11:13:36 -0700 Subject: [PATCH 2/3] .ToArray() --- Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs b/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs index 8bcf3aa17a..980c6a90f9 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs @@ -354,7 +354,7 @@ private void EnsureFetchedKeysIsNotNull() } private void BackupState() => - previousState = new(IsLoaded, TotalItemCount, addedKeys.ToList(), removedKeys.ToList()); + previousState = new(IsLoaded, TotalItemCount, addedKeys.ToArray(), removedKeys.ToArray()); private void InitializeFetchedKeys() => FetchedKeys = new LruCache(WellKnown.EntitySetCacheSize, cachedKey => cachedKey); From 217e622bbba90ba5a08fb6983a4c2ba3ce4bcebc Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Wed, 21 Aug 2024 11:49:55 -0700 Subject: [PATCH 3/3] Revert GetEnumerator() --- .../Orm/Internals/EntitySetState.cs | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs b/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs index 980c6a90f9..57a4394f95 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs @@ -294,23 +294,35 @@ protected override void Invalidate() public IEnumerator GetEnumerator() { var versionSnapshot = version; - foreach (var fetchedKey in FetchedKeys) { - if (versionSnapshot != version) { - throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); - } - if (!removedKeys.Contains(fetchedKey)) { - yield return fetchedKey; + using (var fetchedKeysEnumerator = FetchedKeys.GetEnumerator()) { + while (true) { + if (versionSnapshot != version) { + throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); + } + + if (!fetchedKeysEnumerator.MoveNext()) { + break; + } + + var fetchedKey = fetchedKeysEnumerator.Current; + if (!removedKeys.Contains(fetchedKey)) { + yield return fetchedKey; + } } } - if (versionSnapshot != version) { - throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); - } - foreach (var addedKey in addedKeys) { - if (versionSnapshot != version) { - throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); + using (var addedKeysEnumerator = addedKeys.GetEnumerator()) { + while (true) { + if (versionSnapshot != version) { + throw new InvalidOperationException(Strings.ExCollectionHasBeenChanged); + } + + if (!addedKeysEnumerator.MoveNext()) { + break; + } + + yield return addedKeysEnumerator.Current; } - yield return addedKey; } }