Skip to content

Commit 83d4b23

Browse files
committed
Optimizations of Domain build (#83)
* Optimize TypeInfo.Validators * Optimization: create FieldInfo.Assocations on demand * Avoid unnecessary .ToList() in TopologicalSorter.Sort() * DO_SAFE_COLLECTION_WRAPPER constant * readonly struct ColumnIndexMap * Optimize IReadOnlyList in SelectProvider
1 parent 5229164 commit 83d4b23

38 files changed

+209
-205
lines changed

Directory.Build.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,7 @@
128128
</ItemGroup>
129129

130130
<Import Condition="Exists('User.Directory.Build.props')" Project="User.Directory.Build.props" />
131+
<PropertyGroup>
132+
<DefineConstants Condition="'$(DO_SAFE_COLLECTION_WRAPPER)'!='false'">$(DefineConstants);DO_SAFE_COLLECTION_WRAPPER</DefineConstants>
133+
</PropertyGroup>
131134
</Project>

Orm/Xtensive.Orm.Tests.Core/Helpers/TopologicalSorterTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private static void InternalPerformanceTest(int nodeCount, int averageConnection
6363
List<Node<int, int>> removedEdges;
6464
var result = TopologicalSorter.Sort(nodes, out removedEdges);
6565
if (!allowLoops)
66-
Assert.AreEqual(nodeCount, result.Count);
66+
Assert.AreEqual(nodeCount, result.Count());
6767
}
6868
GC.GetTotalMemory(true);
6969
}
@@ -132,7 +132,7 @@ public void CombinedTest()
132132
private void TestSort<T>(T[] data, Predicate<T, T> connector, T[] expected, T[] loops)
133133
{
134134
List<Node<T, object>> actualLoopNodes;
135-
List<T> actual = TopologicalSorter.Sort(data, connector, out actualLoopNodes);
135+
List<T> actual = TopologicalSorter.Sort(data, connector, out actualLoopNodes).ToList();
136136
T[] actualLoops = null;
137137
if (actualLoopNodes != null)
138138
actualLoops = actualLoopNodes

Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using System;
99
using System.Collections;
1010
using System.Collections.Generic;
11-
11+
using System.Runtime.CompilerServices;
1212

1313
namespace Xtensive.Core
1414
{
@@ -119,5 +119,25 @@ public static void EnsureIndexIsValid(this IList list, int index)
119119
if (index < 0 || index >= list.Count)
120120
throw new IndexOutOfRangeException(Strings.ExIndexOutOfRange);
121121
}
122+
123+
#if DO_SAFE_COLLECTION_WRAPPER
124+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
125+
public static IReadOnlyList<T> AsSafeWrapper<T>(this List<T> list) => list.AsReadOnly();
126+
127+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
128+
public static IReadOnlyList<T> AsSafeWrapper<T>(this IReadOnlyList<T> list) => new ReadOnlyCollection(list);
129+
130+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
131+
public static IReadOnlyList<T> AsSafeWrapper<T>(this T[] array) => Array.AsReadOnly(array);
132+
#else
133+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
134+
public static IReadOnlyList<T> AsSafeWrapper<T>(this List<T> list) => list;
135+
136+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
137+
public static IReadOnlyList<T> AsSafeWrapper<T>(this IReadOnlyList<T> list) => list;
138+
139+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
140+
public static IReadOnlyList<T> AsSafeWrapper<T>(this T[] array) => array;
141+
#endif
122142
}
123-
}
143+
}

Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections.Generic;
99
using System.Collections.ObjectModel;
1010
using System.Linq.Expressions;
11+
using Xtensive.Core;
1112

1213
namespace Xtensive.Linq
1314
{
@@ -17,7 +18,7 @@ namespace Xtensive.Linq
1718
/// </summary>
1819
public abstract class ExpressionVisitor : ExpressionVisitor<Expression>
1920
{
20-
protected override ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
21+
protected override IReadOnlyList<Expression> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
2122
{
2223
bool isChanged = false;
2324
var results = new List<Expression>(expressions.Count);
@@ -27,7 +28,7 @@ protected override ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCo
2728
results.Add(p);
2829
isChanged |= !ReferenceEquals(expression, p);
2930
}
30-
return isChanged ? results.AsReadOnly() : expressions;
31+
return isChanged ? results.AsSafeWrapper() : expressions;
3132
}
3233

3334
/// <summary>
@@ -37,8 +38,8 @@ protected override ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCo
3738
/// <returns>Visit result.</returns>
3839
protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
3940
{
40-
ReadOnlyCollection<Expression> arguments = VisitExpressionList(initializer.Arguments);
41-
if (arguments!=initializer.Arguments) {
41+
var arguments = VisitExpressionList(initializer.Arguments);
42+
if (arguments != initializer.Arguments) {
4243
return Expression.ElementInit(initializer.AddMethod, arguments);
4344
}
4445
return initializer;
@@ -49,7 +50,7 @@ protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
4950
/// </summary>
5051
/// <param name="original">The original element initializer list.</param>
5152
/// <returns>Visit result.</returns>
52-
protected virtual ReadOnlyCollection<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
53+
protected virtual IReadOnlyList<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
5354
{
5455
var results = new List<ElementInit>();
5556
bool isChanged = false;
@@ -59,7 +60,7 @@ protected virtual ReadOnlyCollection<ElementInit> VisitElementInitializerList(Re
5960
results.Add(p);
6061
isChanged |= !ReferenceEquals(originalIntializer, p);
6162
}
62-
return isChanged ? results.AsReadOnly() : original;
63+
return isChanged ? results.AsSafeWrapper() : original;
6364
}
6465

6566
/// <inheritdoc/>
@@ -246,7 +247,7 @@ protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBindi
246247
/// </summary>
247248
/// <param name="original">The original binding list.</param>
248249
/// <returns>Visit result.</returns>
249-
protected virtual ReadOnlyCollection<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
250+
protected virtual IReadOnlyList<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
250251
{
251252
var results = new List<MemberBinding>();
252253
bool isChanged = false;
@@ -256,7 +257,7 @@ protected virtual ReadOnlyCollection<MemberBinding> VisitBindingList(ReadOnlyCol
256257
results.Add(p);
257258
isChanged |= !ReferenceEquals(originalBinding, p);
258259
}
259-
return isChanged ? results.AsReadOnly() : original;
260+
return isChanged ? results.AsSafeWrapper() : original;
260261
}
261262

262263
protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding)

Orm/Xtensive.Orm/Linq/ExpressionVisitor{TResult}.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Collections.ObjectModel;
1010
using System.Linq.Expressions;
1111
using Xtensive.Reflection;
12+
using Xtensive.Core;
1213

1314

1415

@@ -139,14 +140,14 @@ protected virtual TResult Visit(Expression e)
139140
/// </summary>
140141
/// <param name="expressions">The expression list.</param>
141142
/// <returns>Visit result.</returns>
142-
protected virtual ReadOnlyCollection<TResult> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
143+
protected virtual IReadOnlyList<TResult> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
143144
{
144145
var results = new List<TResult>(expressions.Count);
145146
for (int i = 0, n = expressions.Count; i < n; i++) {
146147
var p = Visit(expressions[i]);
147148
results.Add(p);
148149
}
149-
return results.AsReadOnly();
150+
return results.AsSafeWrapper();
150151
}
151152

152153
/// <summary>

Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public static void BuildAssociation(BuildingContext context, FieldDef fieldDef,
2525
fieldDef.OnOwnerRemove, fieldDef.OnTargetRemove);
2626
association.Name = context.NameBuilder.BuildAssociationName(association);
2727
context.Model.Associations.Add(association);
28-
field.Associations.Add(association);
28+
field.AddAssociation(association);
2929

3030
if (!fieldDef.PairTo.IsNullOrEmpty())
3131
context.PairedAssociations.Add(new Pair<AssociationInfo, string>(association, fieldDef.PairTo));
@@ -40,8 +40,8 @@ public static void BuildAssociation(BuildingContext context, AssociationInfo ori
4040
.Where(a => a.TargetType == association.TargetType)
4141
.ToList();
4242
foreach (var toRemove in associationsToRemove)
43-
field.Associations.Remove(toRemove);
44-
field.Associations.Add(association);
43+
field.RemoveAssociation(toRemove);
44+
field.AddAssociation(association);
4545

4646
var pairTo = context.PairedAssociations.Where(p => p.First==origin).FirstOrDefault();
4747
if (pairTo.First!=null)
@@ -86,9 +86,9 @@ public static void BuildReversedAssociation(BuildingContext context, Association
8686
.Where(a => a.TargetType == association.TargetType)
8787
.ToList();
8888
foreach (var toRemove in associationsToRemove)
89-
field.Associations.Remove(toRemove);
89+
field.RemoveAssociation(toRemove);
9090

91-
field.Associations.Add(association);
91+
field.AddAssociation(association);
9292
}
9393
}
9494

Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ private IndexInfo BuildFilterIndex(TypeInfo reflectedType, IndexInfo indexToFilt
551551
& (IndexAttributes.Primary | IndexAttributes.Secondary | IndexAttributes.Unique | IndexAttributes.Abstract)
552552
| IndexAttributes.Filtered | IndexAttributes.Virtual;
553553
var result = new IndexInfo(reflectedType, attributes, indexToFilter, Array.Empty<IndexInfo>()) {
554-
FilterByTypes = filterByTypes.ToList().AsReadOnly()
554+
FilterByTypes = filterByTypes.ToList().AsSafeWrapper()
555555
};
556556

557557
// Adding key columns
@@ -778,7 +778,7 @@ private IndexInfo BuildViewIndex(TypeInfo reflectedType, IndexInfo indexToApplyV
778778
}
779779

780780
result.ValueColumns.AddRange(valueColumns);
781-
result.SelectColumns = columnMap.AsReadOnly();
781+
result.SelectColumns = columnMap.AsSafeWrapper();
782782
result.Name = nameBuilder.BuildIndexName(reflectedType, result);
783783
result.Group = BuildColumnGroup(result);
784784

Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ private void PreprocessAssociations()
277277

278278
foreach (var association in associationsToRemove) {
279279
context.Model.Associations.Remove(association);
280-
refField.Associations.Remove(association);
280+
refField.RemoveAssociation(association);
281281
}
282282
foreach (var association in associationsToKeep) {
283283
var interfaceAssociationsToRemove = interfaceAssociations
@@ -287,10 +287,10 @@ private void PreprocessAssociations()
287287
foreach (var interfaceAssociation in interfaceAssociationsToRemove)
288288
interfaceAssociations.Remove(interfaceAssociation);
289289
}
290-
refField.Associations.AddRange(interfaceAssociations);
290+
refField.AddAssociations(interfaceAssociations);
291291
foreach (var association in inheritedAssociations) {
292-
if (!refField.Associations.Contains(association.Name))
293-
refField.Associations.Add(association);
292+
if (!refField.ContainsAssociation(association.Name))
293+
refField.AddAssociation(association);
294294
if (!context.Model.Associations.Contains(association))
295295
context.Model.Associations.Add(association);
296296
}

Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,21 @@ public TypeInfo BuildType(TypeDef typeDef)
3939
{
4040
using (BuildLog.InfoRegion(nameof(Strings.LogBuildingX), typeDef.UnderlyingType.GetShortName())) {
4141

42+
var validators = typeDef.Validators;
43+
if (typeDef.IsEntity && DeclaresOnValidate(typeDef.UnderlyingType)) {
44+
validators.Add(new EntityValidator());
45+
}
46+
4247
var typeInfo = new TypeInfo(context.Model, typeDef.Attributes) {
4348
UnderlyingType = typeDef.UnderlyingType,
4449
Name = typeDef.Name,
4550
MappingName = typeDef.MappingName,
4651
MappingDatabase = typeDef.MappingDatabase,
4752
MappingSchema = typeDef.MappingSchema,
4853
HasVersionRoots = typeDef.UnderlyingType.GetInterfaces().Any(type => type == typeof(IHasVersionRoots)),
49-
Validators = typeDef.Validators,
54+
Validators = validators,
5055
};
5156

52-
if (typeInfo.IsEntity && DeclaresOnValidate(typeInfo.UnderlyingType)) {
53-
typeInfo.Validators.Add(new EntityValidator());
54-
}
55-
5657
if (typeDef.StaticTypeId != null) {
5758
typeInfo.TypeId = typeDef.StaticTypeId.Value;
5859
}
@@ -232,6 +233,16 @@ private FieldInfo BuildDeclaredField(TypeInfo type, FieldDef fieldDef)
232233
{
233234
BuildLog.Info(nameof(Strings.LogBuildingDeclaredFieldXY), type.Name, fieldDef.Name);
234235

236+
var validators = fieldDef.Validators;
237+
238+
if (fieldDef.IsStructure && DeclaresOnValidate(fieldDef.ValueType)) {
239+
validators.Add(new StructureFieldValidator());
240+
}
241+
242+
if (fieldDef.IsEntitySet && DeclaresOnValidate(fieldDef.ValueType)) {
243+
validators.Add(new EntitySetFieldValidator());
244+
}
245+
235246
var fieldInfo = new FieldInfo(type, fieldDef.Attributes) {
236247
UnderlyingProperty = fieldDef.UnderlyingProperty,
237248
Name = fieldDef.Name,
@@ -242,17 +253,9 @@ private FieldInfo BuildDeclaredField(TypeInfo type, FieldDef fieldDef)
242253
Length = fieldDef.Length,
243254
Scale = fieldDef.Scale,
244255
Precision = fieldDef.Precision,
245-
Validators = fieldDef.Validators,
256+
Validators = validators,
246257
};
247258

248-
if (fieldInfo.IsStructure && DeclaresOnValidate(fieldInfo.ValueType)) {
249-
fieldInfo.Validators.Add(new StructureFieldValidator());
250-
}
251-
252-
if (fieldInfo.IsEntitySet && DeclaresOnValidate(fieldInfo.ValueType)) {
253-
fieldInfo.Validators.Add(new EntitySetFieldValidator());
254-
}
255-
256259
type.Fields.Add(fieldInfo);
257260

258261
if (fieldInfo.IsEntitySet) {

Orm/Xtensive.Orm/Orm/Building/BuildingContext.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ public sealed class BuildingContext
6262
/// <summary>
6363
/// Gets all available <see cref="IModule"/> implementations.
6464
/// </summary>
65-
public ICollection<IModule> Modules { get; private set; }
65+
public IReadOnlyList<IModule> Modules { get; }
6666

6767
/// <summary>
6868
/// Gets all available <see cref="IModule2"/> implementations.
6969
/// </summary>
70-
public ICollection<IModule2> Modules2 { get; private set; }
70+
public IReadOnlyList<IModule2> Modules2 { get; }
7171

7272
internal ModelDefBuilder ModelDefBuilder { get; set; }
7373

@@ -85,8 +85,8 @@ internal BuildingContext(DomainBuilderConfiguration builderConfiguration)
8585
ModelInspectionResult = new ModelInspectionResult();
8686
DependencyGraph = new Graph<TypeDef>();
8787

88-
Modules = BuilderConfiguration.Services.Modules.ToList().AsReadOnly();
89-
Modules2 = Modules.OfType<IModule2>().ToList().AsReadOnly();
88+
Modules = BuilderConfiguration.Services.Modules.AsSafeWrapper();
89+
Modules2 = Modules.OfType<IModule2>().ToList().AsSafeWrapper();
9090
Validator = new Validator(builderConfiguration.Services.ProviderInfo.SupportedTypes);
9191
DefaultSchemaInfo = builderConfiguration.DefaultSchemaInfo;
9292
}

Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public string PairTo
287287
/// <summary>
288288
/// Gets of <see cref="IPropertyValidator"/> instances associated with this field.
289289
/// </summary>
290-
public IList<IPropertyValidator> Validators { get; private set; }
290+
public List<IPropertyValidator> Validators { get; } = new();
291291

292292
internal bool IsDeclaredAsNullable
293293
{
@@ -325,7 +325,6 @@ internal FieldDef(Type valueType, Validator validator)
325325
if ((valueType.IsClass || valueType.IsInterface) && !IsStructure)
326326
attributes |= FieldAttributes.Nullable;
327327
ValueType = valueType;
328-
Validators = new List<IPropertyValidator>();
329328

330329
// Nullable<T>
331330
if (valueType.IsNullable()) {

Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public NodeCollection<TypeDef> Implementors
119119
/// <summary>
120120
/// Gets <see cref="IObjectValidator"/> instances associated with this type.
121121
/// </summary>
122-
public IList<IObjectValidator> Validators { get; private set; }
122+
public List<IObjectValidator> Validators { get; } = new();
123123

124124
/// <summary>
125125
/// Gets or sets the type discriminator value.
@@ -225,8 +225,6 @@ internal TypeDef(ModelDefBuilder builder, Type type, Validator validator)
225225
implementors = IsInterface
226226
? new NodeCollection<TypeDef>(this, "Implementors")
227227
: NodeCollection<TypeDef>.Empty;
228-
229-
Validators = new List<IObjectValidator>();
230228
}
231229
}
232230
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/IHasNestedNodes.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ namespace Xtensive.Orm.Internals.Prefetch
1111
{
1212
internal interface IHasNestedNodes
1313
{
14-
ReadOnlyCollection<BaseFieldNode> NestedNodes { get; }
14+
IReadOnlyList<BaseFieldNode> NestedNodes { get; }
1515

1616
IReadOnlyCollection<Key> ExtractKeys(object target);
1717

18-
IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection<BaseFieldNode> nestedNodes);
18+
IHasNestedNodes ReplaceNestedNodes(IReadOnlyList<BaseFieldNode> nestedNodes);
1919
}
2020
}

0 commit comments

Comments
 (0)