Skip to content

Commit 27294bb

Browse files
authored
Merge pull request #154 from servicetitan/optimizeAllocations
Optimizations: allocations in RecordSetHeader.Join(); use IReadOnlyList instead of ReadOnlyList to save allocations
2 parents 9b13eae + b930c31 commit 27294bb

File tree

5 files changed

+104
-90
lines changed

5 files changed

+104
-90
lines changed

Orm/Xtensive.Orm/Orm/Model/ColumnGroup.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2008-2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Kochetov
55
// Created: 2008.08.01
66

@@ -28,12 +28,12 @@ public sealed class ColumnGroup
2828
/// <summary>
2929
/// Gets the indexes of key columns.
3030
/// </summary>
31-
public ReadOnlyList<int> Keys { get; private set; }
31+
public IReadOnlyList<int> Keys { get; private set; }
3232

3333
/// <summary>
3434
/// Gets the indexes of all columns.
3535
/// </summary>
36-
public ReadOnlyList<int> Columns { get; private set; }
36+
public IReadOnlyList<int> Columns { get; private set; }
3737

3838

3939
// Constructors
@@ -55,11 +55,11 @@ public ColumnGroup(TypeInfoRef type, IEnumerable<int> keys, IEnumerable<int> col
5555
/// <param name="type">The type.</param>
5656
/// <param name="keys">The keys.</param>
5757
/// <param name="columns">The columns.</param>
58-
public ColumnGroup(TypeInfoRef type, IList<int> keys, IList<int> columns)
58+
public ColumnGroup(TypeInfoRef type, IReadOnlyList<int> keys, IReadOnlyList<int> columns)
5959
{
6060
TypeInfoRef = type;
61-
Keys = new ReadOnlyList<int>(keys);
62-
Columns = new ReadOnlyList<int>(columns);
61+
Keys = keys;
62+
Columns = columns;
6363
}
6464
}
65-
}
65+
}

Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2009-2010 Xtensive LLC.
2-
// This code is distributed under MIT license terms.
3-
// See the License.txt file in the project root for more information.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
2+
// All rights reserved.
3+
// For conditions of distribution and use, see license.
44
// Created by: Denis Krjuchkov
55
// Created: 2009.11.13
66

@@ -15,7 +15,7 @@
1515

1616
namespace Xtensive.Orm.Providers
1717
{
18-
partial class SqlCompiler
18+
partial class SqlCompiler
1919
{
2020
protected override SqlProvider VisitFreeText(FreeTextProvider provider)
2121
{
@@ -65,18 +65,22 @@ private SqlSelect BuildTableQuery(IndexInfo index)
6565
atRootPolicy = true;
6666
}
6767

68-
SqlSelect query;
68+
var indexColumns = index.Columns;
69+
var tableRef = SqlDml.TableRef(table);
70+
var query = SqlDml.Select(tableRef);
71+
var queryColumns = query.Columns;
72+
queryColumns.Capacity = queryColumns.Count + indexColumns.Count;
6973
if (!atRootPolicy) {
70-
var tableRef = SqlDml.TableRef(table);
71-
query = SqlDml.Select(tableRef);
72-
query.Columns.AddRange(index.Columns.Select(c => tableRef[c.Name]));
74+
foreach (var c in indexColumns) {
75+
queryColumns.Add(tableRef[c.Name]);
76+
}
7377
}
7478
else {
7579
var root = index.ReflectedType.GetRoot().AffectedIndexes.First(i => i.IsPrimary);
7680
var lookup = root.Columns.ToDictionary(c => c.Field, c => c.Name);
77-
var tableRef = SqlDml.TableRef(table);
78-
query = SqlDml.Select(tableRef);
79-
query.Columns.AddRange(index.Columns.Select(c => tableRef[lookup[c.Field]]));
81+
foreach (var c in indexColumns) {
82+
queryColumns.Add(tableRef[lookup[c.Field]]);
83+
}
8084
}
8185
return query;
8286
}

Orm/Xtensive.Orm/Orm/Rse/ColumnCollection.cs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2007-2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Kochetov
55
// Created: 2007.09.24
66

@@ -37,12 +37,6 @@ public Column this[string fullName] {
3737
}
3838
}
3939

40-
private void BuildNameIndex()
41-
{
42-
for (var index = 0; index < Count; index++)
43-
nameIndex.Add(this[index].Name, index);
44-
}
45-
4640
/// <summary>
4741
/// Joins this collection with specified the column collection.
4842
/// </summary>
@@ -59,7 +53,7 @@ public ColumnCollection Join(IEnumerable<Column> joined)
5953
/// <param name="alias">The alias to add.</param>
6054
/// <returns>Aliased collection of columns.</returns>
6155
public ColumnCollection Alias(string alias)
62-
{
56+
{
6357
ArgumentValidator.EnsureArgumentNotNullOrEmpty(alias, "alias");
6458
return new ColumnCollection(this.Select(column => column.Clone(alias + "." + column.Name)));
6559
}
@@ -71,10 +65,8 @@ public ColumnCollection Alias(string alias)
7165
/// </summary>
7266
/// <param name="collection">Collection of items to add.</param>
7367
public ColumnCollection(IEnumerable<Column> collection)
74-
: base(collection.ToList())
68+
: this(collection.ToList())
7569
{
76-
nameIndex = new Dictionary<string, int>(Count);
77-
BuildNameIndex();
7870
}
7971

8072
/// <summary>
@@ -85,7 +77,9 @@ public ColumnCollection(List<Column> collection)
8577
: base(collection)
8678
{
8779
nameIndex = new Dictionary<string, int>(Count);
88-
BuildNameIndex();
80+
for (var index = 0; index < Count; index++) {
81+
nameIndex.Add(this[index].Name, index);
82+
}
8983
}
9084
}
9185
}

Orm/Xtensive.Orm/Orm/Rse/RecordSetHeader.cs

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2007-2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Kochetov
55
// Created: 2007.09.13
66

@@ -55,7 +55,7 @@ public int Length {
5555
public DirectionCollection<int> Order { get; private set; }
5656

5757
/// <summary>
58-
/// Gets the tuple descriptor describing
58+
/// Gets the tuple descriptor describing
5959
/// a set of <see cref="Order"/> columns.
6060
/// </summary>
6161
public TupleDescriptor OrderTupleDescriptor {
@@ -111,10 +111,10 @@ public RecordSetHeader Add(IEnumerable<Column> columns)
111111
var newTupleDescriptor = TupleDescriptor.Create(newFieldTypes);
112112

113113
return new RecordSetHeader(
114-
newTupleDescriptor,
115-
newColumns,
116-
ColumnGroups,
117-
OrderTupleDescriptor,
114+
newTupleDescriptor,
115+
newColumns,
116+
ColumnGroups,
117+
OrderTupleDescriptor,
118118
Order);
119119
}
120120

@@ -126,29 +126,37 @@ public RecordSetHeader Add(IEnumerable<Column> columns)
126126
public RecordSetHeader Join(RecordSetHeader joined)
127127
{
128128
var columnCount = Columns.Count;
129-
var newColumns = new List<Column>(Columns);
130-
newColumns.AddRange(
131-
from c in joined.Columns
132-
select c.Clone(columnCount + c.Index));
129+
var newColumns = new List<Column>(columnCount + joined.Columns.Count);
130+
newColumns.AddRange(Columns);
131+
foreach (var c in joined.Columns) {
132+
newColumns.Add(c.Clone(columnCount + c.Index));
133+
}
133134

134135
var newFieldTypes = new Type[newColumns.Count];
135136
for (var i = 0; i < newColumns.Count; i++)
136137
newFieldTypes[i] = newColumns[i].Type;
137138
var newTupleDescriptor = TupleDescriptor.Create(newFieldTypes);
138139

139-
var groups = new List<ColumnGroup>(ColumnGroups);
140-
groups.AddRange(
141-
joined.ColumnGroups
142-
.Select(g => new ColumnGroup(
143-
g.TypeInfoRef,
144-
g.Keys.Select(i => columnCount + i),
145-
g.Columns.Select(i => columnCount + i))));
146-
140+
var columnGroupCount = ColumnGroups.Count;
141+
var groups = new List<ColumnGroup>(columnGroupCount + joined.ColumnGroups.Count);
142+
groups.AddRange(ColumnGroups);
143+
foreach (var g in joined.ColumnGroups) {
144+
var keys = new List<int>(g.Keys.Count);
145+
foreach (var i in g.Keys) {
146+
keys.Add(columnCount + i);
147+
}
148+
var columns = new List<int>(g.Columns.Count);
149+
foreach (var i in g.Columns) {
150+
columns.Add(columnCount + i);
151+
}
152+
groups.Add(new ColumnGroup(g.TypeInfoRef, keys, columns));
153+
}
154+
147155
return new RecordSetHeader(
148-
newTupleDescriptor,
149-
newColumns,
156+
newTupleDescriptor,
157+
newColumns,
150158
groups,
151-
OrderTupleDescriptor,
159+
OrderTupleDescriptor,
152160
Order);
153161
}
154162

@@ -171,25 +179,25 @@ public RecordSetHeader Select(IEnumerable<int> selectedColumns)
171179
var resultOrder = new DirectionCollection<int>(
172180
Order
173181
.Select(o => new KeyValuePair<int, Direction>(columnsMap[o.Key], o.Value))
174-
.TakeWhile(o => o.Key >= 0));
182+
.TakeWhile(o => o.Key >= 0));
175183

176184
var resultColumns = columns.Select((oldIndex, newIndex) => Columns[oldIndex].Clone(newIndex));
177185

178186
var resultGroups = ColumnGroups
179187
.Where(g => g.Keys.All(k => columnsMap[k]>=0))
180188
.Select(g => new ColumnGroup(
181189
g.TypeInfoRef,
182-
g.Keys.Select(k => columnsMap[k]),
190+
g.Keys.Select(k => columnsMap[k]),
183191
g.Columns
184192
.Select(c => columnsMap[c])
185193
.Where(c => c >= 0)));
186194

187195
return new RecordSetHeader(
188-
resultTupleDescriptor,
189-
resultColumns,
190-
resultGroups,
191-
null,
192-
resultOrder);
196+
resultTupleDescriptor,
197+
resultColumns,
198+
resultGroups,
199+
null,
200+
resultOrder);
193201
}
194202

195203
/// <summary>
@@ -242,34 +250,34 @@ private static RecordSetHeader CreateHeader(IndexInfo indexInfo)
242250
.Select(columnInfo => columnInfo.Key.ValueType)
243251
.ToArray(indexInfoKeyColumns.Count);
244252
var keyDescriptor = TupleDescriptor.Create(keyFieldTypes);
245-
253+
246254
var resultColumns = indexInfoColumns.Select((c,i) => (Column) new MappedColumn(c,i,c.ValueType));
247255
var resultGroups = new[]{indexInfo.Group};
248256

249257
return new RecordSetHeader(
250-
resultTupleDescriptor,
251-
resultColumns,
252-
resultGroups,
253-
keyDescriptor,
258+
resultTupleDescriptor,
259+
resultColumns,
260+
resultGroups,
261+
keyDescriptor,
254262
order);
255263
}
256264

257265
/// <inheritdoc/>
258266
public override string ToString()
259267
{
260-
return Columns.Select(c => c.ToString()).ToCommaDelimitedString();
268+
return Columns.Select(c => c.ToString()).ToCommaDelimitedString();
261269
}
262-
270+
263271

264272
// Constructors
265273

266274
/// <summary>
267275
/// Initializes a new instance of this class.
268276
/// </summary>
269277
/// <param name="tupleDescriptor">Descriptor of the result item.</param>
270-
/// <param name="columns">Result columns.</param>
278+
/// <param name="columns">Result columns.</param>
271279
public RecordSetHeader(
272-
TupleDescriptor tupleDescriptor,
280+
TupleDescriptor tupleDescriptor,
273281
IEnumerable<Column> columns)
274282
: this(tupleDescriptor, columns, null, null, null)
275283
{
@@ -279,11 +287,11 @@ public RecordSetHeader(
279287
/// Initializes a new instance of this class.
280288
/// </summary>
281289
/// <param name="tupleDescriptor">Descriptor of the result item.</param>
282-
/// <param name="columns">Result columns.</param>
290+
/// <param name="columns">Result columns.</param>
283291
/// <param name="columnGroups">Column groups.</param>
284292
public RecordSetHeader(
285-
TupleDescriptor tupleDescriptor,
286-
IEnumerable<Column> columns,
293+
TupleDescriptor tupleDescriptor,
294+
IEnumerable<Column> columns,
287295
IEnumerable<ColumnGroup> columnGroups)
288296
: this(tupleDescriptor, columns, columnGroups, null, null)
289297
{
@@ -293,14 +301,14 @@ public RecordSetHeader(
293301
/// Initializes a new instance of this class.
294302
/// </summary>
295303
/// <param name="tupleDescriptor">Descriptor of the result item.</param>
296-
/// <param name="columns">Result columns.</param>
304+
/// <param name="columns">Result columns.</param>
297305
/// <param name="orderKeyDescriptor">Descriptor of ordered columns.</param>
298306
/// <param name="order">Result sort order.</param>
299307
public RecordSetHeader(
300-
TupleDescriptor tupleDescriptor,
301-
IEnumerable<Column> columns,
308+
TupleDescriptor tupleDescriptor,
309+
IEnumerable<Column> columns,
302310
TupleDescriptor orderKeyDescriptor,
303-
DirectionCollection<int> order)
311+
DirectionCollection<int> order)
304312
: this(tupleDescriptor, columns, null, orderKeyDescriptor, order)
305313
{
306314
}
@@ -309,25 +317,25 @@ public RecordSetHeader(
309317
/// Initializes a new instance of this class.
310318
/// </summary>
311319
/// <param name="tupleDescriptor">Descriptor of the result item.</param>
312-
/// <param name="columns">Result columns.</param>
320+
/// <param name="columns">Result columns.</param>
313321
/// <param name="columnGroups">Column groups.</param>
314322
/// <param name="orderKeyDescriptor">Descriptor of ordered columns.</param>
315323
/// <param name="order">Result sort order.</param>
316324
/// <exception cref="ArgumentOutOfRangeException"><c>columns.Count</c> is out of range.</exception>
317325
public RecordSetHeader(
318-
TupleDescriptor tupleDescriptor,
319-
IEnumerable<Column> columns,
326+
TupleDescriptor tupleDescriptor,
327+
IEnumerable<Column> columns,
320328
IEnumerable<ColumnGroup> columnGroups,
321329
TupleDescriptor orderKeyDescriptor,
322-
DirectionCollection<int> order)
330+
DirectionCollection<int> order)
323331
{
324332
ArgumentValidator.EnsureArgumentNotNull(tupleDescriptor, "tupleDescriptor");
325333
ArgumentValidator.EnsureArgumentNotNull(columns, "columns");
326334

327335
TupleDescriptor = tupleDescriptor;
328336
// Unsafe perf. optimization: if you pass a list, it should be immutable!
329-
Columns = columns is List<Column> columnList
330-
? new ColumnCollection(columnList)
337+
Columns = columns is List<Column> columnList
338+
? new ColumnCollection(columnList)
331339
: new ColumnCollection(columns);
332340
if (tupleDescriptor.Count!=Columns.Count)
333341
throw new ArgumentOutOfRangeException("columns.Count");
@@ -336,7 +344,7 @@ public RecordSetHeader(
336344
? ColumnGroupCollection.Empty
337345
// Unsafe perf. optimization: if you pass a list, it should be immutable!
338346
: (columnGroups is List<ColumnGroup> columnGroupList
339-
? new ColumnGroupCollection(columnGroupList)
347+
? new ColumnGroupCollection(columnGroupList)
340348
: new ColumnGroupCollection(columnGroups));
341349

342350
orderTupleDescriptor = orderKeyDescriptor ?? TupleDescriptor.Empty;

0 commit comments

Comments
 (0)