Skip to content

Commit dc6346f

Browse files
authored
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 5036833 commit dc6346f

File tree

6 files changed

+123
-25
lines changed

6 files changed

+123
-25
lines changed

Directory.Build.props

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,6 @@
108108

109109
<ItemGroup>
110110
<SourceRoot Include="$(SolutionDir)"/>
111-
<PackageReference Condition="$(DoGeneratePackage) == 'true'"
112-
Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
113111
<None Include="$(DoPackageIcon)" Visible="false" >
114112
<PackagePath>.</PackagePath>
115113
<Pack>true</Pack>

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
using System.Collections.ObjectModel;
1313
using System.Diagnostics;
1414
using System.Linq;
15+
using System.Reflection;
1516
using System.Threading;
1617
using JetBrains.Annotations;
1718
using Xtensive.Core;
1819
using Xtensive.Orm.Internals;
1920
using Xtensive.Orm.Validation;
21+
using Xtensive.Orm.Upgrade;
22+
using Xtensive.Reflection;
2023
using Xtensive.Tuples;
2124
using Xtensive.Tuples.Transform;
2225
using Tuple = Xtensive.Tuples.Tuple;
@@ -42,6 +45,10 @@ public sealed class TypeInfo : SchemaMappedNode
4245
/// </summary>
4346
public const int MinTypeId = 100;
4447

48+
private static readonly Type
49+
TypeEntity = typeof(Entity)
50+
, TypeStructure = typeof(Structure);
51+
4552
private static volatile int CurrentSharedId = 0;
4653
private static readonly ConcurrentDictionary<Type, int> TypeToSharedId = new();
4754

@@ -421,6 +428,9 @@ public FieldInfoCollection Fields
421428
get { return fields; }
422429
}
423430

431+
private FieldInfo[] persistentFields;
432+
internal FieldInfo[] PersistentFields => persistentFields ??= BuildPersistentFields();
433+
424434
/// <summary>
425435
/// Gets the field map for implemented interfaces.
426436
/// </summary>
@@ -913,6 +923,67 @@ private IDictionary<Pair<FieldInfo>, FieldInfo> BuildStructureFieldMapping()
913923
return new ReadOnlyDictionary<Pair<FieldInfo>, FieldInfo>(result);
914924
}
915925

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

918989
/// <inheritdoc/>

Orm/Xtensive.Orm/Orm/Persistent.cs

Lines changed: 14 additions & 6 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,10 +176,11 @@ 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
{
178-
if (field.ReflectedType.IsInterface)
179-
field = TypeInfo.FieldMap[field];
180184
var fieldAccessor = GetNormalizedFieldAccessor<T>(field);
181185
T result = default(T);
182186
try {
@@ -358,6 +362,9 @@ protected internal void SetFieldValue(string fieldName, object value)
358362
SetFieldValue(TypeInfo.Fields[fieldName], value);
359363
}
360364

365+
protected internal void SetFieldValue<T>(int fieldIndex, T value) =>
366+
SetNormalizedFieldValue(TypeInfo.PersistentFields[fieldIndex], value, null, null);
367+
361368
/// <summary>
362369
/// Sets the field value.
363370
/// Field value type must be specified precisely.
@@ -381,10 +388,11 @@ protected internal void SetFieldValue(FieldInfo field, object value)
381388
SetFieldValue(field, value, null, null);
382389
}
383390

384-
internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext)
391+
internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) =>
392+
SetNormalizedFieldValue(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field, value, syncContext, removalContext);
393+
394+
internal void SetNormalizedFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext)
385395
{
386-
if (field.ReflectedType.IsInterface)
387-
field = TypeInfo.FieldMap[field];
388396
SystemSetValueAttempt(field, value);
389397
var fieldAccessor = GetNormalizedFieldAccessor(field);
390398
object oldValue = GetNormalizedFieldValue(field, fieldAccessor);

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: 6 additions & 6 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.Tail);
4848
il.Emit(OpCodes.Call, accessor);
@@ -57,7 +57,7 @@ private void ImplementGetter(ProcessorContext context)
5757
body.Instructions.Clear();
5858
var il = body.GetILProcessor();
5959
il.Emit(OpCodes.Ldarg_0);
60-
il.Emit(OpCodes.Ldstr, persistentName);
60+
il.Emit(OpCodes.Ldc_I4, persistentIndex);
6161
il.Emit(OpCodes.Tail);
6262
il.Emit(OpCodes.Call, accessor);
6363
il.Emit(OpCodes.Ret);
@@ -77,14 +77,14 @@ private MethodReference GetAccessor(ProcessorContext context,
7777
return result;
7878
}
7979

80-
public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, string persistentName)
80+
public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, int persistentIndex)
8181
{
8282
ArgumentNullException.ThrowIfNull(type);
8383
ArgumentNullException.ThrowIfNull(property);
8484
this.kind = kind;
8585
this.type = type;
8686
this.property = property;
87-
this.persistentName = persistentName;
87+
this.persistentIndex = persistentIndex;
8888
}
8989
}
90-
}
90+
}

0 commit comments

Comments
 (0)