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

Remove some allocations under HttpFileSystemBasedFindPackageByIdResource.ConsumeFlatContainerIndexAsync #6265

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from

Conversation

ToddGrun
Copy link
Contributor

@ToddGrun ToddGrun commented Feb 11, 2025

Bug

Fixes: NuGet/Home#14095

Description

Reduces allocation in HttpFileSystemBasedFindPackageByIdResource.ConsumeFlatContainerIndexAsync

  1. Moved Utf8JsonStreamReader to the build\Shared location so it can be compiled into multiple assemblies
  2. Created a new resource file for shared strings that Utf8JsonStreamReader.
  3. Switched to using Utf8JsonStreamReader for reading some JSON in HttpFileSystemBasedFindPackageByIdResource.
  4. Changed PackageInfo.ContentUri to be calculated on a deferred basis as it's very expensive to calculate and is used quite infrequently (I saw about 0.1% of PackageInfo objects created actually get queried for that data)

PR Checklist

  • Meaningful title, helpful description and a linked NuGet/Home issue
  • Added tests
  • Link to an issue or pull request to update docs if this PR changes settings, environment variables, new feature, etc.

@ToddGrun ToddGrun requested a review from a team as a code owner February 11, 2025 20:44
@microsoft-github-policy-service microsoft-github-policy-service bot added the Community PRs created by someone not in the NuGet team label Feb 11, 2025
var normalizedVersionString = Identity.Version.ToNormalizedString();
string idInLowerCase = Id.ToLowerInvariant();

var builder = StringBuilderPool.Shared.Rent(256);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Roughly 50% of the string builders used in this codepath end up exceeding the 256 char limit, and thus aren't released back to the pool. I didn't address it in this PR, as delay calculating this ends up getting rid of the vast majority of the work, but it might be worth considering upping that 256 char limit used in StringBuilderPool.

@jeffkl jeffkl self-assigned this Feb 11, 2025
@ToddGrun
Copy link
Contributor Author

Test insertion based on commit 1 to get speedometer numbers: https://devdiv.visualstudio.com/DevDiv/_git/VS/pullrequest/610228

@microsoft-github-policy-service microsoft-github-policy-service bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Feb 19, 2025
@jeffkl jeffkl removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Feb 19, 2025
@jeffkl
Copy link
Contributor

jeffkl commented Feb 26, 2025

I have pushed a change on behalf of the PR author to make Utf8JsonStreamReader a shared class.

NuGet.Common.PublicStrings
~static NuGet.Common.PublicStrings.Culture.get -> System.Globalization.CultureInfo
~static NuGet.Common.PublicStrings.Culture.set -> void
~static NuGet.Common.PublicStrings.Invalid_AttributeValue.get -> string
Copy link
Member

Choose a reason for hiding this comment

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

this is just my personal opinion, but I don't like resource strings as public APIs. There's too much risk of runtime failures if new {x} parameters are added to the string, and callers of the API don't update their string.Format argument list. We've even had this type of failure within the repo, so it'll be much worse if anyone using the NuGet Client SDK packages in their own app use this string for some reason.

I don't think that duplicating the strings, and keeping them internal, in multiple assemblies is bad enough to prefer public APIs.

Copy link
Contributor

Choose a reason for hiding this comment

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

See https://github.com/NuGet/NuGet.Client/pull/6265/files#r1970768798 for one complexity of not having these as public.

I totally understand its unusual here, but these are the only options I could come up with:

  1. Have a new .resx in any assembly that needs shared code under a common namespace so that the shared code can compile against it.
  2. Hardcode this string and not have it localized at all
  3. Have a set of public strings in NuGet.Common.dll that we are confident won't change and are pretty generic (this one for example is just telling a user that an attribute value is invalid).
  4. InternalsVisibleTo/friend assembly. I wish you could grant friend access to individual classes...
  5. Make Utf8JsonStreamReader public so that our assemblies can share it
  6. Refactor entire assemblies and combine them
  7. Other?

In this case, it seemed like the least worst option.

Copy link
Member

Choose a reason for hiding this comment

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

I want 6, but let's wait on that until we have a major version.

I'd prefer 1

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay I've pushed an update to use a shared resource that gets compiled into each assembly that needs it. At least the string is only defined in one place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community PRs created by someone not in the NuGet team
Projects
None yet
4 participants