Skip to content

Avoid intermediary array allocation in incremental generator#62100

Merged
CyrusNajmabadi merged 6 commits intodotnet:mainfrom
CyrusNajmabadi:intermediaryAlloc
Jun 28, 2022
Merged

Avoid intermediary array allocation in incremental generator#62100
CyrusNajmabadi merged 6 commits intodotnet:mainfrom
CyrusNajmabadi:intermediaryAlloc

Conversation

@CyrusNajmabadi
Copy link
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Jun 23, 2022

This drops us from 3GB of alloc to 2.3 GB (a good 25% reduction) during incremental generation.

Specifically it removes this array:

Before:
image

After:
image

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner June 23, 2022 18:27
@ghost ghost added the Area-Compilers label Jun 23, 2022
public ImmutableArray<NodeStateEntry<T>> Batch()
public IEnumerable<NodeStateEntry<T>> Batch()
{
var sourceBuilder = ArrayBuilder<NodeStateEntry<T>>.GetInstance();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this the array we're avoiding creating.

Copy link
Member

@chsienki chsienki left a comment

Choose a reason for hiding this comment

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

Nice! LGTM

}

public ImmutableArray<NodeStateEntry<T>> Batch()
public IEnumerable<NodeStateEntry<T>> Batch()
Copy link
Contributor

Choose a reason for hiding this comment

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

Batch

It looks like there is just the one caller to this method. Could we inline the enumerator in the caller and avoid allocating an iterator instance, etc.?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure. let me try that.

@CyrusNajmabadi CyrusNajmabadi requested a review from cston June 27, 2022 17:34
@CyrusNajmabadi
Copy link
Contributor Author

@cston much nicer. thanks!

@CyrusNajmabadi CyrusNajmabadi enabled auto-merge June 27, 2022 17:35

foreach (var entry in sourceTable)
{
if (entry.State != EntryState.Removed)
Copy link
Contributor

Choose a reason for hiding this comment

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

EntryState.Removed

Do we need to check sourceTable.HasTrackedSteps as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no. that was only used for adding information about steps. but we always do that in the line after this if-check.

if (entry.State != EntryState.Removed)
sourceValuesBuilder.Add(entry.Item);

sourceInputsBuilder?.Add((entry.Step!, entry.OutputIndex));
Copy link
Contributor

Choose a reason for hiding this comment

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

sourceInputsBuilder?.Add

Should adding to sourceInputsBuilder rely on the same conditions as sourceValuesBuilder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no. we always want to add the information about the steps including for a 'remove'. i can add comments.

if (entry.State != EntryState.Removed || HasTrackedSteps)
{
sourceBuilder.Add(entry);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cston the HasTrackedSteps was to make sure that we retruend this entry from the iterator so we could associated it with the perf data we're collecting. On the receiver side though we then filtered this out from the actual entry-list we were creating, but we used it for the step-list being built.


var batchedSourceEntries = sourceTable.Batch();
var sourceValues = batchedSourceEntries.SelectAsArray(sourceEntry => sourceEntry.State != EntryState.Removed, sourceEntry => sourceEntry.Item);
var sourceInputs = newTable.TrackIncrementalSteps ? batchedSourceEntries.SelectAsArray(sourceEntry => (sourceEntry.Step!, sourceEntry.OutputIndex)) : default;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cston here you can see the impact. batchedSourceEntries.SelectAsArray(sourceEntry => sourceEntry.State != EntryState.Removed removes the removed items that were included because we were tracking steps. but batchedSourceEntries.SelectAsArray(sourceEntry => (sourceEntry.Step!, sourceEntry.OutputIndex) keeps everything. The logic we have below preserve that. we enumerate everything, do not include the removed-items from the values-builder, but keeps the steps fro the for the steps-builder.

@CyrusNajmabadi CyrusNajmabadi requested a review from cston June 27, 2022 18:09
@CyrusNajmabadi CyrusNajmabadi merged commit 8b9b4b7 into dotnet:main Jun 28, 2022
@CyrusNajmabadi CyrusNajmabadi deleted the intermediaryAlloc branch June 28, 2022 00:03
@ghost ghost added this to the Next milestone Jun 28, 2022
@RikkiGibson RikkiGibson removed this from the Next milestone Jun 28, 2022
@RikkiGibson RikkiGibson added this to the 17.3 P3 milestone Jun 28, 2022
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