Skip to content

Commit

Permalink
Introduce optimized dependency graph resolution algorithm (#5963)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffkl authored Aug 12, 2024
1 parent 4b906a9 commit e68fb0f
Show file tree
Hide file tree
Showing 16 changed files with 1,501 additions and 138 deletions.
102 changes: 102 additions & 0 deletions build/Shared/SharedStringBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable disable

using System;
using System.Text;

namespace NuGet
{
/// <summary>
/// Provides a resource pool that enables reusing instances of <see cref="StringBuilder"/> instances.
/// </summary>
/// <remarks>
/// <para>
/// Renting and returning buffers with an <see cref="StringBuilderPool"/> can increase performance
/// in situations where <see cref="StringBuilder"/> instances are created and destroyed frequently,
/// resulting in significant memory pressure on the garbage collector.
/// </para>
/// <para>
/// This class is thread-safe. All members may be used by multiple threads concurrently.
/// </para>
/// </remarks>
internal class SharedStringBuilder
{
private const int MaxSize = 256;

[ThreadStatic]
private static StringBuilder BuilderInstance = null;

[ThreadStatic]
private static bool InUse = false;

public static readonly SharedStringBuilder Instance = new SharedStringBuilder();

private SharedStringBuilder()
{
}

/// <summary>
/// Retrieves a <see cref="StringBuilder"/> that is at least the requested length.
/// </summary>
/// <param name="minimumCapacity">The minimum capacity of the <see cref="StringBuilder"/> needed.</param>
/// <returns>
/// A <see cref="StringBuilder"/> that is at least <paramref name="minimumCapacity"/> in length.
/// </returns>
/// <remarks>
/// This buffer is loaned to the caller and should be returned to the same pool via
/// <see cref="ToStringAndReturn"/> so that it may be reused in subsequent usage of <see cref="Rent"/>.
/// It is not a fatal error to not return a rented string builder, but failure to do so may lead to
/// decreased application performance, as the pool may need to create a new instance to replace
/// the one lost.
/// </remarks>
public StringBuilder Rent(int minimumCapacity)
{
if (!InUse)
{
if (minimumCapacity <= MaxSize)
{
InUse = true;

if (BuilderInstance == null)
{
BuilderInstance = new StringBuilder(MaxSize);
}

return BuilderInstance;
}
}

return new StringBuilder(minimumCapacity);
}

/// <summary>
/// Returns to the pool an array that was previously obtained via <see cref="Rent"/> on the same
/// <see cref="StringBuilderPool"/> instance, returning the built string.
/// </summary>
/// <param name="builder">
/// The <see cref="StringBuilder"/> previously obtained from <see cref="Rent"/> to return to the pool.
/// </param>
/// <remarks>
/// Once a <see cref="StringBuilder"/> has been returned to the pool, the caller gives up all ownership
/// of the instance and must not use it. The reference returned from a given call to <see cref="Rent"/>
/// must only be returned via <see cref="ToStringAndReturn"/> once. The default <see cref="StringBuilderPool"/>
/// may hold onto the returned instance in order to rent it again, or it may release the returned instance
/// if it's determined that the pool already has enough instances stored.
/// </remarks>
/// <returns>The string, built from <paramref name="builder"/>.</returns>
public string ToStringAndReturn(StringBuilder builder)
{
string result = builder.ToString();

if (ReferenceEquals(BuilderInstance, builder))
{
InUse = false;
builder.Clear();
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1017,19 +1017,21 @@ private ICollection<ProjectWithInnerNodes> LoadProjects(IEnumerable<ProjectGraph
// Suppresses an error that a target does not exist because it may or may not contain the targets that we're running
BuildRequestDataFlags.SkipNonexistentTargets));

BuildResult result = buildSubmission.Execute();

if (result.OverallResult == BuildResultCode.Failure)
buildSubmission.ExecuteAsync((submission) =>
{
failedBuildSubmissionCount++;
}
BuildResult result = submission.BuildResult;
if (result.OverallResult == BuildResultCode.Failure)
{
failedBuildSubmissionCount++;
}
buildCount++;
buildCount++;
projects.AddOrUpdate(
projectInstance.FullPath,
key => new ProjectWithInnerNodes(targetFramework, new MSBuildProjectInstance(projectInstance)),
(_, item) => item.Add(targetFramework, new MSBuildProjectInstance(projectInstance)));
projects.AddOrUpdate(
projectInstance.FullPath,
key => new ProjectWithInnerNodes(targetFramework, new MSBuildProjectInstance(projectInstance)),
(_, item) => item.Add(targetFramework, new MSBuildProjectInstance(projectInstance)));
}, context: null);
}
}
finally
Expand Down
3 changes: 1 addition & 2 deletions src/NuGet.Core/NuGet.Commands/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'bool PackCommandRunner.ProcessProjectJsonFile(PackageBuilder builder, string basePath, string id, NuGetVersion version, string suffix, Func<string, string> propertyProvider)', validate parameter 'builder' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PackCommandRunner.ProcessProjectJsonFile(NuGet.Packaging.PackageBuilder,System.String,System.String,NuGet.Versioning.NuGetVersion,System.String,System.Func{System.String,System.String})~System.Boolean")]
[assembly: SuppressMessage("Build", "CA1801:Parameter propertyProvider of method ProcessProjectJsonFile is never used. Remove the parameter or use it in the method body.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PackCommandRunner.ProcessProjectJsonFile(NuGet.Packaging.PackageBuilder,System.String,System.String,NuGet.Versioning.NuGetVersion,System.String,System.Func{System.String,System.String})~System.Boolean")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void PackCommandRunner.SetupCurrentDirectory(PackArgs packArgs)', validate parameter 'packArgs' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PackCommandRunner.SetupCurrentDirectory(NuGet.Commands.PackArgs)")]
[assembly: SuppressMessage("Build", "CA1031:Modify 'GetRuntimeGraph' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.ProjectRestoreCommand.GetRuntimeGraph(System.String)~NuGet.RuntimeModel.RuntimeGraph")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.ProjectRestoreCommand.GetRuntimeGraph(System.String,NuGet.Commands.RestoreCollectorLogger)~NuGet.RuntimeModel.RuntimeGraph")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Task PushRunner.Run(ISettings settings, IPackageSourceProvider sourceProvider, string packagePath, string source, string apiKey, string symbolSource, string symbolApiKey, int timeoutSeconds, bool disableBuffering, bool noSymbols, bool noServiceEndpoint, bool skipDuplicate, ILogger logger)', validate parameter 'sourceProvider' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PushRunner.Run(NuGet.Configuration.ISettings,NuGet.Configuration.IPackageSourceProvider,System.String,System.String,System.String,System.String,System.String,System.Int32,System.Boolean,System.Boolean,System.Boolean,System.Boolean,NuGet.Common.ILogger)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void RemoveSourceRunner.Run(RemoveSourceArgs args, Func<ILogger> getLogger)', validate parameter 'getLogger' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RemoveSourceRunner.Run(NuGet.Commands.RemoveSourceArgs,System.Func{NuGet.Common.ILogger})")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void RestoreArgs.ApplyStandardProperties(RestoreRequest request)', validate parameter 'request' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RestoreArgs.ApplyStandardProperties(NuGet.Commands.RestoreRequest)")]
Expand Down Expand Up @@ -197,7 +197,6 @@
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.ListCommandRunner.PrintPackages(NuGet.Commands.ListArgs,NuGet.Common.IEnumeratorAsync{NuGet.Protocol.Core.Types.IPackageSearchMetadata})~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.LockFileBuilder.EnsureUniqueLockFileLibraries(NuGet.ProjectModel.LockFile)~System.Collections.Generic.Dictionary{System.ValueTuple{System.String,NuGet.Versioning.NuGetVersion},NuGet.ProjectModel.LockFileLibrary}")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PackagesLockFileBuilder.GetPackagesLockFileVersion(NuGet.ProjectModel.LockFile)~System.Int32")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.ProjectRestoreCommand.ResolveDownloadDependenciesAsync(NuGet.DependencyResolver.RemoteWalkContext,NuGet.ProjectModel.TargetFrameworkInformation,System.Threading.CancellationToken)~System.Threading.Tasks.Task{NuGet.Commands.DownloadDependencyResolutionResult}")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RestoreCommand.ConcatAsString``1(System.Collections.Generic.IEnumerable{``0})~System.String")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.TrustedSignersCommandRunner.ValidateAndParseFingerprintAlgorithm(System.String)~NuGet.Common.HashAlgorithmName")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.TrustedSignersCommandRunner.ValidateAndParseV3ServiceIndexUrl(System.String)~System.Uri")]
Expand Down
Loading

0 comments on commit e68fb0f

Please sign in to comment.