Skip to content

Commit 1610db8

Browse files
authored
Merge pull request #249 from servicetitan/upstream/OptimizeTracking
Optimize Tracking: convert ChangedValue & TrackingStackFrame to readonly structs
2 parents 166a892 + 58cf9b1 commit 1610db8

File tree

10 files changed

+83
-95
lines changed

10 files changed

+83
-95
lines changed

Extensions/Xtensive.Orm.Tracking.Tests/TrackingStackFrameTests.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class TrackingStackFrameTests : TrackingTestBase
1111
[Test]
1212
public void SafelyInsertTheSameItemTwiceTest()
1313
{
14-
var frame = new TrackingStackFrame();
14+
var frame = new TrackingStackFrame(false);
1515
var key = Key.Create(Domain, typeof(MyEntity), 1);
1616
var item = CreateTrackingItem(key, TrackingItemState.Created);
1717
frame.Register(item);
@@ -21,8 +21,8 @@ public void SafelyInsertTheSameItemTwiceTest()
2121
[Test]
2222
public void MergeTwoEmptyFramesTest()
2323
{
24-
var target = new TrackingStackFrame();
25-
var source = new TrackingStackFrame();
24+
var target = new TrackingStackFrame(false);
25+
var source = new TrackingStackFrame(false);
2626
target.MergeWith(source);
2727

2828
Assert.AreEqual(0, target.Count);
@@ -32,9 +32,9 @@ public void MergeTwoEmptyFramesTest()
3232
public void MergeEmptyFrameWithNonEmptyFrameTest()
3333
{
3434
var key = Key.Create(Domain, typeof(MyEntity), 1);
35-
var target = new TrackingStackFrame();
35+
var target = new TrackingStackFrame(false);
3636

37-
var source = new TrackingStackFrame();
37+
var source = new TrackingStackFrame(false);
3838
source.Register(CreateTrackingItem(key, TrackingItemState.Created));
3939

4040
target.MergeWith(source);
@@ -46,11 +46,11 @@ public void MergeEmptyFrameWithNonEmptyFrameTest()
4646
public void MergeNonEmptyFrameWithEmptyFrameTest()
4747
{
4848
var key = Key.Create(Domain, typeof(MyEntity), 1);
49-
var target = new TrackingStackFrame();
49+
var target = new TrackingStackFrame(false);
5050
target.Register(CreateTrackingItem(key, TrackingItemState.Created));
5151
var count = target.Count;
5252

53-
var source = new TrackingStackFrame();
53+
var source = new TrackingStackFrame(false);
5454

5555
target.MergeWith(source);
5656

@@ -61,11 +61,11 @@ public void MergeNonEmptyFrameWithEmptyFrameTest()
6161
public void MergeFramesWithTheSameItemsTest()
6262
{
6363
var key = Key.Create(Domain, typeof(MyEntity), 1);
64-
var target = new TrackingStackFrame();
64+
var target = new TrackingStackFrame(false);
6565
target.Register(CreateTrackingItem(key, TrackingItemState.Created));
6666
var count = target.Count;
6767

68-
var source = new TrackingStackFrame();
68+
var source = new TrackingStackFrame(false);
6969
source.Register(CreateTrackingItem(key, TrackingItemState.Changed));
7070

7171
target.MergeWith(source);
@@ -79,10 +79,10 @@ public void MergeFramesWithDifferentItemsTest()
7979
{
8080
var key1 = Key.Create(Domain, typeof(MyEntity), 1);
8181
var key2 = Key.Create(Domain, typeof(MyEntity), 2);
82-
var target = new TrackingStackFrame();
82+
var target = new TrackingStackFrame(false);
8383
target.Register(CreateTrackingItem(key1, TrackingItemState.Created));
8484

85-
var source = new TrackingStackFrame();
85+
var source = new TrackingStackFrame(false);
8686
source.Register(CreateTrackingItem(key2, TrackingItemState.Changed));
8787
var count = target.Count + source.Count;
8888

Extensions/Xtensive.Orm.Tracking/ChangedValue.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
1-
using Xtensive.Orm.Model;
1+
// Copyright (C) 2012-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 Xtensive.Orm.Model;
26

37
namespace Xtensive.Orm.Tracking
48
{
59
/// <summary>
610
/// Represents a pair of original and changed values for a persistent field
711
/// </summary>
8-
public sealed class ChangedValue
12+
public readonly struct ChangedValue
913
{
1014
/// <summary>
1115
/// Gets the field.
1216
/// </summary>
13-
public FieldInfo Field { get; private set; }
17+
public FieldInfo Field { get; }
1418

1519
/// <summary>
1620
/// Gets the original value.
1721
/// </summary>
18-
public object OriginalValue { get; private set; }
22+
public object OriginalValue { get; }
1923

2024
/// <summary>
2125
/// Gets the new value.
2226
/// </summary>
23-
public object NewValue { get; private set; }
27+
public object NewValue { get; }
2428

2529
/// <summary>
2630
/// Initializes a new instance of the <see cref="ChangedValue"/> class.

Extensions/Xtensive.Orm.Tracking/Interfaces/ITrackingItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ public interface ITrackingItem
3232
/// <summary>
3333
/// Gets list of detected changes of field values.
3434
/// </summary>
35-
IList<ChangedValue> ChangedValues { get; }
35+
IReadOnlyList<ChangedValue> ChangedValues { get; }
3636
}
3737
}

Extensions/Xtensive.Orm.Tracking/Internals/SessionTrackingMonitor.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2019-2020 Xtensive LLC.
1+
// Copyright (C) 2019-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

@@ -15,19 +15,20 @@ internal sealed class SessionTrackingMonitor : TrackingMonitor, ISessionTracking
1515
{
1616
private readonly Session session;
1717
private readonly DirectSessionAccessor accessor;
18-
private readonly Stack<TrackingStackFrame> stack;
18+
private readonly Stack<TrackingStackFrame> stack = new(2);
1919

2020
private void Subscribe()
2121
{
22-
session.Events.Persisting += OnPersisting;
23-
session.Events.TransactionOpened += OnOpenTransaction;
24-
session.Events.TransactionCommitted += OnCommitTransaction;
25-
session.Events.TransactionRollbacked += OnRollbackTransaction;
22+
var events = session.Events;
23+
events.Persisting += OnPersisting;
24+
events.TransactionOpened += OnOpenTransaction;
25+
events.TransactionCommitted += OnCommitTransaction;
26+
events.TransactionRollbacked += OnRollbackTransaction;
2627
}
2728

2829
private void OnOpenTransaction(object sender, TransactionEventArgs e)
2930
{
30-
stack.Push(new TrackingStackFrame());
31+
stack.Push(new TrackingStackFrame(false));
3132
}
3233

3334
private void OnCommitTransaction(object sender, TransactionEventArgs e)
@@ -76,16 +77,10 @@ private void OnPersisting(object sender, EventArgs e)
7677
[ServiceConstructor]
7778
public SessionTrackingMonitor(Session session, DirectSessionAccessor accessor)
7879
{
79-
if (session==null)
80-
throw new ArgumentNullException("session");
81-
if (accessor==null)
82-
throw new ArgumentNullException("accessor");
80+
this.session = session ?? throw new ArgumentNullException(nameof(session));
81+
this.accessor = accessor ?? throw new ArgumentNullException(nameof(accessor));
8382

84-
this.session = session;
85-
this.accessor = accessor;
86-
87-
stack = new Stack<TrackingStackFrame>();
88-
stack.Push(new TrackingStackFrame());
83+
stack.Push(new TrackingStackFrame(false));
8984

9085
Subscribe();
9186
}

Extensions/Xtensive.Orm.Tracking/Internals/TrackingItem.cs

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

@@ -16,23 +16,15 @@ namespace Xtensive.Orm.Tracking
1616
[DebuggerDisplay("{Key}")]
1717
internal sealed class TrackingItem : ITrackingItem
1818
{
19-
private IList<ChangedValue> cachedChangedValues;
19+
private IReadOnlyList<ChangedValue> cachedChangedValues;
2020

21-
public Key Key { get; private set; }
21+
public Key Key { get; }
2222

2323
public DifferentialTuple RawData { get; private set; }
2424

2525
public TrackingItemState State { get; private set; }
2626

27-
public IList<ChangedValue> ChangedValues
28-
{
29-
get
30-
{
31-
if (cachedChangedValues==null)
32-
cachedChangedValues = CalculateChangedValues().ToList().AsReadOnly();
33-
return cachedChangedValues;
34-
}
35-
}
27+
public IReadOnlyList<ChangedValue> ChangedValues => cachedChangedValues ??= CalculateChangedValues();
3628

3729
public void MergeWith(TrackingItem source)
3830
{
@@ -55,29 +47,33 @@ public void MergeWith(TrackingItem source)
5547
State = source.State;
5648
}
5749

58-
private IEnumerable<ChangedValue> CalculateChangedValues()
50+
private IReadOnlyList<ChangedValue> CalculateChangedValues()
5951
{
6052
var originalValues = RawData.Origin;
6153
var changedValues = RawData.Difference;
6254

63-
if (State==TrackingItemState.Created) {
55+
if (State == TrackingItemState.Created) {
6456
originalValues = null;
6557
changedValues = RawData.Origin;
6658
}
6759

68-
foreach (var field in Key.TypeInfo.Fields.Where(f => f.Column!=null)) {
60+
var changedValuesList = new List<ChangedValue>(Key.TypeInfo.Fields.Count);
61+
62+
foreach (var field in Key.TypeInfo.Fields.Where(f => f.Column != null)) {
6963
object origValue = null, changedValue = null;
7064
int fieldIndex = field.MappingInfo.Offset;
7165
TupleFieldState fieldState;
72-
if (originalValues!=null)
66+
if (originalValues != null)
7367
origValue = originalValues.GetValue(fieldIndex, out fieldState);
74-
if (changedValues!=null) {
68+
if (changedValues != null) {
7569
changedValue = changedValues.GetValue(fieldIndex, out fieldState);
7670
if (!fieldState.IsAvailable())
7771
continue;
7872
}
79-
yield return new ChangedValue(field, origValue, changedValue);
73+
changedValuesList.Add(new ChangedValue(field, origValue, changedValue));
8074
}
75+
76+
return changedValuesList.AsReadOnly();
8177
}
8278

8379
private void MergeWith(Tuple difference)
Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,58 @@
1-
// Copyright (C) 2012 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2012-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: Dmitri Maximov
55
// Created: 2012.05.16
66

7-
using System;
87
using System.Collections;
98
using System.Collections.Generic;
9+
using Xtensive.Core;
1010

1111
namespace Xtensive.Orm.Tracking
1212
{
13-
internal sealed class TrackingStackFrame : IEnumerable<TrackingItem>
13+
internal readonly struct TrackingStackFrame : IEnumerable<TrackingItem>
1414
{
15-
private readonly Dictionary<Key, TrackingItem> items = new Dictionary<Key, TrackingItem>();
15+
private readonly Dictionary<Key, TrackingItem> items;
1616

17-
public int Count { get { return items.Count; } }
17+
public int Count => items.Count;
1818

1919
public void Register(TrackingItem item)
2020
{
21-
if (item==null)
22-
throw new ArgumentNullException("item");
23-
24-
TrackingItem existing;
25-
if (items.TryGetValue(item.Key, out existing)) {
26-
if (item==existing)
27-
return;
21+
ArgumentValidator.EnsureArgumentNotNull(item, "item");
2822

23+
var key = item.Key;
24+
if (!items.TryGetValue(key, out var existing)) {
25+
items.Add(key, item);
26+
}
27+
else if (item != existing) {
2928
existing.MergeWith(item);
30-
return;
3129
}
32-
items.Add(item.Key, item);
3330
}
3431

35-
public void Clear()
36-
{
37-
items.Clear();
38-
}
32+
public void Clear() => items.Clear();
3933

4034
public void MergeWith(TrackingStackFrame source)
4135
{
42-
if (source==null)
43-
throw new ArgumentNullException("source");
44-
4536
foreach (var sourceItem in source) {
46-
TrackingItem target;
47-
if (items.TryGetValue(sourceItem.Key, out target))
48-
target.MergeWith(sourceItem);
49-
else
50-
items.Add(sourceItem.Key, sourceItem);
37+
var key = sourceItem.Key;
38+
if (items.TryGetValue(key, out var existing)) {
39+
existing.MergeWith(sourceItem);
40+
}
41+
else {
42+
items.Add(key, sourceItem);
43+
}
5144
}
5245
}
5346

54-
public IEnumerator<TrackingItem> GetEnumerator()
55-
{
56-
return items.Values.GetEnumerator();
57-
}
47+
public IEnumerator<TrackingItem> GetEnumerator() => items.Values.GetEnumerator();
48+
49+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
5850

59-
IEnumerator IEnumerable.GetEnumerator()
51+
// parameterless ctor not allowed in C#9
52+
//TODO: remove it after moving to C#10
53+
public TrackingStackFrame(bool _)
6054
{
61-
return GetEnumerator();
55+
items = new();
6256
}
6357
}
64-
}
58+
}

Orm/Xtensive.Orm.Tests.Core/Xtensive.Orm.Tests.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
<PackageReference Include="NUnit" Version="3.13.2" />
2222
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0" />
2323
</ItemGroup>
24-
24+
2525
</Project>

Orm/Xtensive.Orm.Tests.Framework/Xtensive.Orm.Tests.Framework.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
<PropertyGroup>
33
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
44
<IsPackable>false</IsPackable>
5-
<DefineConstants>TRACE</DefineConstants>
65
<Configurations>Debug;Release;Debug-NET5;Release-NET5;Debug-NET6;Release-NET6;</Configurations>
76
</PropertyGroup>
87
<PropertyGroup>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public IDisposable ChangeSessionHandler(SessionHandler newHandler)
5757
/// An object implementing <see cref="IDisposable"/> which
5858
/// disposal will restore previous state of
5959
/// <see cref="Session.Transaction"/> property;
60-
/// <see langword="null" />, if <see cref="Session.Transaction"/>
60+
/// <see langword="null" />, if <see cref="Session.Transaction"/>
6161
/// is already <see langword="null" />.
6262
/// </returns>
6363
public IDisposable NullifySessionTransaction()

Orm/Xtensive.Orm/Properties/Visibility.cs

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

77
using System.Runtime.CompilerServices;
88

9-
[assembly: InternalsVisibleTo("Xtensive.Orm.Tests.Framework, PublicKey=" +
9+
[assembly: InternalsVisibleTo("Xtensive.Orm.Tests.Framework, PublicKey=" +
1010
"0024000004800000940000000602000000240000525341310004000001000100fbdd689d62e9c6" +
1111
"7bb6356267f95e0b58d478cf56393c4f060fbaff42a9686272e37009ab71bfa2e41046e952f389" +
1212
"f37c6a033d1a2a5354fc97226fc469128e49e6a479ac5d1dd69d7da5607d0dc4ede0765d477745" +
1313
"1034dc3a15f1532d010db3e633e62fc5e67a3ed175457acb9dc6c9d39ccc8ecfdaae62df34d488" +
1414
"c45009b2")]
15-
[assembly: InternalsVisibleTo("Xtensive.Orm.Tests.Core, PublicKey=" +
15+
[assembly: InternalsVisibleTo("Xtensive.Orm.Tests.Core, PublicKey=" +
1616
"0024000004800000940000000602000000240000525341310004000001000100fbdd689d62e9c6" +
1717
"7bb6356267f95e0b58d478cf56393c4f060fbaff42a9686272e37009ab71bfa2e41046e952f389" +
1818
"f37c6a033d1a2a5354fc97226fc469128e49e6a479ac5d1dd69d7da5607d0dc4ede0765d477745" +
1919
"1034dc3a15f1532d010db3e633e62fc5e67a3ed175457acb9dc6c9d39ccc8ecfdaae62df34d488" +
2020
"c45009b2")]
21-
[assembly: InternalsVisibleTo("Xtensive.Orm.Tests, PublicKey=" +
21+
[assembly: InternalsVisibleTo("Xtensive.Orm.Tests, PublicKey=" +
2222
"0024000004800000940000000602000000240000525341310004000001000100fbdd689d62e9c6" +
2323
"7bb6356267f95e0b58d478cf56393c4f060fbaff42a9686272e37009ab71bfa2e41046e952f389" +
2424
"f37c6a033d1a2a5354fc97226fc469128e49e6a479ac5d1dd69d7da5607d0dc4ede0765d477745" +
@@ -29,4 +29,4 @@
2929
"7bb6356267f95e0b58d478cf56393c4f060fbaff42a9686272e37009ab71bfa2e41046e952f389" +
3030
"f37c6a033d1a2a5354fc97226fc469128e49e6a479ac5d1dd69d7da5607d0dc4ede0765d477745" +
3131
"1034dc3a15f1532d010db3e633e62fc5e67a3ed175457acb9dc6c9d39ccc8ecfdaae62df34d488" +
32-
"c45009b2")]
32+
"c45009b2")]

0 commit comments

Comments
 (0)