From 53483b3689f05077cd3f88636bf18da68f0f750d Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 11:05:32 -0800 Subject: [PATCH 01/14] Optimize `TypeRegistry` --- Orm/Xtensive.Orm/Collections/TypeRegistry.cs | 22 +++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Orm/Xtensive.Orm/Collections/TypeRegistry.cs b/Orm/Xtensive.Orm/Collections/TypeRegistry.cs index b5233f73e..9f59f5126 100644 --- a/Orm/Xtensive.Orm/Collections/TypeRegistry.cs +++ b/Orm/Xtensive.Orm/Collections/TypeRegistry.cs @@ -4,13 +4,10 @@ // Created by: Dmitri Maximov // Created: 2007.08.03 -using System; using System.Collections; using System.Collections.Frozen; -using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using System.Linq; using Xtensive.Core; using Xtensive.IoC; @@ -24,13 +21,11 @@ public class TypeRegistry : LockableBase, IEnumerable, ICloneable { - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - private readonly List types = new List(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ISet typeSet = new HashSet(); - private readonly List actions = new List(); + private List actions = new(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly HashSet actionSet = new HashSet(); + private readonly HashSet actionSet = new(); private readonly ITypeRegistrationProcessor processor; private bool isProcessingPendingActions = false; protected ServiceRegistration[] serviceRegistrations; @@ -63,7 +58,6 @@ public void Register(Type type) Register(new TypeRegistration(type)); else if (typeSet.Add(type)) { serviceRegistrations = null; - types.Add(type); ((ISet)Assemblies).Add(type.Assembly); } } @@ -110,9 +104,8 @@ public void Register(Assembly assembly, string @namespace) public bool Register(in TypeRegistration action) { EnsureNotLocked(); - if (actionSet.Contains(action)) + if (!actionSet.Add(action)) return false; - actionSet.Add(action); actions.Add(action); return true; } @@ -126,8 +119,8 @@ private void ProcessPendingActions() isProcessingPendingActions = true; try { while (true) { - var oldActions = actions.ToList(); - actions.Clear(); + var oldActions = actions; + actions = new(); foreach (var action in oldActions) processor.Process(this, action); if (actions.Count == 0) @@ -168,7 +161,7 @@ public virtual object Clone() public IEnumerator GetEnumerator() { ProcessPendingActions(); - return types.GetEnumerator(); + return typeSet.GetEnumerator(); } /// @@ -186,7 +179,7 @@ public int Count { get { ProcessPendingActions(); - return types.Count; + return typeSet.Count; } } @@ -211,7 +204,6 @@ protected TypeRegistry(TypeRegistry source) ArgumentValidator.EnsureArgumentIs(source, GetType(), "source"); actions = new List(source.actions); actionSet = new HashSet(source.actionSet); - types = new List(source.types); typeSet = new HashSet(source.typeSet); processor = source.processor; Assemblies = new HashSet(source.Assemblies); From c032bcee9bc3aa24174f78aa96f635ab685fc19d Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 11:28:58 -0800 Subject: [PATCH 02/14] Optimize FindTypes --- Orm/Xtensive.Orm/Collections/TypeRegistrationProcessorBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Collections/TypeRegistrationProcessorBase.cs b/Orm/Xtensive.Orm/Collections/TypeRegistrationProcessorBase.cs index 5511e482b..88d37511a 100644 --- a/Orm/Xtensive.Orm/Collections/TypeRegistrationProcessorBase.cs +++ b/Orm/Xtensive.Orm/Collections/TypeRegistrationProcessorBase.cs @@ -65,7 +65,7 @@ protected virtual bool IsAcceptable(in TypeRegistration registration, Type type) return type.IsSubclassOf(BaseType) && (ns.IsNullOrEmpty() || (type.FullName.IndexOf(ns + ".", StringComparison.InvariantCulture) >= 0)); } - private static IList FindTypes(Assembly assembly, Type baseType, TypeFilter filter) + private static IReadOnlyList FindTypes(Assembly assembly, Type baseType, TypeFilter filter) { ArgumentNullException.ThrowIfNull(assembly); ArgumentNullException.ThrowIfNull(baseType); From 2730d439de27258bc5bb3ddcf9358f3c9852465b Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 11:47:08 -0800 Subject: [PATCH 03/14] Remove ConvertingEnumerator --- .../Conversion/ConvertingEnumerable.cs | 60 ------------- .../Internals/ConvertingEnumerator.cs | 84 ------------------- 2 files changed, 144 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Conversion/ConvertingEnumerable.cs delete mode 100644 Orm/Xtensive.Orm/Conversion/Internals/ConvertingEnumerator.cs diff --git a/Orm/Xtensive.Orm/Conversion/ConvertingEnumerable.cs b/Orm/Xtensive.Orm/Conversion/ConvertingEnumerable.cs deleted file mode 100644 index 327153132..000000000 --- a/Orm/Xtensive.Orm/Conversion/ConvertingEnumerable.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alex Ilyin -// Created: 2007.06.04 - -using System; -using System.Collections; -using System.Collections.Generic; -using Xtensive.Core; - - -namespace Xtensive.Conversion -{ - /// - /// An implementor performing - /// conversion from to - /// on the fly. - /// - /// The item type to convert from. - /// The item type to convert to. - public class ConvertingEnumerable : IEnumerable - { - IEnumerable innerEnumerable; - Converter converter; - - /// - public IEnumerator GetEnumerator() - { - return new ConvertingEnumerator( - innerEnumerable.GetEnumerator(), - converter); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return new ConvertingEnumerator( - innerEnumerable.GetEnumerator(), - converter); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// Enumerable to wrap. - /// Item converter to use. - public ConvertingEnumerable(IEnumerable innerEnumerable, Converter converter) - { - ArgumentNullException.ThrowIfNull(innerEnumerable); - ArgumentNullException.ThrowIfNull(converter); - this.innerEnumerable = innerEnumerable; - this.converter = converter; - } - } - -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Conversion/Internals/ConvertingEnumerator.cs b/Orm/Xtensive.Orm/Conversion/Internals/ConvertingEnumerator.cs deleted file mode 100644 index a47c8dbc6..000000000 --- a/Orm/Xtensive.Orm/Conversion/Internals/ConvertingEnumerator.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alex Ilyin -// Created: 2007.06.04 - -using System; -using System.Collections.Generic; -using Xtensive.Core; - - -namespace Xtensive.Conversion -{ - [Serializable] - internal sealed class ConvertingEnumerator : IEnumerator - { - private IEnumerator innerEnumerator; - private readonly Converter converter; - private T2 current; - private bool currentIsValid = false; - - internal T1 InnerCurrent - { - get { return innerEnumerator.Current; } - } - - object System.Collections.IEnumerator.Current - { - get { return Current; } - } - - public T2 Current - { - get { - if (!currentIsValid) { - current = converter(innerEnumerator.Current); - currentIsValid = true; - } - return current; - } - } - - public bool MoveNext() - { - currentIsValid = false; - return innerEnumerator.MoveNext(); - } - - public void Reset() - { - currentIsValid = false; - innerEnumerator.Reset(); - } - - - // Constructors - - /// - /// Initializes new instance of this type. - /// - /// The inner enumerator. - /// The converter. - public ConvertingEnumerator(IEnumerator innerEnumerator, Converter converter) - { - ArgumentNullException.ThrowIfNull(innerEnumerator); - ArgumentNullException.ThrowIfNull(converter); - this.innerEnumerator = innerEnumerator; - this.converter = converter; - } - - - // Destructor - - /// - /// Releases resources associated with this instance. - /// - public void Dispose() - { - currentIsValid = false; - innerEnumerator.Dispose(); - innerEnumerator = null; - } - } -} \ No newline at end of file From eb091d76ac4e11b2f573e891ddd6ba702dd94016 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 11:55:27 -0800 Subject: [PATCH 04/14] Optimize NotifyConnectionOpening --- Orm/Xtensive.Orm/Sql/SqlHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Sql/SqlHelper.cs b/Orm/Xtensive.Orm/Sql/SqlHelper.cs index f25670f2c..9d4019fdb 100644 --- a/Orm/Xtensive.Orm/Sql/SqlHelper.cs +++ b/Orm/Xtensive.Orm/Sql/SqlHelper.cs @@ -564,8 +564,9 @@ public static NotSupportedException NotSupported(ServerFeatures feature) public static void NotifyConnectionOpening( IEnumerable connectionAccessors, DbConnection connection, bool reconnect = false) { + ConnectionEventData eventData = null; foreach (var accessor in connectionAccessors) { - accessor.ConnectionOpening(new ConnectionEventData(connection, reconnect)); + accessor.ConnectionOpening(eventData ??= new ConnectionEventData(connection, reconnect)); } } From be0b904fb15bd1c461eab33e85cac39f62f4e13e Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 11:56:12 -0800 Subject: [PATCH 05/14] Optimize NotifyConnectionOpening --- Orm/Xtensive.Orm/Sql/SqlHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Sql/SqlHelper.cs b/Orm/Xtensive.Orm/Sql/SqlHelper.cs index 9d4019fdb..c24da3488 100644 --- a/Orm/Xtensive.Orm/Sql/SqlHelper.cs +++ b/Orm/Xtensive.Orm/Sql/SqlHelper.cs @@ -583,9 +583,10 @@ public static void NotifyConnectionOpening( public static async Task NotifyConnectionOpeningAsync( IEnumerable connectionAccessors, DbConnection connection, bool reconnect = false, CancellationToken token = default) { + ConnectionEventData eventData = null; foreach (var accessor in connectionAccessors) { await accessor.ConnectionOpeningAsync( - new ConnectionEventData(connection, reconnect), token) + eventData ??= new ConnectionEventData(connection, reconnect), token) .ConfigureAwaitFalse(); } } From 983ec61f36338050183627b756d4c737f2c35e5c Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 12:44:04 -0800 Subject: [PATCH 06/14] Revert part of TypeRegistry --- Orm/Xtensive.Orm/Collections/TypeRegistry.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Collections/TypeRegistry.cs b/Orm/Xtensive.Orm/Collections/TypeRegistry.cs index 9f59f5126..87a1b11dc 100644 --- a/Orm/Xtensive.Orm/Collections/TypeRegistry.cs +++ b/Orm/Xtensive.Orm/Collections/TypeRegistry.cs @@ -21,11 +21,13 @@ public class TypeRegistry : LockableBase, IEnumerable, ICloneable { + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + private readonly List types = new List(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ISet typeSet = new HashSet(); - private List actions = new(); + private List actions = new List(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly HashSet actionSet = new(); + private readonly HashSet actionSet = new HashSet(); private readonly ITypeRegistrationProcessor processor; private bool isProcessingPendingActions = false; protected ServiceRegistration[] serviceRegistrations; @@ -58,6 +60,7 @@ public void Register(Type type) Register(new TypeRegistration(type)); else if (typeSet.Add(type)) { serviceRegistrations = null; + types.Add(type); ((ISet)Assemblies).Add(type.Assembly); } } @@ -161,7 +164,7 @@ public virtual object Clone() public IEnumerator GetEnumerator() { ProcessPendingActions(); - return typeSet.GetEnumerator(); + return types.GetEnumerator(); } /// @@ -179,7 +182,7 @@ public int Count { get { ProcessPendingActions(); - return typeSet.Count; + return types.Count; } } @@ -204,6 +207,7 @@ protected TypeRegistry(TypeRegistry source) ArgumentValidator.EnsureArgumentIs(source, GetType(), "source"); actions = new List(source.actions); actionSet = new HashSet(source.actionSet); + types = new List(source.types); typeSet = new HashSet(source.typeSet); processor = source.processor; Assemblies = new HashSet(source.Assemblies); From 1969a25123d734148ef64ecaf13f5f1f97754da7 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 12:50:00 -0800 Subject: [PATCH 07/14] Optimize `StructureFieldExpression` --- .../Orm/Linq/Expressions/StructureFieldExpression.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs index c5f0175cb..46b98273d 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs @@ -37,10 +37,11 @@ public override StructureFieldExpression Remap(ColNum offset, Dictionary((ColNum) (Mapping.Offset + offset), Mapping.Length); var result = new StructureFieldExpression(PersistentType, Field, newMapping, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); - var processedFields = new List(fields.Count); + var processedFields = new PersistentFieldExpression[fields.Count]; + int i = 0; foreach (var field in fields) { // Do not convert to LINQ. We want to avoid a closure creation here. - processedFields.Add(field.Remap(offset, processedExpressions)); + processedFields[i++] = field.Remap(offset, processedExpressions); } if (Owner == null) { From 601377e3cb7f629c577e23a7c688632a056d29fa Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 12:55:19 -0800 Subject: [PATCH 08/14] Optimize `StructureExpression` --- .../Linq/Expressions/StructureExpression.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs index 8b28a6f50..16b7fa443 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs @@ -26,7 +26,7 @@ internal sealed class StructureExpression : ParameterizedExpression, IPersistent public IReadOnlyList Fields => fields; - private void SetFields(List value) + private void SetFields(IReadOnlyList value) { fields = value; foreach (var fieldExpression in fields.OfType()) { @@ -42,10 +42,11 @@ public override Expression Remap(ColNum offset, Dictionary((ColNum) (Mapping.Offset + offset), Mapping.Length); var result = new StructureExpression(PersistentType, mapping); processedExpressions.Add(this, result); - var processedFields = new List(fields.Count); + var processedFields = new PersistentFieldExpression[fields.Count]; + int i = 0; foreach (var field in fields) { // Do not convert to LINQ. We intentionally avoiding closure creation here - processedFields.Add(field.Remap(offset, processedExpressions)); + processedFields[i++] = field.Remap(offset, processedExpressions); } result.SetFields(processedFields); @@ -96,10 +97,11 @@ public override ParameterizedExpression BindParameter(ParameterExpression parame var result = new StructureExpression(PersistentType, Mapping); processedExpressions.Add(this, result); - var processedFields = new List(fields.Count); + var processedFields = new PersistentFieldExpression[fields.Count]; + int i = 0; foreach (var field in fields) { // Do not convert to LINQ. We intentionally avoiding closure creation here - processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions)); + processedFields[i++] = (PersistentFieldExpression) field.BindParameter(parameter, processedExpressions); } result.SetFields(processedFields); @@ -114,10 +116,11 @@ public override Expression RemoveOuterParameter(Dictionary(fields.Count); + var processedFields = new PersistentFieldExpression[fields.Count]; + int i = 0; foreach (var field in fields) { // Do not convert to LINQ. We intentionally avoiding closure creation here - processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions)); + processedFields[i++] = (PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions); } result.SetFields(processedFields); @@ -131,12 +134,13 @@ public static StructureExpression CreateLocalCollectionStructure(TypeInfo typeIn } var sourceFields = typeInfo.Fields; - var destinationFields = new List(sourceFields.Count); + var destinationFields = new PersistentFieldExpression[sourceFields.Count]; var result = new StructureExpression(typeInfo, mapping); result.SetFields(destinationFields); + int i = 0; foreach (var field in sourceFields) { // Do not convert to LINQ. We intentionally avoiding closure creation here - destinationFields.Add(BuildNestedFieldExpression(field, mapping.Offset)); + destinationFields[i] = BuildNestedFieldExpression(field, mapping.Offset); } return result; From d5129295c9db31acc123ee0c423aa60b717352f8 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 13:32:01 -0800 Subject: [PATCH 09/14] Optimize IndexInfo --- .../Orm/Building/Builders/IndexBuilder.ClassTable.cs | 2 +- Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.ClassTable.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.ClassTable.cs index f31cdc578..e9dccce41 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.ClassTable.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.ClassTable.cs @@ -97,7 +97,7 @@ private void BuildClassTableIndexes(TypeInfo type) if (untypedIndexes.Contains(primaryIndex) && primaryIndex.ReflectedType == root) { primaryIndex = type.Indexes.Single(i => i.DeclaringIndex == primaryIndex.DeclaringIndex && i.IsTyped); } - var filterByTypes = type.AllDescendants.Append(type).ToList(); + var filterByTypes = type.AllDescendants.Append(type).ToArray(); // Build virtual primary index if (ancestors.Count > 0) { diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs index f2b9b3e7a..bde8c542c 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs @@ -632,7 +632,7 @@ private IndexInfo BuildJoinIndex(TypeInfo reflectedType, IEnumerable valueColumnMapping.Add((item.i, item.columns)); } - result.ValueColumnsMap = valueColumnMapping; + result.ValueColumnsMap = valueColumnMapping.ToArray(); result.ValueColumns.AddRange(GatherValueColumns(columnsToAdd)); result.Name = nameBuilder.BuildIndexName(reflectedType, result); result.Group = BuildColumnGroup(result); @@ -757,7 +757,7 @@ private IndexInfo BuildViewIndex(TypeInfo reflectedType, IndexInfo indexToApplyV } result.ValueColumns.AddRange(valueColumns); - result.SelectColumns = columnMap; + result.SelectColumns = columnMap.ToArray(); result.Name = nameBuilder.BuildIndexName(reflectedType, result); result.Group = BuildColumnGroup(result); diff --git a/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs b/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs index a1d29c917..f298cdf0a 100644 --- a/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs @@ -4,11 +4,7 @@ // Created by: Alex Ustinov // Created: 2007.07.10 -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics; -using System.Linq; using System.Linq.Expressions; using Xtensive.Collections; using Xtensive.Core; From f184be4ffc4b00b075c4706281836706fd0129b9 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 14:18:33 -0800 Subject: [PATCH 10/14] Fix missed ++ --- Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs index 16b7fa443..fa693980c 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs @@ -140,7 +140,7 @@ public static StructureExpression CreateLocalCollectionStructure(TypeInfo typeIn int i = 0; foreach (var field in sourceFields) { // Do not convert to LINQ. We intentionally avoiding closure creation here - destinationFields[i] = BuildNestedFieldExpression(field, mapping.Offset); + destinationFields[i++] = BuildNestedFieldExpression(field, mapping.Offset); } return result; From 52a574454037e812e091b3d2205c05531f85b56a Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 14:31:52 -0800 Subject: [PATCH 11/14] Continue --- .../Orm/Linq/Expressions/StructureFieldExpression.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs index 46b98273d..d5142c056 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs @@ -164,10 +164,11 @@ public static StructureFieldExpression CreateStructure(FieldInfo structureField, var fieldMappingInfo = structureField.MappingInfo; var mapping = new Segment((ColNum)(offset + fieldMappingInfo.Offset), fieldMappingInfo.Length); var result = new StructureFieldExpression(persistentType, structureField, mapping, null, false); - var processedFields = new List(persistentType.Fields.Count); + var processedFields = new PersistentFieldExpression[persistentType.Fields.Count]; + int i = 0; foreach (var field in persistentType.Fields) { // Do not convert to LINQ. We want to avoid a closure creation here. - processedFields.Add(BuildNestedFieldExpression(field, (ColNum) (offset + fieldMappingInfo.Offset))); + processedFields[i++] = BuildNestedFieldExpression(field, (ColNum) (offset + fieldMappingInfo.Offset)); } result.SetFields(processedFields); From 3d5c161a3c5e9b05e3ae3446f5a4d342abf39ff8 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 14:42:35 -0800 Subject: [PATCH 12/14] Optimize EnsureNotLocked() --- Orm/Xtensive.Orm/Core/LockableBase.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Core/LockableBase.cs b/Orm/Xtensive.Orm/Core/LockableBase.cs index 48cb59a6d..4e7dc4ec1 100644 --- a/Orm/Xtensive.Orm/Core/LockableBase.cs +++ b/Orm/Xtensive.Orm/Core/LockableBase.cs @@ -4,9 +4,8 @@ // Created by: Alex Yakunin // Created: 2007.11.22 -using System; using System.Diagnostics; - +using System.Runtime.CompilerServices; namespace Xtensive.Core { @@ -16,10 +15,12 @@ namespace Xtensive.Core [Serializable] public abstract class LockableBase: ILockable { - /// public bool IsLocked { [DebuggerStepThrough] get; private set; } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInstanceIsLockedException() => throw new InstanceIsLockedException(Strings.ExInstanceIsLocked); + /// /// Ensures the object is not locked (see ) yet. /// @@ -27,7 +28,7 @@ public abstract class LockableBase: ILockable public void EnsureNotLocked() { if (IsLocked) { - throw new InstanceIsLockedException(Strings.ExInstanceIsLocked); + ThrowInstanceIsLockedException(); } } From 3939992f4a9747f754e7251cc8603139079e25ae Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 15 Dec 2025 14:45:27 -0800 Subject: [PATCH 13/14] Optimize EnsureNotLocked() --- Orm/Xtensive.Orm/Core/LockableBase.cs | 60 ++++++++++----------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/Orm/Xtensive.Orm/Core/LockableBase.cs b/Orm/Xtensive.Orm/Core/LockableBase.cs index 4e7dc4ec1..628aec58a 100644 --- a/Orm/Xtensive.Orm/Core/LockableBase.cs +++ b/Orm/Xtensive.Orm/Core/LockableBase.cs @@ -7,48 +7,34 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -namespace Xtensive.Core +namespace Xtensive.Core; + +/// +/// Base class for implementors. +/// +[Serializable] +public abstract class LockableBase(bool isLocked = false) : ILockable { + /// + public bool IsLocked { [DebuggerStepThrough] get; private set; } = isLocked; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInstanceIsLockedException() => throw new InstanceIsLockedException(Strings.ExInstanceIsLocked); + /// - /// Base class for implementors. + /// Ensures the object is not locked (see ) yet. /// - [Serializable] - public abstract class LockableBase: ILockable + /// The instance is locked. + public void EnsureNotLocked() { - /// - public bool IsLocked { [DebuggerStepThrough] get; private set; } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ThrowInstanceIsLockedException() => throw new InstanceIsLockedException(Strings.ExInstanceIsLocked); - - /// - /// Ensures the object is not locked (see ) yet. - /// - /// The instance is locked. - public void EnsureNotLocked() - { - if (IsLocked) { - ThrowInstanceIsLockedException(); - } + if (IsLocked) { + ThrowInstanceIsLockedException(); } + } - /// - public void Lock() => Lock(true); - - /// - public virtual void Lock(bool recursive) => IsLocked = true; - - - - // Constructors + /// + public void Lock() => Lock(true); - /// - /// Initializes new instance of this type. - /// - /// Initial property value. - protected LockableBase(bool isLocked = false) - { - IsLocked = isLocked; - } - } + /// + public virtual void Lock(bool recursive) => IsLocked = true; } From a62873ee7139ec0eba61dd71adf34924ee3094e7 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 16 Dec 2025 00:02:47 -0800 Subject: [PATCH 14/14] Optimize BuildBatch() --- .../Sql.Drivers.Oracle/v09/Translator.cs | 5 +--- .../Sql/Compiler/SqlTranslator.cs | 25 ++++++++----------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs index 27cdccc3a..a686550a5 100644 --- a/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs +++ b/Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs @@ -18,9 +18,6 @@ namespace Xtensive.Sql.Drivers.Oracle.v09 { internal class Translator : SqlTranslator { - /// - public override string NewLine => "\n"; - /// public override string BatchBegin => "BEGIN\n"; @@ -441,4 +438,4 @@ public Translator(SqlDriver driver) { } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs index 3282fc67a..a6ed0ff56 100644 --- a/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs +++ b/Orm/Xtensive.Orm/Sql/Compiler/SqlTranslator.cs @@ -34,7 +34,7 @@ public abstract class SqlTranslator : SqlDriverBound public NumberFormatInfo FloatNumberFormat { get; private set; } public NumberFormatInfo DoubleNumberFormat { get; private set; } - public virtual string NewLine { get { return Environment.NewLine; } } + public string NewLine { get; } = "\n"; public virtual string OpeningParenthesis => "("; public virtual string ClosingParenthesis => ")"; @@ -2491,26 +2491,23 @@ public virtual string BuildBatch(IReadOnlyList statements) } var expectedLength = BatchBegin.Length + BatchEnd.Length + ((BatchItemDelimiter.Length + NewLine.Length) * statements.Count) - + statements.Sum(statement => statement.Length); - var valueBuilder = new ValueStringBuilder(expectedLength); - valueBuilder.Append(BatchBegin); + + statements.Sum(static statement => statement.Length); + StringBuilder sb = new(BatchBegin, expectedLength); foreach (var statement in statements) { - var statementAsSpan = (ReadOnlySpan) statement; - var actualStatement = statementAsSpan + var actualStatement = statement.AsSpan() .Trim() .TryCutPrefix(BatchBegin) .TryCutSuffix(BatchEnd) .TryCutSuffix(NewLine) .TryCutSuffix(BatchItemDelimiter) .Trim(); - if (actualStatement.Length == 0) - continue; - valueBuilder.Append(actualStatement.ToString()); - valueBuilder.Append(BatchItemDelimiter); - valueBuilder.Append(NewLine); - } - valueBuilder.Append(BatchEnd); - return valueBuilder.ToString(); + if (actualStatement.Length != 0) { + _ = sb.Append(actualStatement) + .Append(BatchItemDelimiter) + .Append(NewLine); + } + } + return sb.Append(BatchEnd).ToString(); } ///