Skip to content

Commit

Permalink
Reduce allocations for version / versionranges (#5862)
Browse files Browse the repository at this point in the history
  • Loading branch information
ToddGrun authored Jul 1, 2024
1 parent cc7c617 commit 15ad272
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 2 deletions.
24 changes: 24 additions & 0 deletions src/NuGet.Core/NuGet.Versioning/NuGetVersionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ namespace NuGet.Versioning
{
public partial class NuGetVersion
{
// If dictionary exceeds this size, ParsedNuGetVersionsMapping will be cleared.
private const int ParsedNuGetVersionsMappingMaxEntries = 500;

// Cached mappings from string => NuGetVersion. On cache hit, avoids allocations during TryParse.
private static Dictionary<string, NuGetVersion> ParsedNuGetVersionsMapping = new Dictionary<string, NuGetVersion>(ParsedNuGetVersionsMappingMaxEntries);

/// <summary>
/// Creates a NuGetVersion from a string representing the semantic version.
/// </summary>
Expand Down Expand Up @@ -39,6 +45,14 @@ public static bool TryParse(string? value, [NotNullWhen(true)] out NuGetVersion?

if (value != null)
{
lock (ParsedNuGetVersionsMapping)
{
if (ParsedNuGetVersionsMapping.TryGetValue(value, out version))
{
return true;
}
}

Version? systemVersion;

// trim the value before passing it in since we not strict here
Expand Down Expand Up @@ -87,6 +101,16 @@ public static bool TryParse(string? value, [NotNullWhen(true)] out NuGetVersion?
metadata: buildMetadata ?? string.Empty,
originalVersion: originalVersion);

lock (ParsedNuGetVersionsMapping)
{
if (ParsedNuGetVersionsMapping.Count >= ParsedNuGetVersionsMappingMaxEntries)
{
ParsedNuGetVersionsMapping.Clear();
}

ParsedNuGetVersionsMapping[value] = version;
}

return true;
}
}
Expand Down
41 changes: 39 additions & 2 deletions src/NuGet.Core/NuGet.Versioning/VersionRangeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public partial class VersionRange
{
// Static factory methods for creating version range objects.

// If dictionary exceeds this size, ParsedVersionRangeMapping will be cleared.
private const int ParsedVersionRangeMappingMaxEntries = 500;

// Cached mappings from (string value, bool allowFloating) => VersionRange. On cache hit, avoids allocations during TryParse.
private static Dictionary<(string, bool), VersionRange> ParsedVersionRangeMapping = new Dictionary<(string, bool), VersionRange>(ParsedVersionRangeMappingMaxEntries);

/// <summary>
/// A range that accepts all versions, prerelease and stable.
/// </summary>
Expand Down Expand Up @@ -94,20 +100,36 @@ public static bool TryParse(string value, bool allowFloating, [NotNullWhen(true)
{
versionRange = null;

var trimmedValue = value?.Trim();
if (value is null)
{
return false;
}

lock (ParsedVersionRangeMapping)
{
if (ParsedVersionRangeMapping.TryGetValue((value, allowFloating), out versionRange))
{
return true;
}
}

var trimmedValue = value.Trim();
if (string.IsNullOrEmpty(trimmedValue))
{
return false;
}

var charArray = trimmedValue!.ToCharArray();
var charArray = trimmedValue.ToCharArray();

// * is the only 1 char range
if (allowFloating
&& charArray.Length == 1
&& charArray[0] == '*')
{
versionRange = new VersionRange(new NuGetVersion(0, 0, 0), true, null, true, FloatRange.Parse(trimmedValue), originalString: value);

UpdateCachedVersionRange(value, allowFloating, versionRange);

return true;
}

Expand Down Expand Up @@ -267,9 +289,24 @@ public static bool TryParse(string value, bool allowFloating, [NotNullWhen(true)
floatRange: floatRange,
originalString: value);

UpdateCachedVersionRange(value, allowFloating, versionRange);

return true;
}

private static void UpdateCachedVersionRange(string value, bool allowFloating, VersionRange versionRange)
{
lock (ParsedVersionRangeMapping)
{
if (ParsedVersionRangeMapping.Count >= ParsedVersionRangeMappingMaxEntries)
{
ParsedVersionRangeMapping.Clear();
}

ParsedVersionRangeMapping[(value, allowFloating)] = versionRange;
}
}

/// <summary>
/// Returns the smallest range that includes all given versions.
/// </summary>
Expand Down

0 comments on commit 15ad272

Please sign in to comment.