Skip to content

Optimize indexer access of an inline range expression#36147

Merged
agocke merged 7 commits intodotnet:masterfrom
agocke:optimize-pattern-range
Jun 12, 2019
Merged

Optimize indexer access of an inline range expression#36147
agocke merged 7 commits intodotnet:masterfrom
agocke:optimize-pattern-range

Conversation

@agocke
Copy link
Copy Markdown
Member

@agocke agocke commented Jun 4, 2019

Introduces optimizations in lowering for an indexer access of the form
receiver[a..b]. With the optimizations, the generation of intermediate
System.Index and System.Range types will be avoided.

agocke added 4 commits June 3, 2019 23:17
Introduces optimizations in lowering for an indexer access of the form
receiver[a..b]. With the optimizations, the generation of intermediate
System.Index and System.Range types will be avoided.
@agocke agocke changed the title Optimize pattern range Optimize indexer access of an inline range expression Jun 4, 2019
@agocke agocke marked this pull request as ready for review June 4, 2019 19:58
@agocke agocke requested a review from a team as a code owner June 4, 2019 19:58
@jcouv jcouv added the Feature - Range Range label Jun 4, 2019
@jcouv jcouv added this to the 16.2.P3 milestone Jun 4, 2019
}

BoundExpression endExpr;
switch (rangeExpr.RightOperandOpt)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider changing to if (rangeExpr.RightOperandOpt is null) ... else ...

@@ -384,44 +363,29 @@ .locals init (System.Span<int> V_0, //s
IL_0056: stloc.s V_4
IL_0058: ldloca.s V_4
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ldloca.s V_4 [](start = 12, length = 14)

Is this temp here to avoid bad behavior from repeated reads of the receiver? Just trying to picture what scenarios would cause trouble if the original were used instead of a temp.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, if the receiver is a property or method call (something with side effects) we don't want to evaluate it multiple times, so we create a temp.

Copy link
Copy Markdown
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@agocke
Copy link
Copy Markdown
Member Author

agocke commented Jun 6, 2019

@dotnet/roslyn-compiler for one more review please

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Jun 7, 2019

Looking #Closed

// int end = range.End.GetOffset(length)
// receiver.Slice(start, end - start)
// int rangeSize = range.End.GetOffset(length) - start
// receiver.Slice(start, rangeSize)
Copy link
Copy Markdown
Member

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

receiver.Slice(start, rangeSize) [](start = 14, length = 33)

Is there a design doc for ranges, where we can document the API patterns recognized by the compiler? #Closed

Copy link
Copy Markdown
Member Author

@agocke agocke Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// during lowering, but it's simpler to always require them to prevent
// the user from getting surprising errors when optimizations fail
_ = GetWellKnownTypeMember(Compilation, WellKnownMember.System_Range__get_Start, diagnostics, syntax: syntax);
_ = GetWellKnownTypeMember(Compilation, WellKnownMember.System_Range__get_End, diagnostics, syntax: syntax);
Copy link
Copy Markdown
Member

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetWellKnownTypeMember [](start = 32, length = 22)

consider factoring the GetWellKnownTypeMember checks into a local function and calling here and above. #Closed


bool usedLength = false;

if (rangeExpr.LeftOperandOpt is null)
Copy link
Copy Markdown
Member

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rangeExpr.LeftOperandOpt is null [](start = 20, length = 32)

nit: Consider inverting if and use a pattern like below: if (rangeExpr.RightOperandOpt is BoundExpression left) .... Or invert the if below as Rikki already pointed out ;-) #Closed


var F = _factory;

var localsBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
Copy link
Copy Markdown
Member

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.GetInstance(); [](start = 57, length = 15)

On line 221, you use an optimistic size (3), but here and on line below you don't pass any size. Is that worth aligning? #Closed

Copy link
Copy Markdown
Member Author

@agocke agocke Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm making a very subtle guess here. For Index, there's a pattern optimization where we don't need the Length if the Index is from the start, being created inline via a conversion. So the code would have to look like:

int offset = ...;
int item = collection[(Index)offset];

Note that this won't work if the offset is either from the end, or if there's no Index conversion (since the normal behavior is to call the indexer that takes an int directly).

So my prediction is that almost every pattern Index indexer optimization will be from the end. Thus, I hint that explicitly.

For Range, it's completely different. Code like 0..5 seems like it will very common, and because Range only takes Indexes, the construction requires the use of the Index -> int explicit conversion, that I try to remove optimistically. So in this case, if the user is making a range only from start, or only from end, they won't or will require the length, respectively.

Here, I have no idea which will be more common, so I don't hint anything.

Do you think this is worth a comment? #Closed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine to leave as-is. Thanks


In reply to: 291857095 [](ancestors = 291857095)


if (usedLength)
{
localsBuilder.Insert(1, lengthLocal.LocalSymbol);
Copy link
Copy Markdown
Member

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insert(1, [](start = 34, length = 10)

why not .Add(?
(also on next line) #Closed

Copy link
Copy Markdown
Member Author

@agocke agocke Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe side effects for the synthesized nodes are evaluated in order, so if we need the length, we need it before the bound nodes that use it, but after receiver, which is the first side effect calculated. #Closed


BoundExpression startExpr;
BoundExpression rangeSizeExpr;
if (rangeArg is BoundRangeExpression rangeExpr)
Copy link
Copy Markdown
Member

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if [](start = 12, length = 2)

The test cases I would expect for the new/optimized code: pattern indexer with .., ..y, x.. and x..y.
I wasn't able to identify test(s) for all of them. Can you confirm the coverage? #Closed

Copy link
Copy Markdown
Member Author

@agocke agocke Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked through and found a test for everything except ... I've added one in the latest commit. #Closed

Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review pass (iteration 5)

@jinujoseph jinujoseph modified the milestones: 16.2.P3, 16.2 Jun 9, 2019
@agocke
Copy link
Copy Markdown
Member Author

agocke commented Jun 11, 2019

@jcouv Anything else?

@agocke
Copy link
Copy Markdown
Member Author

agocke commented Jun 11, 2019

ping @jcouv

Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks (iteration 7)

@agocke agocke merged commit ac17b9c into dotnet:master Jun 12, 2019
@agocke agocke deleted the optimize-pattern-range branch June 12, 2019 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants