Skip to content

Commit 4e9225e

Browse files
committed
Introduce readonly structure RegistryItems wrapper
Improves enumeration performance
1 parent 8dddd40 commit 4e9225e

File tree

8 files changed

+81
-44
lines changed

8 files changed

+81
-44
lines changed

Orm/Xtensive.Orm/Orm/Internals/ChangeRegistries/EntityChangeRegistry.cs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
using System;
88
using System.Collections.Generic;
9-
using Xtensive.Collections;
109

1110

1211
namespace Xtensive.Orm.Internals
@@ -19,12 +18,11 @@ public sealed class EntityChangeRegistry : SessionBound
1918
private readonly HashSet<EntityState> @new = new HashSet<EntityState>();
2019
private readonly HashSet<EntityState> modified = new HashSet<EntityState>();
2120
private readonly HashSet<EntityState> removed = new HashSet<EntityState>();
22-
private int count;
2321

2422
/// <summary>
2523
/// Gets the number of registered entities.
2624
/// </summary>
27-
public int Count { get { return count; } }
25+
public int Count { get; private set; }
2826

2927
/// <summary>
3028
/// Registers the specified item.
@@ -35,7 +33,7 @@ internal void Register(EntityState item)
3533
// Remove-create sequences fix for Issue 690
3634
if (item.PersistenceState == PersistenceState.New && removed.Contains(item)) {
3735
removed.Remove(item);
38-
count--;
36+
Count--;
3937
if (item.DifferentialTuple.Difference == null) {
4038
item.SetPersistenceState(PersistenceState.Synchronized);
4139
return;
@@ -44,57 +42,50 @@ internal void Register(EntityState item)
4442
}
4543
else if (item.PersistenceState == PersistenceState.Removed && @new.Contains(item)) {
4644
@new.Remove(item);
47-
count--;
45+
Count--;
4846
return;
4947
}
5048
else if (item.PersistenceState == PersistenceState.Removed && modified.Contains(item)) {
5149
modified.Remove(item);
52-
count--;
50+
Count--;
5351
}
5452

5553
var container = GetContainer(item.PersistenceState);
5654
if (container.Add(item))
57-
count++;
55+
Count++;
5856
}
5957

6058
/// <summary>
6159
/// Gets the items with specified <paramref name="state"/>.
6260
/// </summary>
6361
/// <param name="state">The state of items to get.</param>
6462
/// <returns>The sequence of items with specified state.</returns>
65-
public IEnumerable<EntityState> GetItems(PersistenceState state)
66-
{
67-
foreach (var item in GetContainer(state))
68-
yield return item;
69-
}
63+
public RegistryItems<EntityState> GetItems(in PersistenceState state) =>
64+
new RegistryItems<EntityState>(GetContainer(state));
7065

7166
/// <summary>
7267
/// Clears the registry.
7368
/// </summary>
7469
public void Clear()
7570
{
76-
count = 0;
71+
Count = 0;
7772
@new.Clear();
7873
modified.Clear();
7974
removed.Clear();
8075
}
8176

8277
/// <exception cref="ArgumentOutOfRangeException"><paramref name="state"/> is out of range.</exception>
83-
private HashSet<EntityState> GetContainer(PersistenceState state)
78+
private HashSet<EntityState> GetContainer(in PersistenceState state)
8479
{
85-
switch (state) {
86-
case PersistenceState.New:
87-
return @new;
88-
case PersistenceState.Modified:
89-
return modified;
90-
case PersistenceState.Removed:
91-
return removed;
92-
default:
93-
throw new ArgumentOutOfRangeException("state");
94-
}
80+
return state switch {
81+
PersistenceState.New => @new,
82+
PersistenceState.Modified => modified,
83+
PersistenceState.Removed => removed,
84+
_ => throw new ArgumentOutOfRangeException("state"),
85+
};
9586
}
9687

97-
88+
9889
// Constructors
9990

10091
/// <summary>

Orm/Xtensive.Orm/Orm/Internals/ChangeRegistries/EntitySetChangeRegistry.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@ public void Register(EntitySetState entitySetState)
3333
/// Gets all registered items.
3434
/// </summary>
3535
/// <returns></returns>
36-
public IEnumerable<EntitySetState> GetItems()
37-
{
38-
return modifiedEntitySets;
39-
}
36+
public RegistryItems<EntitySetState> GetItems() =>
37+
new RegistryItems<EntitySetState>(modifiedEntitySets);
4038

4139
public void Clear()
4240
{

Orm/Xtensive.Orm/Orm/Internals/ChangeRegistries/ReferenceFieldChangeInfo.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@ internal sealed class ReferenceFieldChangeInfo
1616
/// <summary>
1717
/// Gets key of entity that owns the <see cref="ReferenceFieldChangeInfo.Field"/>.
1818
/// </summary>
19-
public Key FieldOwner { get; private set; }
19+
public Key FieldOwner { get; }
2020

2121
/// <summary>
2222
/// Gets value of field which was set.
2323
/// </summary>
24-
public Key FieldValue { get; private set; }
24+
public Key FieldValue { get; }
2525

2626
/// <summary>
2727
/// Gets field which was set.
2828
/// </summary>
29-
public FieldInfo Field { get; private set; }
29+
public FieldInfo Field { get; }
3030

3131
/// <summary>
3232
/// Auxiliary entity which associated with <see cref="EntitySet{T}"/>.
3333
/// </summary>
34-
public Key AuxiliaryEntity { get; private set; }
34+
public Key AuxiliaryEntity { get; }
3535

3636
public ReferenceFieldChangeInfo(Key fieldOwner, Key fieldValue, FieldInfo field)
3737
{

Orm/Xtensive.Orm/Orm/Internals/ChangeRegistries/ReferenceFieldsChangesRegistry.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ public void Register(Key fieldOwner, Key fieldValue, Key auxiliaryEntity, FieldI
5151
/// Gets all registered items.
5252
/// </summary>
5353
/// <returns>All registered items.</returns>
54-
public IEnumerable<ReferenceFieldChangeInfo> GetItems()
55-
{
56-
return changes;
57-
}
54+
public RegistryItems<ReferenceFieldChangeInfo> GetItems() => new(changes);
5855

5956
/// <summary>
6057
/// Removes all registered items.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (C) 2022 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System.Collections;
6+
using System.Collections.Generic;
7+
8+
9+
namespace Xtensive.Orm.Internals
10+
{
11+
/// <summary>
12+
/// Items of registry (e.g. <see cref="EntityChangeRegistry"/> or <see cref="EntitySetChangeRegistry"/>)
13+
/// </summary>
14+
public readonly struct RegistryItems<T> : IReadOnlyCollection<T>
15+
{
16+
private readonly HashSet<T> hashSet;
17+
18+
/// <inheritdoc/>
19+
public int Count => hashSet.Count;
20+
21+
/// <summary>
22+
/// Gets bare enumerator of internal collection so faster
23+
/// enumeration is available
24+
/// </summary>
25+
/// <returns><see cref="HashSet{T}.Enumerator"/>.</returns>
26+
public HashSet<T>.Enumerator GetEnumerator()
27+
=> hashSet.GetEnumerator();
28+
29+
/// <inheritdoc/>
30+
IEnumerator<T> IEnumerable<T>.GetEnumerator()
31+
=> hashSet.GetEnumerator();
32+
33+
/// <inheritdoc/>
34+
IEnumerator IEnumerable.GetEnumerator()
35+
=> hashSet.GetEnumerator();
36+
37+
public RegistryItems(HashSet<T> hashset)
38+
{
39+
hashSet = hashset;
40+
}
41+
}
42+
}

Orm/Xtensive.Orm/Orm/Services/DirectSessionAccessor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections.Generic;
99
using Xtensive.Core;
1010
using Xtensive.IoC;
11+
using Xtensive.Orm.Internals;
1112
using Xtensive.Orm.Providers;
1213

1314
namespace Xtensive.Orm.Services
@@ -76,7 +77,7 @@ public IDisposable NullifySessionTransaction()
7677
/// </summary>
7778
/// <param name="persistenceState">Type of entity change.</param>
7879
/// <returns><see cref="EntityState"/>s with the specified <paramref name="persistenceState"/>.</returns>
79-
public IEnumerable<EntityState> GetChangedEntities(PersistenceState persistenceState)
80+
public RegistryItems<EntityState> GetChangedEntities(PersistenceState persistenceState)
8081
{
8182
return Session.EntityChangeRegistry.GetItems(persistenceState);
8283
}

Orm/Xtensive.Orm/Orm/Services/Old/SessionStateAccessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public Entity this[Key key] {
5858
/// </summary>
5959
public void Invalidate()
6060
{
61-
if (Session.EntityChangeRegistry.GetItems(PersistenceState.New).Any())
61+
if (Session.EntityChangeRegistry.GetItems(PersistenceState.New).Count > 0)
6262
throw new InvalidOperationException(Strings.UnableToInvalidateSessionStateNewlyCreatedEntitiesAreAttachedToSession);
6363
Session.Invalidate();
6464
}

Orm/Xtensive.Orm/Orm/VersionCapturer.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,21 @@ private void TransactionOpened(object sender, TransactionEventArgs transactionEv
6767
private void Persisting(object sender, EventArgs eventArgs)
6868
{
6969
var registry = Session.EntityChangeRegistry;
70-
var modifiedStates = registry.GetItems(PersistenceState.Modified)
71-
.Concat(registry.GetItems(PersistenceState.New));
72-
foreach (var state in modifiedStates) {
73-
var versionTuple = state.Type.VersionExtractor.Apply(TupleTransformType.Tuple, state.Tuple);
74-
modifiedVersions.Add(state.Key, new VersionInfo(versionTuple), true);
70+
//two foreach operators are faster than one concat because of bare hashset enumerator
71+
foreach(var state in registry.GetItems(PersistenceState.Modified)) {
72+
AddModifiedVersion(state);
73+
}
74+
foreach (var state in registry.GetItems(PersistenceState.New)) {
75+
AddModifiedVersion(state);
7576
}
77+
7678
removedKeys.AddRange(registry.GetItems(PersistenceState.Removed).Select(s => s.Key));
79+
80+
void AddModifiedVersion(EntityState state)
81+
{
82+
var versionTuple = state.Type.VersionExtractor.Apply(TupleTransformType.Tuple, state.Tuple);
83+
_ = modifiedVersions.Add(state.Key, new VersionInfo(versionTuple), true);
84+
}
7785
}
7886

7987
#endregion

0 commit comments

Comments
 (0)