Collection expressions: optimize List<T> construction#70197
Collection expressions: optimize List<T> construction#70197cston merged 16 commits intodotnet:features/CollectionLiteralsfrom
Conversation
… have known length
| // while also allowing using the known length for common cases. In particular, this allows | ||
| // using the known length for simple concatenation of two elements [e, ..y] or [..x, ..y]. | ||
| // Temporaries are only needed up to the last spread, so this also allows [..x, e1, e2, ...]. | ||
| const int maxTemporaries = 3; |
7a673c6 to
d2928db
Compare
d2928db to
7dbca2a
Compare
| indexTemp, | ||
| _factory.Binary(BinaryOperatorKind.Addition, indexTemp.Type, indexTemp, _factory.Literal(1)), | ||
| isRef: false, | ||
| indexTemp.Type)); |
There was a problem hiding this comment.
Before the first spread element, the compiler should emit constants indices rather than using an index variable.
| (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, | ||
|
|
||
| // System_Collections_Generic_List_T__ctor | ||
| (byte)MemberFlags.Constructor, // Flags |
There was a problem hiding this comment.
nit: the indent on comments below is messed up
|
Here are the commits introduced after #69875: https://github.com/dotnet/roslyn/pull/70197/files/2f4cd81c92dfe4a6bb58ed4bcd011ba6f64e4589..acaacd8483cbe34ee6d6f880f6d1c62eb257c3b3 @333fred and I should be able to sign off on this PR just by reviewing those 3 commits. |
| return includesIEnumerable(compilation, interfaces); | ||
| } | ||
|
|
||
| static bool includesIEnumerable(CSharpCompilation compilation, ImmutableArray<NamedTypeSymbol> allInterfaces) |
There was a problem hiding this comment.
This refactoring (extracting a local function) doesn't seem particularly useful... #Closed
There was a problem hiding this comment.
The refactoring occurred before extracting GetAllInterfacesOrEffectiveInterfaces(). Merged.
| _ = GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_List_T__ctorInt32, diagnostics, syntax: syntax); | ||
| _ = GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_List_T__ToArray, diagnostics, syntax: syntax); | ||
|
|
||
| if (collectionTypeKind is CollectionExpressionTypeKind.List) |
There was a problem hiding this comment.
I didn't understand why we need Add but not ToArray when targeting to List and ToArray but not Add when targeting to list interface. #Resolved
There was a problem hiding this comment.
For list interface, we may use List<T>.Add. Updated.
| localsBuilder, | ||
| numberIncludingLastSpread, | ||
| sideEffects, | ||
| (ArrayBuilder<BoundExpression> expressions, BoundExpression arrayTemp, BoundExpression rewrittenValue) => |
There was a problem hiding this comment.
nit: On the other hand, the refactoring here (inlining of the local function) seems negative to me as it makes the main body of the method harder to follow (much logic inside the lambda now). Consider keeping original structure.
I'd probably also use a local function for the other call to AddCollectionExpressionElements below (line 592) #Closed
There was a problem hiding this comment.
Will keep as is, since there are three separate callers of AddCollectionExpressionElements() that each pass a lambda expression with inline method.
| // Do not use optimizations in async method since the optimizations require Span<T>. | ||
| if (useKnownLength && elements.Length > 0 && _factory.CurrentFunction?.IsAsync == false) | ||
| { | ||
| setCount = ((MethodSymbol?)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_CollectionsMarshal__SetCount_T))?.Construct(typeArguments); |
There was a problem hiding this comment.
In a custom BCL scenario, we'd want to check constraints. I'm okay to leave as-is as long as this was an explicit choice. It feels like the team has been more comfortable making assumptions about the well-known BCL APIs recently...
Also obsolete
|
It looks like @RikkiGibson and @jcouv are looking at this one, so I'll look at a different PR. |
| MethodSymbol? asSpan = null; | ||
|
|
||
| // Do not use optimizations in async method since the optimizations require Span<T>. | ||
| if (useKnownLength && elements.Length > 0 && _factory.CurrentFunction?.IsAsync == false) |
There was a problem hiding this comment.
ListConstruction_02 includes a test for the empty collection. We had several existing tests that hit the unknown length but added an explicit test: ListConstruction_03.
jcouv
left a comment
There was a problem hiding this comment.
Done with review pass (iteration 15). Only reviewed from commit 12. Minor comments/questions only
MIsc.
jcouv
left a comment
There was a problem hiding this comment.
LGTM Thanks (iterations 12 to 16)
|
|
||
| private BoundExpression GetKnownLengthExpression(ImmutableArray<BoundExpression> elements, int numberIncludingLastSpread, ArrayBuilder<BoundLocal> rewrittenExpressions) | ||
| { | ||
| Debug.Assert(rewrittenExpressions.Count >= numberIncludingLastSpread); |
There was a problem hiding this comment.
nit: consider adding a comment why rewrittenExpressions can have more items
When constructing a
List<T>instance when the collection length is known and the collection satisfies certain heuristics:CollectionsMarshal.SetCount<T>(List<T>, int)andCollectionsMarshal.AsSpan<T>(List<T>)if availableList<T>..ctor(int capacity)ifCollectionsMarshalis not available.Initial commits are from #69875.
Relates to test plan #66418