Skip to content

Commit a4034a0

Browse files
authored
Merge pull request #186 from servicetitan/upstream/optimizeSomeHotPaths
Optimize some hot paths
2 parents 84f4864 + 31749a1 commit a4034a0

11 files changed

Lines changed: 102 additions & 88 deletions

File tree

Orm/Xtensive.Orm/Modelling/Comparison/Comparer.cs

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Xtensive.Reflection;
1515
using Xtensive.Modelling.Attributes;
1616
using Xtensive.Modelling.Comparison.Hints;
17+
using Xtensive.Orm;
1718

1819

1920
namespace Xtensive.Modelling.Comparison
@@ -149,21 +150,27 @@ protected virtual Difference VisitNode(Node source, Node target)
149150
{
150151
using (TryActivate(source, target, (s, t) => new NodeDifference(s, t))) {
151152
IgnoreHint ignoreHint = null;
152-
if (source!=null)
153+
if (source!=null) {
153154
ignoreHint = Hints.GetHint<IgnoreHint>(source);
154-
if (ignoreHint!=null)
155+
}
156+
157+
if (ignoreHint!=null) {
155158
return null;
159+
}
156160

157161
var context = Context;
158162
var difference = (NodeDifference) context.Difference;
159-
if (difference==null)
163+
if (difference==null) {
160164
throw new NullReferenceException();
165+
}
166+
161167
var any = source ?? target;
162-
if (any==null)
168+
if (any==null) {
163169
throw Exceptions.InternalError(Strings.ExBothSourceAndTargetAreNull, CoreLog.Instance);
170+
}
164171

165172

166-
bool isNewDifference = TryRegisterDifference(source, target, difference);
173+
var isNewDifference = TryRegisterDifference(source, target, difference);
167174
if (isNewDifference) {
168175
// Build movement info
169176
difference.MovementInfo = BuildMovementInfo(source, target);
@@ -353,68 +360,74 @@ protected IEnumerable<NodeDifference> GetPropertyDifferences(NodeDifference diff
353360
/// <exception cref="NullReferenceException">Current difference is not <see cref="NodeCollectionDifference"/>.</exception>
354361
protected virtual Difference VisitNodeCollection(NodeCollection source, NodeCollection target)
355362
{
356-
using (TryActivate(source, target, (s,t) => new NodeCollectionDifference(s,t))) {
363+
using (TryActivate(source, target, (s, t) => new NodeCollectionDifference(s, t))) {
357364
var context = Context;
358365
var difference = (NodeCollectionDifference) context.Difference;
359-
if (difference==null)
366+
if (difference == null) {
360367
throw new NullReferenceException();
368+
}
361369

362-
bool isNewDifference = TryRegisterDifference(source, target, difference);
370+
TryRegisterDifference(source, target, difference);
363371
difference.ItemChanges.Clear();
364372

365-
// Inlining 2 below lines leads to error in PEVerify.exe!
366-
// (well-known issue with null coalescing operator + cast)
367-
var sourceItems = (IEnumerable) source;
368-
var targetItems = (IEnumerable) target;
373+
if (source?.Count == 0 && target?.Count == 0) {
374+
return null;
375+
}
376+
377+
var sourceSize = source?.Count ?? 0;
378+
var sourceKeyMap = new Dictionary<string, Node>(sourceSize, StringComparer.OrdinalIgnoreCase);
379+
for (var index = sourceSize; index-- > 0;) {
380+
var node = source[index];
381+
sourceKeyMap.Add(GetNodeComparisonKey(node), node);
382+
}
369383

370-
var src = sourceItems ?? new ReadOnlyList<Node>(new Node[] {});
371-
var tgt = targetItems ?? new ReadOnlyList<Node>(new Node[] {});
384+
var targetSize = target?.Count ?? 0;
385+
var targetKeyMap = new Dictionary<string, Node>(targetSize, StringComparer.OrdinalIgnoreCase);
386+
for (var index = targetSize; index-- > 0;) {
387+
var node = target[index];
388+
targetKeyMap.Add(GetNodeComparisonKey(node), node);
389+
}
372390

373-
var srcCount = source!=null ? source.Count : 0;
374-
var tgtCount = target!=null ? target.Count : 0;
391+
foreach (var sourceItem in sourceKeyMap) {
392+
if (!targetKeyMap.ContainsKey(sourceItem.Key)) {
393+
var d = Visit(sourceKeyMap[sourceItem.Key], null);
394+
if (d != null) {
395+
difference.ItemChanges.Add((NodeDifference) d);
396+
}
397+
}
398+
}
375399

376-
if (srcCount==0 && tgtCount==0)
377-
return null;
378-
var someItems = srcCount!=0 ? src : tgt;
379-
var someItem = someItems.Cast<Node>().First();
380-
381-
Func<Node, Pair<Node, string>> keyExtractor =
382-
n => new Pair<Node, string>(n, GetNodeComparisonKey(n));
383-
384-
var sourceKeyMap = src
385-
.Cast<Node>()
386-
.Select(keyExtractor)
387-
.ToDictionary(pair => pair.Second, pair => pair.First, StringComparer.OrdinalIgnoreCase);
388-
var targetKeyMap = tgt
389-
.Cast<Node>()
390-
.Select(keyExtractor)
391-
.ToDictionary(pair => pair.Second, pair => pair.First, StringComparer.OrdinalIgnoreCase);
392-
393-
var sourceKeys = src.Cast<Node>().Select(n => keyExtractor(n).Second);
394-
var targetKeys = tgt.Cast<Node>().Select(n => keyExtractor(n).Second);
395-
var commonKeys = sourceKeys.Intersect(targetKeys, StringComparer.OrdinalIgnoreCase);
396-
397-
var sequence =
398-
sourceKeys.Except(commonKeys, StringComparer.OrdinalIgnoreCase)
399-
.Select(k => new {Index = sourceKeyMap[k].Index, Type = 0,
400-
Source = sourceKeyMap[k], Target = (Node) null})
401-
.Concat(commonKeys
402-
.Select(k => new {Index = targetKeyMap[k].Index, Type = 1,
403-
Source = sourceKeyMap[k], Target = targetKeyMap[k]}))
404-
.Concat(targetKeys.Except(commonKeys, StringComparer.OrdinalIgnoreCase)
405-
.Select(k => new {Index = targetKeyMap[k].Index, Type = 2,
406-
Source = (Node) null, Target = targetKeyMap[k]}))
407-
.OrderBy(i => i.Type!=0).ThenBy(i => i.Index).ThenBy(i => i.Type);
408-
409-
foreach (var i in sequence) {
410-
var d = Visit(i.Source, i.Target);
411-
if (d!=null)
400+
foreach (var targetItem in targetKeyMap) {
401+
var (s, t) = sourceKeyMap.ContainsKey(targetItem.Key)
402+
? (sourceKeyMap[targetItem.Key], targetKeyMap[targetItem.Key])
403+
: (null, targetKeyMap[targetItem.Key]);
404+
var d = Visit(s, t);
405+
if (d != null) {
412406
difference.ItemChanges.Add((NodeDifference) d);
407+
}
408+
413409
}
410+
difference.ItemChanges.Sort(CompareNodeDifference);
414411

415-
return (difference.ItemChanges.Count!=0) ? difference : null;
412+
return difference.ItemChanges.Count != 0 ? difference : null;
416413
}
417414
}
415+
416+
// Sort by items only with source, then by (target ?? source).Index then with source and target and then only with target
417+
private static int CompareNodeDifference(NodeDifference curr, NodeDifference other)
418+
{
419+
var currType = curr.Source != null && curr.Target != null ? 1 : curr.Source == null ? 3 : 0;
420+
var otherType = other.Source != null && other.Target != null ? 1 : other.Source == null ? 3 : 0;
421+
var typeIsNot0Comparison = (currType != 0).CompareTo(otherType != 0);
422+
if (typeIsNot0Comparison != 0) {
423+
return typeIsNot0Comparison;
424+
}
425+
426+
var currIndex = (curr.Target ?? curr.Source)?.Index ?? 0;
427+
var otherIndex = (other.Target ?? other.Source)?.Index ?? 0;
428+
var indexComparison = currIndex.CompareTo(otherIndex);
429+
return indexComparison != 0 ? indexComparison : currType.CompareTo(otherType);
430+
}
418431

419432
/// <summary>
420433
/// Visits specified objects.

Orm/Xtensive.Orm/Modelling/Comparison/Upgrader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public ReadOnlyList<NodeAction> GetUpgradeSequence(Difference difference, HintSe
121121
ArgumentValidator.EnsureArgumentNotNull(hints, nameof(hints));
122122
ArgumentValidator.EnsureArgumentNotNull(comparer, nameof(comparer));
123123
if (difference == null) {
124-
return new ReadOnlyList<NodeAction>(Enumerable.Empty<NodeAction>().ToList());
124+
return ReadOnlyList<NodeAction>.Empty;
125125
}
126126

127127
TemporaryRenames = new Dictionary<string, Node>(StringComparer.OrdinalIgnoreCase);

Orm/Xtensive.Orm/Modelling/Node.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public abstract class Node : LockableBase,
3434
/// Path delimiter character.
3535
/// </summary>
3636
public static readonly char PathDelimiter = '/';
37+
public static readonly string PathDelimiterString = PathDelimiter.ToString();
3738
/// <summary>
3839
/// Path escape character.
3940
/// </summary>
@@ -185,18 +186,24 @@ public IEnumerable<Pair<string, IPathNode>> GetPathNodes(bool nestedOnly)
185186
public string Path {
186187
[DebuggerStepThrough]
187188
get {
188-
if (cachedPath!=null)
189+
if (cachedPath!=null) {
189190
return cachedPath;
190-
if (Parent==null)
191+
}
192+
193+
if (Parent==null) {
191194
return string.Empty;
192-
string parentPath = Parent.Path;
193-
if (parentPath.Length!=0)
194-
parentPath += PathDelimiter;
195+
}
196+
197+
var parentPath = Parent.Path;
198+
if (parentPath.Length!=0) {
199+
parentPath += PathDelimiterString;
200+
}
201+
195202
return string.Concat(
196203
parentPath,
197204
Nesting.EscapedPropertyName,
198-
Nesting.IsNestedToCollection ? PathDelimiter.ToString() : string.Empty,
199-
Nesting.IsNestedToCollection ? EscapedName : string.Empty);
205+
Nesting.IsNestedToCollection ? PathDelimiterString : null,
206+
Nesting.IsNestedToCollection ? EscapedName : null);
200207
}
201208
}
202209

@@ -972,7 +979,7 @@ public override string ToString()
972979
var m = Model;
973980
var fullName = Path;
974981
if (m != null) {
975-
fullName = string.Concat(m.EscapedName, PathDelimiter, fullName);
982+
fullName = string.Concat(m.EscapedName, PathDelimiterString, fullName);
976983
}
977984

978985
if (!Nesting.IsNestedToCollection && !(this is IModel)) {

Orm/Xtensive.Orm/Modelling/NodeCollection.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,13 @@
55
// Created: 2009.03.16
66

77
using System;
8-
using System.Linq;
98
using System.Collections;
109
using System.Collections.Generic;
1110
using System.Collections.Specialized;
1211
using System.Diagnostics;
1312
using System.Runtime.Serialization;
14-
using Xtensive.Collections;
1513
using Xtensive.Core;
1614

17-
using Xtensive.Modelling;
18-
using Xtensive.Modelling.Comparison;
19-
2015

2116
namespace Xtensive.Modelling
2217
{
@@ -28,8 +23,6 @@ public abstract class NodeCollection : LockableBase,
2823
INodeCollection,
2924
IDeserializationCallback
3025
{
31-
private static readonly ReadOnlyList<Node> emptyCountable =
32-
new ReadOnlyList<Node>(new List<Node>(), false);
3326
[NonSerialized]
3427
private string escapedName;
3528
[NonSerialized]
@@ -90,7 +83,7 @@ public string Path {
9083
if (parentPath.Length==0)
9184
return EscapedName;
9285
return string.Concat(
93-
parentPath, Node.PathDelimiter,
86+
parentPath, Node.PathDelimiterString,
9487
EscapedName);
9588
}
9689
}
@@ -355,7 +348,7 @@ public override string ToString()
355348
var m = Model;
356349
string fullName = Path;
357350
if (m!=null)
358-
fullName = string.Concat(m.EscapedName, Node.PathDelimiter, fullName);
351+
fullName = string.Concat(m.EscapedName, Node.PathDelimiterString, fullName);
359352
return string.Format(Strings.NodeInfoFormat, fullName, Count);
360353
}
361354

Orm/Xtensive.Orm/Orm/Building/PrefetchActionContainer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public PrefetchActionContainer(TypeInfo type, List<AssociationInfo> associations
4242
{
4343
this.type = type;
4444
this.associations = associations;
45-
fields = new ReadOnlyList<PrefetchFieldDescriptor>(new List<PrefetchFieldDescriptor>());
45+
fields = ReadOnlyList<PrefetchFieldDescriptor>.Empty;
4646
}
4747
}
4848
}

Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -600,10 +600,12 @@ public override void UpdateState()
600600
targetAssociations = new ReadOnlyList<AssociationInfo>(GetTargetAssociations());
601601
ownerAssociations = new ReadOnlyList<AssociationInfo>(GetOwnerAssociations());
602602

603-
int adapterIndex = 0;
604-
foreach (FieldInfo field in Fields)
605-
if (field.IsStructure || field.IsEntitySet)
603+
var adapterIndex = 0;
604+
foreach (var field in Fields) {
605+
if (field.IsStructure || field.IsEntitySet) {
606606
field.AdapterIndex = adapterIndex++;
607+
}
608+
}
607609

608610
affectedIndexes.UpdateState();
609611
indexes.UpdateState();
@@ -618,14 +620,14 @@ public override void UpdateState()
618620

619621
if (IsEntity) {
620622
if (HasVersionRoots) {
621-
versionFields = new ReadOnlyList<FieldInfo>(new List<FieldInfo>());
623+
versionFields = ReadOnlyList<FieldInfo>.Empty;
622624
versionColumns = new ReadOnlyList<ColumnInfo>(new List<ColumnInfo>());
623625
}
624626
else {
625627
versionFields = new ReadOnlyList<FieldInfo>(GetVersionFields());
626628
versionColumns = new ReadOnlyList<ColumnInfo>(GetVersionColumns());
627629
}
628-
HasVersionFields = versionFields.Any();
630+
HasVersionFields = versionFields.Count > 0;
629631
HasExplicitVersionFields = versionFields.Any(f => f.ManualVersion || f.AutoVersion);
630632
}
631633

@@ -634,7 +636,7 @@ public override void UpdateState()
634636
// We'll check that all implementors are mapped to the same database later.
635637
// MappingSchema is not important: it's copied for consistency.
636638
var firstImplementor = GetImplementors().FirstOrDefault();
637-
if (firstImplementor!=null) {
639+
if (firstImplementor != null) {
638640
MappingDatabase = firstImplementor.MappingDatabase;
639641
MappingSchema = firstImplementor.MappingSchema;
640642
}
@@ -698,10 +700,10 @@ public override void UpdateState()
698700
sequence.AddRange(b);
699701

700702
var first = sequence.Where(a => a.Ancestors.Count > 0).ToList();
701-
if (first.Count==0)
703+
if (first.Count == 0)
702704
removalSequence = new ReadOnlyList<AssociationInfo>(sequence);
703705
else {
704-
var second = sequence.Where(a => a.Ancestors.Count==0).ToList();
706+
var second = sequence.Where(a => a.Ancestors.Count == 0);
705707
removalSequence = new ReadOnlyList<AssociationInfo>(first.Concat(second).ToList());
706708
}
707709
}
@@ -750,7 +752,7 @@ private bool GetIsLeaf()
750752

751753
private void CreateTupleDescriptor()
752754
{
753-
var orderedColumns = columns.OrderBy(c => c.Field.MappingInfo.Offset).ToList();
755+
var orderedColumns = columns.OrderBy(c => c.Field.MappingInfo.Offset);
754756
columns = new ColumnInfoCollection(this, "Columns");
755757
columns.AddRange(orderedColumns);
756758
TupleDescriptor = TupleDescriptor.Create(

Orm/Xtensive.Orm/Orm/Operation.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ public abstract class Operation : IOperation
2525
{
2626
private static readonly ReadOnlyDictionary<string, Key> EmptyIdentifiedEntities =
2727
new ReadOnlyDictionary<string, Key>(new Dictionary<string, Key>());
28-
private static readonly ReadOnlyList<IOperation> EmptyOperations =
29-
new ReadOnlyList<IOperation>(new List<IOperation>());
28+
private static readonly ReadOnlyList<IOperation> EmptyOperations = ReadOnlyList<IOperation>.Empty;
3029

3130
private ReadOnlyDictionary<string, Key> identifiedEntities = EmptyIdentifiedEntities;
3231
private ReadOnlyList<IOperation> precedingOperations = EmptyOperations;

Orm/Xtensive.Orm/Orm/Upgrade/Hints/ChangeFieldTypeHint.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public ChangeFieldTypeHint(Type type, string fieldName)
8888

8989
Type = type;
9090
FieldName = fieldName;
91-
AffectedColumns = new ReadOnlyList<string>(new List<string>());
91+
AffectedColumns = ReadOnlyList<string>.Empty;
9292
}
9393

9494
/// <summary>

Orm/Xtensive.Orm/Orm/Upgrade/Hints/RemoveFieldHint.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public RemoveFieldHint(string type, string field)
9191
ArgumentValidator.EnsureArgumentNotNullOrEmpty(field, "field");
9292
Type = type;
9393
Field = field;
94-
AffectedColumns = new ReadOnlyList<string>(new List<string>());
94+
AffectedColumns = ReadOnlyList<string>.Empty;
9595
}
9696

9797
/// <summary>
@@ -106,7 +106,7 @@ public RemoveFieldHint(Type type, string field)
106106

107107
Type = type.FullName;
108108
Field = field;
109-
AffectedColumns = new ReadOnlyList<string>(new List<string>());
109+
AffectedColumns = ReadOnlyList<string>.Empty;
110110
}
111111

112112
/// <summary>

Orm/Xtensive.Orm/Orm/Upgrade/Hints/RemoveTypeHint.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public RemoveTypeHint(string type)
7575
{
7676
ArgumentValidator.EnsureArgumentNotNullOrEmpty(type, "sourceType");
7777
Type = type;
78-
AffectedTables = new ReadOnlyList<string>(new List<string>());
78+
AffectedTables = ReadOnlyList<string>.Empty;
7979
}
8080
}
8181
}

0 commit comments

Comments
 (0)