Skip to content

Commit 5c81f1f

Browse files
committed
ARCH-6354: Optimize Get/SetFieldValue() to use integer indexes instead of search in Dictionary by string field name (#158)
* ARCH-6354: Optimize Get/SetFieldValue() to use integer indexes instead of search by string key * Correct for IssueJira0538 test case * Refactor * Refactor * non-Public fields too * add comment * rename * Fix issue witn `new` property specifier * Fix Flaky test * Fix BuildPersistentFields() * Skip overridden persistent properties * Correctly process overriddent properties * DOn't ignore abstract props * Add EOL * Upgrade to Ubuntu 22.04 * Fix assigning prop index * Fix processing virtual fields * Minor fix * process IsStructure bases * Fix a few cases * Fix QueryCompositeTest case * Process Recycled fields * Optimize Field mapping * Refactor
1 parent ccbcd4a commit 5c81f1f

File tree

5 files changed

+123
-21
lines changed

5 files changed

+123
-21
lines changed

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

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
using System.Collections.ObjectModel;
1212
using System.Diagnostics;
1313
using System.Linq;
14+
using System.Reflection;
1415
using JetBrains.Annotations;
1516
using Xtensive.Core;
1617
using Xtensive.Orm.Internals;
1718
using Xtensive.Orm.Validation;
19+
using Xtensive.Orm.Upgrade;
20+
using Xtensive.Reflection;
1821
using Xtensive.Tuples;
1922
using Xtensive.Tuples.Transform;
2023
using Tuple = Xtensive.Tuples.Tuple;
@@ -40,6 +43,10 @@ public sealed class TypeInfo : SchemaMappedNode
4043
/// </summary>
4144
public const int MinTypeId = 100;
4245

46+
private static readonly Type
47+
TypeEntity = typeof(Entity),
48+
TypeStructure = typeof(Structure);
49+
4350
private static readonly ImmutableHashSet<TypeInfo> EmptyTypes = ImmutableHashSet.Create<TypeInfo>();
4451

4552
private readonly ColumnInfoCollection columns;
@@ -67,7 +74,7 @@ public sealed class TypeInfo : SchemaMappedNode
6774
private IDictionary<Pair<FieldInfo>, FieldInfo> structureFieldMapping;
6875
private List<AssociationInfo> overridenAssociations;
6976
private FieldInfo typeIdField;
70-
77+
7178

7279
private TypeInfo ancestor;
7380
private IReadOnlySet<TypeInfo> ancestors;
@@ -422,6 +429,9 @@ public FieldInfoCollection Fields
422429
get { return fields; }
423430
}
424431

432+
private FieldInfo[] persistentFields;
433+
internal FieldInfo[] PersistentFields => persistentFields ??= BuildPersistentFields();
434+
425435
/// <summary>
426436
/// Gets the field map for implemented interfaces.
427437
/// </summary>
@@ -915,6 +925,67 @@ private IDictionary<Pair<FieldInfo>, FieldInfo> BuildStructureFieldMapping()
915925
return new ReadOnlyDictionary<Pair<FieldInfo>, FieldInfo>(result);
916926
}
917927

928+
private static IEnumerable<FieldInfo> GetBaseFields(Type type, IEnumerable<FieldInfo> fields, bool bRoot)
929+
{
930+
if (type == TypeEntity || type == TypeStructure) {
931+
return Array.Empty<FieldInfo>();
932+
}
933+
var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
934+
.ToDictionary(p => p.Name, p => p.MetadataToken);
935+
936+
return GetBaseFields(type.BaseType, fields, bRoot)
937+
.Concat(
938+
fields.Select(p => (p, declared.TryGetValue(p.UnderlyingProperty.Name, out var token) ? token : 0))
939+
.Where(t => bRoot ? t.Item2 != 0 : t.Item1.UnderlyingProperty.MetadataToken == t.Item2)
940+
.OrderBy(t => t.Item2)
941+
.Select(t => t.Item1)
942+
);
943+
}
944+
945+
private static bool IsOverrideOfVirtual(FieldInfo a, FieldInfo p) =>
946+
a.Name == p.Name
947+
&& a.Name != "TypeId"
948+
&& p.IsInherited
949+
&& a.UnderlyingProperty.GetMethod?.IsVirtual == true
950+
&& p.UnderlyingProperty.GetMethod?.IsVirtual == true;
951+
952+
private FieldInfo[] BuildPersistentFields()
953+
{
954+
var propTypeId = IsEntity ? Fields[nameof(Entity.TypeId)] : null;
955+
bool isRoot = Hierarchy?.Root == this;
956+
var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray();
957+
var recycled = UnderlyingType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
958+
.Where(p => p.GetAttribute<RecycledAttribute>() != null && !potentialFields.Any(pf => pf.UnderlyingProperty == p));
959+
960+
FieldInfo[] baseFields;
961+
FieldInfo[] ancestorFields = Array.Empty<FieldInfo>();
962+
if (Ancestor != null && Ancestor.UnderlyingType != TypeStructure) {
963+
ancestorFields = Ancestor.PersistentFields;
964+
baseFields = ancestorFields.Select(p => p != null && Fields.TryGetValue(p.Name, out var f) ? f : null).ToArray();
965+
}
966+
else {
967+
baseFields = !(IsEntity || IsStructure)
968+
? Array.Empty<FieldInfo>()
969+
: GetBaseFields(UnderlyingType.BaseType, potentialFields, isRoot).ToArray();
970+
}
971+
var baseFieldsSet = baseFields.ToHashSet();
972+
var props = baseFields.Concat(
973+
potentialFields.Where(p => p.UnderlyingProperty.DeclaringType == UnderlyingType
974+
&& (isRoot
975+
|| p.IsExplicit
976+
|| !baseFieldsSet.Contains(p)
977+
|| ancestorFields.Any(a => IsOverrideOfVirtual(a, p))))
978+
.Select(p => (p, p.UnderlyingProperty.MetadataToken))
979+
.Concat(recycled.Select(p => ((FieldInfo)null, p.MetadataToken)))
980+
.OrderBy(t => t.Item2)
981+
.Select(t => t.Item1)
982+
).Select(p => p?.ReflectedType.IsInterface == true ? FieldMap[p] : p);
983+
if (IsEntity && Ancestor == null) {
984+
props = props.Prepend(propTypeId);
985+
}
986+
return props.ToArray();
987+
}
988+
918989
#endregion
919990

920991
/// <inheritdoc/>

Orm/Xtensive.Orm/Orm/Persistent.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ protected internal object GetFieldValue(string fieldName)
165165
return GetFieldValue(TypeInfo.Fields[fieldName]);
166166
}
167167

168+
protected internal T GetFieldValue<T>(int fieldIndex) =>
169+
GetNormalizedFieldValue<T>(TypeInfo.PersistentFields[fieldIndex]);
170+
168171
/// <summary>
169172
/// Gets the field value.
170173
/// Field value type must be specified precisely.
@@ -173,7 +176,10 @@ protected internal object GetFieldValue(string fieldName)
173176
/// <typeparam name="T">Field value type.</typeparam>
174177
/// <param name="field">The field.</param>
175178
/// <returns>Field value.</returns>
176-
protected internal T GetFieldValue<T>(FieldInfo field)
179+
protected internal T GetFieldValue<T>(FieldInfo field) =>
180+
GetNormalizedFieldValue<T>(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field);
181+
182+
protected internal T GetNormalizedFieldValue<T>(FieldInfo field)
177183
{
178184
if (field.ReflectedType.IsInterface)
179185
field = TypeInfo.FieldMap[field];
@@ -356,6 +362,9 @@ protected internal void SetFieldValue(string fieldName, object value)
356362
SetFieldValue(TypeInfo.Fields[fieldName], value);
357363
}
358364

365+
protected internal void SetFieldValue<T>(int fieldIndex, T value) =>
366+
SetNormalizedFieldValue(TypeInfo.PersistentFields[fieldIndex], value, null, null);
367+
359368
/// <summary>
360369
/// Sets the field value.
361370
/// Field value type must be specified precisely.
@@ -383,10 +392,11 @@ protected internal void SetFieldValue(FieldInfo field, object value)
383392
SetFieldValue(field, value, null, null);
384393
}
385394

386-
internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext)
395+
internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) =>
396+
SetNormalizedFieldValue(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field, value, syncContext, removalContext);
397+
398+
internal void SetNormalizedFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext)
387399
{
388-
if (field.ReflectedType.IsInterface)
389-
field = TypeInfo.FieldMap[field];
390400
SystemSetValueAttempt(field, value);
391401
var fieldAccessor = GetFieldAccessor(field);
392402
object oldValue = GetFieldValue(field);

Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public override ActionResult Execute(ProcessorContext context)
2525
}
2626
registry.OrmAssembly = ormAssembly;
2727

28+
var intType = context.TargetModule.TypeSystem.Int32;
2829
var stringType = context.TargetModule.TypeSystem.String;
2930
var voidType = context.TargetModule.TypeSystem.Void;
3031

@@ -59,13 +60,13 @@ public override ActionResult Execute(ProcessorContext context)
5960
var persistentGetter = new MethodReference("GetFieldValue", voidType, persistentType) {HasThis = true};
6061
var getterType = new GenericParameter("!!T", persistentGetter);
6162
persistentGetter.ReturnType = getterType;
62-
persistentGetter.Parameters.Add(new ParameterDefinition(stringType));
63+
persistentGetter.Parameters.Add(new ParameterDefinition(intType));
6364
persistentGetter.GenericParameters.Add(getterType);
6465
registry.PersistentGetterDefinition = context.TargetModule.ImportReference(persistentGetter);
6566

6667
var persistentSetter = new MethodReference("SetFieldValue", voidType, persistentType) {HasThis = true};
6768
var setterType = new GenericParameter("!!T", persistentSetter);
68-
persistentSetter.Parameters.Add(new ParameterDefinition(stringType));
69+
persistentSetter.Parameters.Add(new ParameterDefinition(intType));
6970
persistentSetter.Parameters.Add(new ParameterDefinition(setterType));
7071
persistentSetter.GenericParameters.Add(setterType);
7172
registry.PersistentSetterDefinition = context.TargetModule.ImportReference(persistentSetter);

Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// Created by: Denis Krjuchkov
55
// Created: 2013.08.21
66

7+
using System;
8+
using System.Collections.Generic;
79
using System.Linq;
810
using Mono.Cecil;
911
using Xtensive.Orm.Weaver.Tasks;
@@ -38,8 +40,8 @@ public override ActionResult Execute(ProcessorContext context)
3840
new[] {references.Entity, references.FieldInfo},
3941
};
4042

41-
propertyChecker = (context.Language==SourceLanguage.CSharp)
42-
? (IPersistentPropertyChecker) new CsPropertyChecker()
43+
propertyChecker = (context.Language==SourceLanguage.CSharp)
44+
? (IPersistentPropertyChecker) new CsPropertyChecker()
4345
: new VbPropertyChecker();
4446

4547
foreach (var type in context.PersistentTypes)
@@ -107,26 +109,44 @@ private void ProcessStructure(ProcessorContext context, TypeInfo type)
107109
context.WeavingTasks.Add(new AddAttributeTask(definition, context.References.StructureTypeAttributeConstructor));
108110
}
109111

112+
private Dictionary<PropertyInfo, int> GetPropertyToIndexMap(TypeInfo type)
113+
{
114+
if (type is null) {
115+
return new();
116+
}
117+
var r = GetPropertyToIndexMap(type.BaseType);
118+
int idx = r.Count == 0 ? 0 : r.Values.Max() + 1;
119+
if (idx == 0 && type.Kind == PersistentTypeKind.Entity) {
120+
idx = 1; // for TypeId
121+
}
122+
foreach (var p in type.Properties.Values.Where(p => p.IsPersistent)
123+
.OrderBy(p => p.Definition.MetadataToken.ToInt32())) {
124+
r[p] = (p.IsOverride && p.BaseProperty.IsPersistent)
125+
? r[p.BaseProperty] // For overridden persistent property assign base property's index
126+
: idx++;
127+
}
128+
return r;
129+
}
130+
110131
private void ProcessFields(ProcessorContext context, TypeInfo type)
111132
{
112-
foreach (var property in type.Properties.Values.Where(p => p.IsPersistent)) {
113-
if (!propertyChecker.ShouldProcess(property, context))
114-
continue;
133+
var typeDefinition = type.Definition;
134+
var propertyToIndex = GetPropertyToIndexMap(type);
115135

116-
var typeDefinition = type.Definition;
136+
foreach (var property in type.Properties.Values.Where(p => p.IsPersistent && propertyChecker.ShouldProcess(p, context))) {
137+
var persistentIndex = propertyToIndex[property];
117138
var propertyDefinition = property.Definition;
118-
var persistentName = property.PersistentName ?? property.Name;
119139
// Backing field
120140
context.WeavingTasks.Add(new RemoveBackingFieldTask(typeDefinition, propertyDefinition));
121141
// Getter
122142
context.WeavingTasks.Add(new ImplementFieldAccessorTask(AccessorKind.Getter,
123-
typeDefinition, propertyDefinition, persistentName));
143+
typeDefinition, propertyDefinition, persistentIndex));
124144
// Setter
125145
if (property.IsKey)
126146
context.WeavingTasks.Add(new ImplementKeySetterTask(typeDefinition, propertyDefinition));
127147
else
128148
context.WeavingTasks.Add(new ImplementFieldAccessorTask(AccessorKind.Setter,
129-
typeDefinition, propertyDefinition, persistentName));
149+
typeDefinition, propertyDefinition, persistentIndex));
130150
if (property.PersistentName!=null)
131151
context.WeavingTasks.Add(new AddAttributeTask(propertyDefinition,
132152
context.References.OverrideFieldNameAttributeConstructor, property.PersistentName));

Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal sealed class ImplementFieldAccessorTask : WeavingTask
1515
{
1616
private readonly TypeDefinition type;
1717
private readonly PropertyDefinition property;
18-
private readonly string persistentName;
18+
private readonly int persistentIndex;
1919
private readonly AccessorKind kind;
2020

2121
public override ActionResult Execute(ProcessorContext context)
@@ -42,7 +42,7 @@ private void ImplementSetter(ProcessorContext context)
4242
body.Instructions.Clear();
4343
var il = body.GetILProcessor();
4444
il.Emit(OpCodes.Ldarg_0);
45-
il.Emit(OpCodes.Ldstr, persistentName);
45+
il.Emit(OpCodes.Ldc_I4, persistentIndex);
4646
il.Emit(OpCodes.Ldarg_1);
4747
il.Emit(OpCodes.Call, accessor);
4848
il.Emit(OpCodes.Ret);
@@ -56,7 +56,7 @@ private void ImplementGetter(ProcessorContext context)
5656
body.Instructions.Clear();
5757
var il = body.GetILProcessor();
5858
il.Emit(OpCodes.Ldarg_0);
59-
il.Emit(OpCodes.Ldstr, persistentName);
59+
il.Emit(OpCodes.Ldc_I4, persistentIndex);
6060
il.Emit(OpCodes.Call, accessor);
6161
il.Emit(OpCodes.Ret);
6262
}
@@ -75,7 +75,7 @@ private MethodReference GetAccessor(ProcessorContext context,
7575
return result;
7676
}
7777

78-
public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, string persistentName)
78+
public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, int persistentIndex)
7979
{
8080
if (type==null)
8181
throw new ArgumentNullException("type");
@@ -85,7 +85,7 @@ public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, Proper
8585
this.kind = kind;
8686
this.type = type;
8787
this.property = property;
88-
this.persistentName = persistentName;
88+
this.persistentIndex = persistentIndex;
8989
}
9090
}
9191
}

0 commit comments

Comments
 (0)