From b6bec80bf58d30dbe5340e353f4e2a3992e69379 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 8 Jul 2021 20:29:08 -0700 Subject: [PATCH 1/7] Optimizations: allocations in RecordSetHeader.Join(); use IReadOnlyList instead of ReadOnlyList to save allocations (#32) * Optimizations: allocate List<> with known capacity; use IReadOnlyList instead of ReadOnlyList * Optimize hot paths: replace LINQ by foreach --- Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs | 18 +-- .../Orm/Providers/SqlCompiler.Index.cs | 26 ++-- Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs | 21 ++-- Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs | 114 ++++++++++-------- .../Dml/Collections/SqlColumnCollection.cs | 5 + 5 files changed, 97 insertions(+), 87 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs b/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs index 3fb80426dd..adef139653 100644 --- a/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs +++ b/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2003-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alexey Kochetov // Created: 2008.08.01 @@ -28,12 +28,12 @@ public sealed class ColumnGroup /// /// Gets the indexes of key columns. /// - public ReadOnlyList Keys { get; private set; } + public IReadOnlyList Keys { get; private set; } /// /// Gets the indexes of all columns. /// - public ReadOnlyList Columns { get; private set; } + public IReadOnlyList Columns { get; private set; } // Constructors @@ -55,11 +55,11 @@ public ColumnGroup(TypeInfoRef type, IEnumerable keys, IEnumerable col /// The type. /// The keys. /// The columns. - public ColumnGroup(TypeInfoRef type, IList keys, IList columns) + public ColumnGroup(TypeInfoRef type, IReadOnlyList keys, IReadOnlyList columns) { TypeInfoRef = type; - Keys = new ReadOnlyList(keys); - Columns = new ReadOnlyList(columns); + Keys = keys; + Columns = columns; } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs index f81c08a9bd..cecba5fabb 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2009-2010 Xtensive LLC. -// This code is distributed under MIT license terms. -// See the License.txt file in the project root for more information. +// Copyright (C) 2009-2021 Xtensive LLC. +// All rights reserved. +// For conditions of distribution and use, see license. // Created by: Denis Krjuchkov // Created: 2009.11.13 @@ -15,7 +15,7 @@ namespace Xtensive.Orm.Providers { - partial class SqlCompiler + partial class SqlCompiler { protected override SqlProvider VisitFreeText(FreeTextProvider provider) { @@ -65,18 +65,22 @@ private SqlSelect BuildTableQuery(IndexInfo index) atRootPolicy = true; } - SqlSelect query; + var indexColumns = index.Columns; + var tableRef = SqlDml.TableRef(table); + var query = SqlDml.Select(tableRef); + var queryColumns = query.Columns; + queryColumns.Capacity = queryColumns.Count + indexColumns.Count; if (!atRootPolicy) { - var tableRef = SqlDml.TableRef(table); - query = SqlDml.Select(tableRef); - query.Columns.AddRange(index.Columns.Select(c => tableRef[c.Name])); + foreach (var c in indexColumns) { + queryColumns.Add(tableRef[c.Name]); + } } else { var root = index.ReflectedType.GetRoot().AffectedIndexes.First(i => i.IsPrimary); var lookup = root.Columns.ToDictionary(c => c.Field, c => c.Name); - var tableRef = SqlDml.TableRef(table); - query = SqlDml.Select(tableRef); - query.Columns.AddRange(index.Columns.Select(c => tableRef[lookup[c.Field]])); + foreach (var c in indexColumns) { + queryColumns.Add(tableRef[lookup[c.Field]]); + } } return query; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs b/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs index e7a82f4d0c..712215bba1 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2003-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alexey Kochetov // Created: 2007.09.24 @@ -37,12 +37,6 @@ public Column this[string fullName] { } } - private void BuildNameIndex() - { - for (var index = 0; index < Count; index++) - nameIndex.Add(this[index].Name, index); - } - /// /// Joins this collection with specified the column collection. /// @@ -59,7 +53,7 @@ public ColumnCollection Join(IEnumerable joined) /// The alias to add. /// Aliased collection of columns. public ColumnCollection Alias(string alias) - { + { ArgumentValidator.EnsureArgumentNotNullOrEmpty(alias, "alias"); return new ColumnCollection(this.Select(column => column.Clone(alias + "." + column.Name))); } @@ -71,10 +65,8 @@ public ColumnCollection Alias(string alias) /// /// Collection of items to add. public ColumnCollection(IEnumerable collection) - : base(collection.ToList()) + : this(collection.ToList()) { - nameIndex = new Dictionary(Count); - BuildNameIndex(); } /// @@ -85,7 +77,8 @@ public ColumnCollection(List collection) : base(collection) { nameIndex = new Dictionary(Count); - BuildNameIndex(); + for (var index = 0; index < Count; index++) + nameIndex.Add(this[index].Name, index); } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs b/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs index fc0a02d826..2e46ab85a8 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2003-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alexey Kochetov // Created: 2007.09.13 @@ -55,7 +55,7 @@ public int Length { public DirectionCollection Order { get; private set; } /// - /// Gets the tuple descriptor describing + /// Gets the tuple descriptor describing /// a set of columns. /// public TupleDescriptor OrderTupleDescriptor { @@ -111,10 +111,10 @@ public RecordSetHeader Add(IEnumerable columns) var newTupleDescriptor = TupleDescriptor.Create(newFieldTypes); return new RecordSetHeader( - newTupleDescriptor, - newColumns, - ColumnGroups, - OrderTupleDescriptor, + newTupleDescriptor, + newColumns, + ColumnGroups, + OrderTupleDescriptor, Order); } @@ -126,29 +126,37 @@ public RecordSetHeader Add(IEnumerable columns) public RecordSetHeader Join(RecordSetHeader joined) { var columnCount = Columns.Count; - var newColumns = new List(Columns); - newColumns.AddRange( - from c in joined.Columns - select c.Clone(columnCount + c.Index)); + var newColumns = new List(columnCount + joined.Columns.Count); + newColumns.AddRange(Columns); + foreach (var c in joined.Columns) { + newColumns.Add(c.Clone(columnCount + c.Index)); + } var newFieldTypes = new Type[newColumns.Count]; for (var i = 0; i < newColumns.Count; i++) newFieldTypes[i] = newColumns[i].Type; var newTupleDescriptor = TupleDescriptor.Create(newFieldTypes); - var groups = new List(ColumnGroups); - groups.AddRange( - joined.ColumnGroups - .Select(g => new ColumnGroup( - g.TypeInfoRef, - g.Keys.Select(i => columnCount + i), - g.Columns.Select(i => columnCount + i)))); - + var columnGroupCount = ColumnGroups.Count; + var groups = new List(columnGroupCount + joined.ColumnGroups.Count); + groups.AddRange(ColumnGroups); + foreach (var g in joined.ColumnGroups) { + var keys = new List(g.Keys.Count); + foreach (var i in g.Keys) { + keys.Add(columnCount + i); + } + var columns = new List(g.Columns.Count); + foreach (var i in g.Columns) { + columns.Add(columnCount + i); + } + groups.Add(new ColumnGroup(g.TypeInfoRef, keys, columns)); + } + return new RecordSetHeader( - newTupleDescriptor, - newColumns, + newTupleDescriptor, + newColumns, groups, - OrderTupleDescriptor, + OrderTupleDescriptor, Order); } @@ -171,7 +179,7 @@ public RecordSetHeader Select(IEnumerable selectedColumns) var resultOrder = new DirectionCollection( Order .Select(o => new KeyValuePair(columnsMap[o.Key], o.Value)) - .TakeWhile(o => o.Key >= 0)); + .TakeWhile(o => o.Key >= 0)); var resultColumns = columns.Select((oldIndex, newIndex) => Columns[oldIndex].Clone(newIndex)); @@ -179,17 +187,17 @@ public RecordSetHeader Select(IEnumerable selectedColumns) .Where(g => g.Keys.All(k => columnsMap[k]>=0)) .Select(g => new ColumnGroup( g.TypeInfoRef, - g.Keys.Select(k => columnsMap[k]), + g.Keys.Select(k => columnsMap[k]), g.Columns .Select(c => columnsMap[c]) .Where(c => c >= 0))); return new RecordSetHeader( - resultTupleDescriptor, - resultColumns, - resultGroups, - null, - resultOrder); + resultTupleDescriptor, + resultColumns, + resultGroups, + null, + resultOrder); } /// @@ -242,24 +250,24 @@ private static RecordSetHeader CreateHeader(IndexInfo indexInfo) .Select(columnInfo => columnInfo.Key.ValueType) .ToArray(indexInfoKeyColumns.Count); var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); - + var resultColumns = indexInfoColumns.Select((c,i) => (Column) new MappedColumn(c,i,c.ValueType)); var resultGroups = new[]{indexInfo.Group}; return new RecordSetHeader( - resultTupleDescriptor, - resultColumns, - resultGroups, - keyDescriptor, + resultTupleDescriptor, + resultColumns, + resultGroups, + keyDescriptor, order); } /// public override string ToString() { - return Columns.Select(c => c.ToString()).ToCommaDelimitedString(); + return Columns.Select(c => c.ToString()).ToCommaDelimitedString(); } - + // Constructors @@ -267,9 +275,9 @@ public override string ToString() /// Initializes a new instance of this class. /// /// Descriptor of the result item. - /// Result columns. + /// Result columns. public RecordSetHeader( - TupleDescriptor tupleDescriptor, + TupleDescriptor tupleDescriptor, IEnumerable columns) : this(tupleDescriptor, columns, null, null, null) { @@ -279,11 +287,11 @@ public RecordSetHeader( /// Initializes a new instance of this class. /// /// Descriptor of the result item. - /// Result columns. + /// Result columns. /// Column groups. public RecordSetHeader( - TupleDescriptor tupleDescriptor, - IEnumerable columns, + TupleDescriptor tupleDescriptor, + IEnumerable columns, IEnumerable columnGroups) : this(tupleDescriptor, columns, columnGroups, null, null) { @@ -293,14 +301,14 @@ public RecordSetHeader( /// Initializes a new instance of this class. /// /// Descriptor of the result item. - /// Result columns. + /// Result columns. /// Descriptor of ordered columns. /// Result sort order. public RecordSetHeader( - TupleDescriptor tupleDescriptor, - IEnumerable columns, + TupleDescriptor tupleDescriptor, + IEnumerable columns, TupleDescriptor orderKeyDescriptor, - DirectionCollection order) + DirectionCollection order) : this(tupleDescriptor, columns, null, orderKeyDescriptor, order) { } @@ -309,25 +317,25 @@ public RecordSetHeader( /// Initializes a new instance of this class. /// /// Descriptor of the result item. - /// Result columns. + /// Result columns. /// Column groups. /// Descriptor of ordered columns. /// Result sort order. /// columns.Count is out of range. public RecordSetHeader( - TupleDescriptor tupleDescriptor, - IEnumerable columns, + TupleDescriptor tupleDescriptor, + IEnumerable columns, IEnumerable columnGroups, TupleDescriptor orderKeyDescriptor, - DirectionCollection order) + DirectionCollection order) { ArgumentValidator.EnsureArgumentNotNull(tupleDescriptor, "tupleDescriptor"); ArgumentValidator.EnsureArgumentNotNull(columns, "columns"); TupleDescriptor = tupleDescriptor; // Unsafe perf. optimization: if you pass a list, it should be immutable! - Columns = columns is List columnList - ? new ColumnCollection(columnList) + Columns = columns is List columnList + ? new ColumnCollection(columnList) : new ColumnCollection(columns); if (tupleDescriptor.Count!=Columns.Count) throw new ArgumentOutOfRangeException("columns.Count"); @@ -336,7 +344,7 @@ public RecordSetHeader( ? ColumnGroupCollection.Empty // Unsafe perf. optimization: if you pass a list, it should be immutable! : (columnGroups is List columnGroupList - ? new ColumnGroupCollection(columnGroupList) + ? new ColumnGroupCollection(columnGroupList) : new ColumnGroupCollection(columnGroups)); orderTupleDescriptor = orderKeyDescriptor ?? TupleDescriptor.Empty; diff --git a/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs b/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs index b717d00af7..084c98b64f 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs @@ -24,6 +24,11 @@ public class SqlColumnCollection : ICollection, IReadOnlyList public int Count => columnList.Count; + public int Capacity { + get => columnList.Capacity; + set => columnList.Capacity = value; + } + /// > bool ICollection.IsReadOnly => false; From 7db0b19275f8297fd09b9402900691c600710d95 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 23 Aug 2021 23:49:50 -0700 Subject: [PATCH 2/7] Update Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs b/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs index adef139653..f9caeee290 100644 --- a/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs +++ b/Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2003-2021 Xtensive LLC. +// Copyright (C) 2008-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kochetov From cc58720560c2f973f146ca09f3eb3aa4e595adc4 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 23 Aug 2021 23:50:05 -0700 Subject: [PATCH 3/7] Update Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs b/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs index 2e46ab85a8..8d9adee519 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2003-2021 Xtensive LLC. +// Copyright (C) 2007-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kochetov From c807b6e65d5d522329575fa6bf683423151b4f70 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 23 Aug 2021 23:50:18 -0700 Subject: [PATCH 4/7] Update Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs b/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs index 084c98b64f..79bb152e21 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs @@ -24,6 +24,9 @@ public class SqlColumnCollection : ICollection, IReadOnlyList public int Count => columnList.Count; + /// + /// Gets or sets capacity of the collection. + /// public int Capacity { get => columnList.Capacity; set => columnList.Capacity = value; From 0873505a2358ef95dbc222a9b0d40d88da5acae1 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 23 Aug 2021 23:50:37 -0700 Subject: [PATCH 5/7] Update Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs b/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs index 712215bba1..579314d676 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs @@ -77,8 +77,9 @@ public ColumnCollection(List collection) : base(collection) { nameIndex = new Dictionary(Count); - for (var index = 0; index < Count; index++) + for (var index = 0; index < Count; index++) { nameIndex.Add(this[index].Name, index); + } } } } From 55f2d1f0c8e09617f7afcd62298ac99e4e94bcc9 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 23 Aug 2021 23:50:43 -0700 Subject: [PATCH 6/7] Update Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs b/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs index 579314d676..88f2ff714c 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2003-2021 Xtensive LLC. +// Copyright (C) 2007-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kochetov From b930c310c2df110bd2eeb0e9e1d44c54840c3f03 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 23 Aug 2021 23:55:20 -0700 Subject: [PATCH 7/7] Update copyright in SqlColumnCollection.cs --- Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs b/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs index 79bb152e21..17d856871b 100644 --- a/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs +++ b/Orm/Xtensive.Orm/Sql/Dml/Collections/SqlColumnCollection.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2008-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information using System; using System.Collections;