Skip to content

Commit 166a892

Browse files
authored
Merge pull request #283 from DataObjects-NET/master-faster-registry-enumerations
XxxChangeRegistry.GetItems() methods' results are faster to enumerate
2 parents f50b4d6 + 9484c7c commit 166a892

10 files changed

+110
-79
lines changed

ChangeLog/7.1.0-Beta-2-dev.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
[main] SqlCustomFunctionCall and SqlFunctionCall share one base type
1313
[main] SqlFunctionCall.Arguments property is IReadOnlyList now and parameters can't be changed after instance creation
1414
[main] Xtensive.Sql.Dml.Extensions.IsNullReference() extension method is marked obsolete, use 'is null' operator instead
15+
[main] DirectSessionAccessor.GetChangedEntities() result type changed to improve enumeration
16+
[main] EntityChangeRegistry.GetItems(PersistenceState) changed result type to improve enumeration
17+
[main] EntitySetChangeRegistry.GetItems() changed result type to improve enumeration
1518
[main] IgnoreRule now has only one public constructor - parameterless
1619
[main] IgnoreRule supports indexes
1720
[main] BitFaster.Caching package reference is updated to 1.0.7

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

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
// Copyright (C) 2008-2020 Xtensive LLC.
1+
// Copyright (C) 2008-2022 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Dmitri Maximov
55
// Created: 2008.11.03
66

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

1110

1211
namespace Xtensive.Orm.Internals
@@ -16,15 +15,14 @@ namespace Xtensive.Orm.Internals
1615
/// </summary>
1716
public sealed class EntityChangeRegistry : SessionBound
1817
{
19-
private readonly HashSet<EntityState> @new = new HashSet<EntityState>();
20-
private readonly HashSet<EntityState> modified = new HashSet<EntityState>();
21-
private readonly HashSet<EntityState> removed = new HashSet<EntityState>();
22-
private int count;
18+
private readonly HashSet<EntityState> @new = new();
19+
private readonly HashSet<EntityState> modified = new();
20+
private readonly HashSet<EntityState> removed = new();
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.
@@ -34,67 +32,61 @@ internal void Register(EntityState item)
3432
{
3533
// Remove-create sequences fix for Issue 690
3634
if (item.PersistenceState == PersistenceState.New && removed.Contains(item)) {
37-
removed.Remove(item);
38-
count--;
35+
_ = removed.Remove(item);
36+
Count--;
3937
if (item.DifferentialTuple.Difference == null) {
4038
item.SetPersistenceState(PersistenceState.Synchronized);
4139
return;
4240
}
4341
item.SetPersistenceState(PersistenceState.Modified);
4442
}
4543
else if (item.PersistenceState == PersistenceState.Removed && @new.Contains(item)) {
46-
@new.Remove(item);
47-
count--;
44+
_ = @new.Remove(item);
45+
Count--;
4846
return;
4947
}
5048
else if (item.PersistenceState == PersistenceState.Removed && modified.Contains(item)) {
51-
modified.Remove(item);
52-
count--;
49+
_ = modified.Remove(item);
50+
Count--;
5351
}
5452

5553
var container = GetContainer(item.PersistenceState);
56-
if (container.Add(item))
57-
count++;
54+
if (container.Add(item)) {
55+
Count++;
56+
}
5857
}
5958

6059
/// <summary>
6160
/// Gets the items with specified <paramref name="state"/>.
6261
/// </summary>
6362
/// <param name="state">The state of items to get.</param>
6463
/// <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-
}
64+
public RegistryItems<EntityState> GetItems(in PersistenceState state) =>
65+
new(GetContainer(state));
7066

7167
/// <summary>
7268
/// Clears the registry.
7369
/// </summary>
7470
public void Clear()
7571
{
76-
count = 0;
72+
Count = 0;
7773
@new.Clear();
7874
modified.Clear();
7975
removed.Clear();
8076
}
8177

8278
/// <exception cref="ArgumentOutOfRangeException"><paramref name="state"/> is out of range.</exception>
83-
private HashSet<EntityState> GetContainer(PersistenceState state)
79+
private HashSet<EntityState> GetContainer(in PersistenceState state)
8480
{
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-
}
81+
return state switch {
82+
PersistenceState.New => @new,
83+
PersistenceState.Modified => modified,
84+
PersistenceState.Removed => removed,
85+
_ => throw new ArgumentOutOfRangeException(nameof(state)),
86+
};
9587
}
9688

97-
89+
9890
// Constructors
9991

10092
/// <summary>

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

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2014 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2014-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.
44
// Created by: Alexey Kulakov
55
// Created: 2014.03.27
66

@@ -13,7 +13,7 @@ namespace Xtensive.Orm.Internals
1313
/// </summary>
1414
public sealed class EntitySetChangeRegistry : SessionBound
1515
{
16-
private readonly HashSet<EntitySetState> modifiedEntitySets = new HashSet<EntitySetState>();
16+
private readonly HashSet<EntitySetState> modifiedEntitySets = new();
1717

1818
/// <summary>
1919
/// Count of registered <see cref="EntitySetState"/>.
@@ -24,24 +24,18 @@ public sealed class EntitySetChangeRegistry : SessionBound
2424
/// Register the specified <see cref="EntitySetState"/>.
2525
/// </summary>
2626
/// <param name="entitySetState"><see cref="EntitySetState"/> to bound.</param>
27-
public void Register(EntitySetState entitySetState)
28-
{
29-
modifiedEntitySets.Add(entitySetState);
30-
}
27+
public void Register(EntitySetState entitySetState) => modifiedEntitySets.Add(entitySetState);
3128

3229
/// <summary>
3330
/// Gets all registered items.
3431
/// </summary>
3532
/// <returns></returns>
36-
public IEnumerable<EntitySetState> GetItems()
37-
{
38-
return modifiedEntitySets;
39-
}
33+
public RegistryItems<EntitySetState> GetItems() => new(modifiedEntitySets);
4034

41-
public void Clear()
42-
{
43-
modifiedEntitySets.Clear();
44-
}
35+
/// <summary>
36+
/// Clears the registry.
37+
/// </summary>
38+
public void Clear() => modifiedEntitySets.Clear();
4539

4640
/// <summary>
4741
/// Initializes a new instance of this class.

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2014 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2014-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.
44
// Created by: Alexey Kulakov
55
// Created: 2014.04.07
66

@@ -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/ReferenceFieldsChangesRegistry.cs renamed to Orm/Xtensive.Orm/Orm/Internals/ChangeRegistries/ReferenceFieldsChangesRegistry.cs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2014 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2014-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.
44
// Created by: Alexey Kulakov
55
// Created: 2014.04.07
66

@@ -15,7 +15,7 @@ namespace Xtensive.Orm.Internals
1515
/// </summary>
1616
internal sealed class ReferenceFieldsChangesRegistry : SessionBound
1717
{
18-
private readonly HashSet<ReferenceFieldChangeInfo> changes = new HashSet<ReferenceFieldChangeInfo>();
18+
private readonly HashSet<ReferenceFieldChangeInfo> changes = new ();
1919

2020
/// <summary>
2121
/// Registrates information about field which value was set.
@@ -51,23 +51,14 @@ 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.
6158
/// </summary>
62-
public void Clear()
63-
{
64-
changes.Clear();
65-
}
59+
public void Clear() => changes.Clear();
6660

67-
private void Register(ReferenceFieldChangeInfo fieldChangeInfo)
68-
{
69-
changes.Add(fieldChangeInfo);
70-
}
61+
private void Register(ReferenceFieldChangeInfo fieldChangeInfo) => changes.Add(fieldChangeInfo);
7162

7263
public ReferenceFieldsChangesRegistry(Session session)
7364
: base(session)
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)