Skip to content

Commit 00acb70

Browse files
authored
Optimize EntitySetState (#241)
* Optimize `EntitySetState` * .ToArray() * Revert GetEnumerator()
1 parent 9ee5bb4 commit 00acb70

File tree

2 files changed

+46
-100
lines changed

2 files changed

+46
-100
lines changed

Orm/Xtensive.Orm/Orm/Internals/EntitySetState.cs

Lines changed: 43 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Linq;
1111
using System.Threading;
1212
using Xtensive.Caching;
13-
using Xtensive.Core;
1413
using KeyCache = Xtensive.Caching.ICache<Xtensive.Orm.Key, Xtensive.Orm.Key>;
1514

1615
namespace Xtensive.Orm.Internals
@@ -22,33 +21,24 @@ public sealed class EntitySetState : TransactionalStateContainer<KeyCache>,
2221
IEnumerable<Key>,
2322
IInvalidatable
2423
{
25-
private class BackupedState
26-
{
27-
public bool IsLoaded { get; private set; }
28-
public long? TotalItemCount { get; private set; }
29-
public IEnumerable<Key> AddedKeys { get; private set; }
30-
public IEnumerable<Key> RemovedKeys { get; private set; }
31-
32-
public BackupedState(EntitySetState state)
33-
{
34-
IsLoaded = state.IsLoaded;
35-
TotalItemCount = state.TotalItemCount;
36-
AddedKeys = state.addedKeys.Values.ToList();
37-
RemovedKeys = state.removedKeys.Values.ToList();
38-
}
39-
}
24+
private readonly record struct BackedUpState
25+
(
26+
bool IsLoaded,
27+
long? TotalItemCount,
28+
IEnumerable<Key> AddedKeys,
29+
IEnumerable<Key> RemovedKeys
30+
);
4031

41-
private readonly EntitySetBase owner;
4232
private readonly bool isDisconnected;
4333

44-
private Guid lastManualPrefetchId;
34+
private Guid lastManualPrefetchId = Guid.Empty;
4535
private bool isLoaded;
4636
private long? totalItemCount;
47-
private int version;
48-
private IDictionary<Key, Key> addedKeys;
49-
private IDictionary<Key, Key> removedKeys;
37+
private volatile int version = int.MinValue;
38+
private HashSet<Key> addedKeys = new();
39+
private HashSet<Key> removedKeys = new();
5040

51-
private BackupedState previousState;
41+
private BackedUpState? previousState;
5242

5343
public KeyCache FetchedKeys
5444
{
@@ -71,8 +61,7 @@ public long? TotalItemCount
7161
/// <summary>
7262
/// Gets the number of cached items.
7363
/// </summary>
74-
public long CachedItemCount
75-
=> FetchedItemsCount - RemovedItemsCount + AddedItemsCount;
64+
public long CachedItemCount => FetchedItemsCount - RemovedItemsCount + AddedItemsCount;
7665

7766
/// <summary>
7867
/// Gets the number of fetched keys.
@@ -138,16 +127,8 @@ public void Update(IEnumerable<Key> keys, long? count)
138127
/// </summary>
139128
/// <param name="key">The key.</param>
140129
/// <returns>Check result.</returns>
141-
public bool Contains(Key key)
142-
{
143-
if (removedKeys.ContainsKey(key)) {
144-
return false;
145-
}
146-
if (addedKeys.ContainsKey(key)) {
147-
return true;
148-
}
149-
return FetchedKeys.ContainsKey(key);
150-
}
130+
public bool Contains(Key key) =>
131+
!removedKeys.Contains(key) && (addedKeys.Contains(key) || FetchedKeys.ContainsKey(key));
151132

152133
/// <summary>
153134
/// Registers the specified fetched key in cached state.
@@ -162,15 +143,13 @@ public bool Contains(Key key)
162143
public void Add(Key key)
163144
{
164145
if (!removedKeys.Remove(key)) {
165-
addedKeys[key] = key;
146+
addedKeys.Add(key);
166147
}
167148
if (TotalItemCount != null) {
168149
TotalItemCount++;
169150
}
170151

171-
unchecked {
172-
_ = Interlocked.Add(ref version, 1);
173-
}
152+
_ = Interlocked.Increment(ref version);
174153
Rebind();
175154
}
176155

@@ -181,15 +160,13 @@ public void Add(Key key)
181160
public void Remove(Key key)
182161
{
183162
if (!addedKeys.Remove(key)) {
184-
removedKeys[key] = key;
163+
removedKeys.Add(key);
185164
}
186165
if (TotalItemCount!=null) {
187166
TotalItemCount--;
188167
}
189168

190-
unchecked {
191-
_ = Interlocked.Add(ref version, 1);
192-
}
169+
_ = Interlocked.Increment(ref version);
193170
Rebind();
194171
}
195172

@@ -205,13 +182,13 @@ public bool ApplyChanges()
205182
InitializeFetchedKeys();
206183

207184
foreach (var currentFetchedKey in currentFetchedKeys) {
208-
if (!removedKeys.ContainsKey(currentFetchedKey)) {
185+
if (!removedKeys.Contains(currentFetchedKey)) {
209186
FetchedKeys.Add(currentFetchedKey);
210187
}
211188
}
212189

213190
foreach (var addedKey in addedKeys) {
214-
FetchedKeys.Add(addedKey.Value);
191+
FetchedKeys.Add(addedKey);
215192
}
216193
InitializeDifferenceCollections();
217194
Rebind();
@@ -226,17 +203,15 @@ public bool ApplyChanges()
226203
public void CancelChanges()
227204
{
228205
InitializeDifferenceCollections();
229-
unchecked {
230-
_ = Interlocked.Add(ref version, 1);
231-
}
206+
_ = Interlocked.Increment(ref version);
232207
Rebind();
233208
}
234209

235210
internal void RollbackState()
236211
{
237-
if (previousState != null) {
238-
TotalItemCount = previousState.TotalItemCount;
239-
IsLoaded = previousState.IsLoaded;
212+
if (previousState is { } prev) {
213+
TotalItemCount = prev.TotalItemCount;
214+
IsLoaded = prev.IsLoaded;
240215
var fetchedKeys = FetchedKeys;
241216

242217
InitializeFetchedKeys();
@@ -246,35 +221,26 @@ internal void RollbackState()
246221
FetchedKeys.Add(fetchedKey);
247222
}
248223

249-
foreach (var addedKey in previousState.AddedKeys) {
224+
foreach (var addedKey in prev.AddedKeys) {
250225
if (fetchedKeys.ContainsKey(addedKey)) {
251226
FetchedKeys.Remove(addedKey);
252227
}
253-
addedKeys.Add(addedKey, addedKey);
228+
addedKeys.Add(addedKey);
254229
}
255-
foreach (var removedKey in previousState.RemovedKeys) {
230+
foreach (var removedKey in prev.RemovedKeys) {
256231
if (!FetchedKeys.ContainsKey(removedKey)) {
257232
FetchedKeys.Add(removedKey);
258233
}
259-
removedKeys.Add(removedKey, removedKey);
234+
removedKeys.Add(removedKey);
260235
}
261236
}
262237
}
263238

264239
internal void RemapKeys(KeyMapping mapping)
265240
{
266-
var oldAddedKeys = addedKeys;
267-
var oldRemovedKeys = removedKeys;
268-
InitializeDifferenceCollections();
269-
270-
foreach (var oldAddedKey in oldAddedKeys) {
271-
var newKey = mapping.TryRemapKey(oldAddedKey.Key);
272-
addedKeys.Add(newKey, newKey);
273-
}
274-
foreach (var oldRemovedKey in oldRemovedKeys) {
275-
var newKey = mapping.TryRemapKey(oldRemovedKey.Key);
276-
removedKeys.Add(newKey, newKey);
277-
}
241+
var remapper = mapping.TryRemapKey;
242+
addedKeys = addedKeys.Select(remapper).ToHashSet();
243+
removedKeys = removedKeys.Select(remapper).ToHashSet();
278244
}
279245

280246
internal bool ShouldUseForcePrefetch(Guid? currentPrefetchOperation)
@@ -299,11 +265,7 @@ internal bool ShouldUseForcePrefetch(Guid? currentPrefetchOperation)
299265
}
300266
}
301267

302-
if (isDisconnected) {
303-
return true;
304-
}
305-
306-
return false;
268+
return isDisconnected;
307269
}
308270

309271
internal void SetLastManualPrefetchId(Guid? prefetchOperationId)
@@ -343,7 +305,7 @@ public IEnumerator<Key> GetEnumerator()
343305
}
344306

345307
var fetchedKey = fetchedKeysEnumerator.Current;
346-
if (!removedKeys.ContainsKey(fetchedKey)) {
308+
if (!removedKeys.Contains(fetchedKey)) {
347309
yield return fetchedKey;
348310
}
349311
}
@@ -359,17 +321,13 @@ public IEnumerator<Key> GetEnumerator()
359321
break;
360322
}
361323

362-
var addedKey = addedKeysEnumerator.Current;
363-
yield return addedKey.Value;
324+
yield return addedKeysEnumerator.Current;
364325
}
365326
}
366327
}
367328

368329
/// <inheritdoc/>
369-
IEnumerator IEnumerable.GetEnumerator()
370-
{
371-
return GetEnumerator();
372-
}
330+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
373331

374332
#endregion
375333

@@ -386,16 +344,14 @@ private void UpdateSyncedState(IEnumerable<Key> keys, long? count)
386344
public void UpdateCachedState(IEnumerable<Key> syncronizedKeys, long? count)
387345
{
388346
FetchedKeys.Clear();
389-
var becameRemovedOnSever = new HashSet<Key>(removedKeys.Keys);
347+
HashSet<Key> becameRemovedOnSever = new(removedKeys);
390348
foreach (var key in syncronizedKeys) {
391349
if (!addedKeys.Remove(key)) {
392350
_ = becameRemovedOnSever.Remove(key);
393351
}
394352
FetchedKeys.Add(key);
395353
}
396-
foreach (var removedOnServer in becameRemovedOnSever) {
397-
_ = removedKeys.Remove(removedOnServer);
398-
}
354+
removedKeys.ExceptWith(becameRemovedOnSever);
399355

400356
TotalItemCount = count.HasValue
401357
? FetchedKeys.Count - removedKeys.Count + AddedItemsCount
@@ -409,15 +365,16 @@ private void EnsureFetchedKeysIsNotNull()
409365
}
410366
}
411367

412-
private void BackupState() => previousState = new BackupedState(this);
368+
private void BackupState() =>
369+
previousState = new(IsLoaded, TotalItemCount, addedKeys.ToArray(), removedKeys.ToArray());
413370

414371
private void InitializeFetchedKeys()
415372
=> FetchedKeys = new LruCache<Key, Key>(WellKnown.EntitySetCacheSize, cachedKey => cachedKey);
416373

417374
private void InitializeDifferenceCollections()
418375
{
419-
addedKeys = new Dictionary<Key, Key>();
420-
removedKeys = new Dictionary<Key, Key>();
376+
addedKeys = new();
377+
removedKeys = new();
421378
}
422379

423380
// Constructors
@@ -426,11 +383,7 @@ internal EntitySetState(EntitySetBase entitySet)
426383
: base(entitySet.Session)
427384
{
428385
InitializeFetchedKeys();
429-
InitializeDifferenceCollections();
430-
owner = entitySet;
431-
version = int.MinValue;
432386
isDisconnected = entitySet.Session.IsDisconnected;
433-
lastManualPrefetchId = Guid.Empty;
434387
}
435388
}
436-
}
389+
}

Orm/Xtensive.Orm/Orm/KeyMapping.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,19 @@
66

77
using System;
88
using System.Collections.Generic;
9-
using System.Collections.ObjectModel;
109
using System.Linq;
1110
using System.Runtime.Serialization;
1211
using System.Security;
1312
using Xtensive.Core;
1413

15-
1614
namespace Xtensive.Orm
1715
{
1816
/// <summary>
1917
/// Maps local ("disconnected") <see cref="Key"/> instances
2018
/// to actual (storage) <see cref="Key"/> instances.
2119
/// </summary>
2220
[Serializable]
23-
public sealed class KeyMapping : ISerializable
21+
public readonly struct KeyMapping : ISerializable
2422
{
2523
/// <summary>
2624
/// Gets the key map.
@@ -34,13 +32,8 @@ public sealed class KeyMapping : ISerializable
3432
/// </summary>
3533
/// <param name="key">The key to remap.</param>
3634
/// <returns>The mapped storage <see cref="Key"/>.</returns>
37-
public Key TryRemapKey(Key key)
38-
{
39-
Key remappedKey;
40-
if (key!=null && Map.TryGetValue(key, out remappedKey))
41-
return remappedKey;
42-
return key;
43-
}
35+
public Key TryRemapKey(Key key) =>
36+
key!=null && Map.TryGetValue(key, out var remappedKey) ? remappedKey : key;
4437

4538
/// <summary>
4639
/// Remaps the keys of cached entities

0 commit comments

Comments
 (0)