Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid temporary list allocations and specify list capacities in TaskItem #7055

Merged

Conversation

drewnoakes
Copy link
Member

Context

Noticed while browsing code.

Changes Made

  • Avoid creating temporary List<> objects in several cases
  • Specify List<> capacities to avoid temporaries during construction and wasted space in result
  • (minor) Use a named argument to resolve ambiguity
  • (minor) Replace as type checks with direct type conversion

Testing

Unit tests.

Notes

May be easier to review each commit separately.

src/Build/Instance/ProjectItemInstance.cs Outdated Show resolved Hide resolved
src/Utilities/TaskItem.cs Outdated Show resolved Hide resolved
Copy link
Member

@Forgind Forgind left a comment

Choose a reason for hiding this comment

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

It isn't actually relevant to your PR, but I noticed a couple instances of github.com/microsoft/msbuild later in this file. Clean-up of that optional.

src/Build/Instance/ProjectItemInstance.cs Outdated Show resolved Hide resolved
@@ -929,7 +929,12 @@ public ICollection MetadataNames
{
get
{
List<string> names = new List<string>((List<string>)CustomMetadataNames);
List<string> names = new List<string>(capacity: MetadataCount);
Copy link
Member

Choose a reason for hiding this comment

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

Note: this is only really an improvement if MetadataCount is very fast—which it currently might not be.

Also, is there some convenient method like:
return MetadataCollection.Count.Union(FileUtilities.ItemSpecModifiers.All);
?

All is an array, so I don't think so, but thought I'd check.

Copy link
Member Author

Choose a reason for hiding this comment

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

MetadataCount has been updated to be much faster. Please take another look and see if you feel this needs further work.

Copy link
Member

Choose a reason for hiding this comment

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

I said Count but meant Keys.

return MetadataCollection.Keys.Concat(FileUtilities.ItemSpecModifiers.All);
?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's possible, though we'd need to convert the IEnumerable<> to an ICollection . Calling ToList or ToArray is going to result in potentially multiple redundant allocations, as the resulting collection size is not known from the enumerator produced by Concat.

By allocating a list up-front with the needed capacity, that overhead is avoided and no waste occurs in the backing arrays.

It would be possible to make this allocate one fewer object by using string[] directly, rather than List<string>.

src/Build/Instance/ProjectItemInstance.cs Show resolved Hide resolved
@drewnoakes
Copy link
Member Author

I noticed a couple instances of github.com/microsoft/msbuild later in this file. Clean-up of that optional.

Fixed in e9eb4f6. There are 267 other such URLs though.

Copy link
Member

@ladipro ladipro left a comment

Choose a reason for hiding this comment

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

I noticed a couple instances of github.com/microsoft/msbuild later in this file. Clean-up of that optional.

Fixed in e9eb4f6. There are 267 other such URLs though.

Oops, I'll file a PR to fix the rest. Thank you!

src/Build/Instance/ProjectItemInstance.cs Outdated Show resolved Hide resolved
@@ -929,7 +929,12 @@ public ICollection MetadataNames
{
get
{
List<string> names = new List<string>((List<string>)CustomMetadataNames);
List<string> names = new List<string>(capacity: MetadataCount);
Copy link
Member

Choose a reason for hiding this comment

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

I said Count but meant Keys.

return MetadataCollection.Keys.Concat(FileUtilities.ItemSpecModifiers.All);
?

ladipro added a commit that referenced this pull request Nov 26, 2021
### Context

As @Forgind and @drewnoakes pointed out, we still have links to [github.com/microsoft/msbuild](https://github.com/microsoft/msbuild) in the tree.

### Changes Made

Replaced [github.com/microsoft/msbuild](https://github.com/microsoft/msbuild) with [github.com/dotnet/msbuild](https://github.com/dotnet/msbuild).

Except for the two occurrences fixed in #7055.

### Testing

Build, CI
These duplicated statements were probably intended to validate that the equality implementation is commutative.
The previous code confused me. I thought the comment was the variable name, which would suggest the instance would be immutable. In fact, the opposite is true. Using a named argument avoids that confusion.
The use of `as` suggests some kind of runtime type checking is involved, and that it may fail. In fact, the type is statically known to implement the interface, so direct assignment is sufficient.

There should be no runtime change here as the compiler appears to optimise the check away. I just find this form of the code clearer.
The repo was moved from the 'microsoft' org to the 'dotnet' org.
Required rewriting some of ProjectItemInstance.TaskItem.Equals.
There was only one usage. As the property is not cheap to call, inline it for further optimisation opportunities.
Internally it optimises for ICollection<>, reserving enough capacity in one hit, then using CopyTo to copy data.
@drewnoakes drewnoakes force-pushed the dev/drnoakes/task-item-list-allocations branch from fdc7bf7 to eab716d Compare December 2, 2021 08:43
@drewnoakes
Copy link
Member Author

drewnoakes commented Dec 2, 2021

I've force-pushed an update that's rebased on main, drops those commits which did not consider overriding metadata, added test coverage for metadata on the task item, and have optimised what seems safe and worth doing, based on API usage.

The next step will be to reduce churn of temporary values in immutable collections when copying sets of metadata around by adding new batch operations. I'll hold off until this and #7085 to land in whatever form they may.

src/Build/Instance/ProjectItemInstance.cs Outdated Show resolved Hide resolved
@rainersigwald rainersigwald added this to the VS 17.1 milestone Dec 6, 2021
@Forgind Forgind added the merge-when-branch-open PRs that are approved, except that there is a problem that means we are not merging stuff right now. label Dec 7, 2021
@rainersigwald rainersigwald merged commit a70ee30 into dotnet:main Dec 7, 2021
@drewnoakes drewnoakes deleted the dev/drnoakes/task-item-list-allocations branch December 9, 2021 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge-when-branch-open PRs that are approved, except that there is a problem that means we are not merging stuff right now. performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants