Use less memory when generating a GuidUdi (#13090)
* Use less memory when generating a GuidUdi * PR feedback Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
This commit is contained in:
@@ -348,5 +348,10 @@ public static partial class Constants
|
|||||||
|
|
||||||
// TODO: return a list of built in types so we can use that to prevent deletion in the uI
|
// TODO: return a list of built in types so we can use that to prevent deletion in the uI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Udi
|
||||||
|
{
|
||||||
|
public const string Prefix = "umb://";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Core;
|
namespace Umbraco.Cms.Core;
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ public class GuidUdi : Udi
|
|||||||
/// <param name="entityType">The entity type part of the udi.</param>
|
/// <param name="entityType">The entity type part of the udi.</param>
|
||||||
/// <param name="guid">The guid part of the udi.</param>
|
/// <param name="guid">The guid part of the udi.</param>
|
||||||
public GuidUdi(string entityType, Guid guid)
|
public GuidUdi(string entityType, Guid guid)
|
||||||
: base(entityType, "umb://" + entityType + "/" + guid.ToString("N")) =>
|
: base(entityType, CreateStringValue(entityType, guid)) =>
|
||||||
Guid = guid;
|
Guid = guid;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -57,4 +58,19 @@ public class GuidUdi : Udi
|
|||||||
EnsureNotRoot();
|
EnsureNotRoot();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string CreateStringValue(ReadOnlySpan<char> entityType, Guid guid)
|
||||||
|
{
|
||||||
|
var startUdiLength = Constants.Conventions.Udi.Prefix.Length;
|
||||||
|
var outputSize = entityType.Length + startUdiLength + 32 + 1; //Based on the format umb://entityType/guid (32 = Guid N format, 1 = / between entityType and guid)
|
||||||
|
Span<char> output = stackalloc char[outputSize];
|
||||||
|
|
||||||
|
//Add all the values of the format to the output
|
||||||
|
Constants.Conventions.Udi.Prefix.CopyTo(output[..startUdiLength]);
|
||||||
|
entityType.CopyTo(output.Slice(startUdiLength, entityType.Length));
|
||||||
|
output[startUdiLength + entityType.Length] = '/';
|
||||||
|
guid.TryFormat(output.Slice(outputSize - 32, 32), out _, "N");
|
||||||
|
|
||||||
|
return new string(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class StringUdi : Udi
|
|||||||
/// <param name="entityType">The entity type part of the udi.</param>
|
/// <param name="entityType">The entity type part of the udi.</param>
|
||||||
/// <param name="id">The string id part of the udi.</param>
|
/// <param name="id">The string id part of the udi.</param>
|
||||||
public StringUdi(string entityType, string id)
|
public StringUdi(string entityType, string id)
|
||||||
: base(entityType, "umb://" + entityType + "/" + EscapeUriString(id)) =>
|
: base(entityType, Constants.Conventions.Udi.Prefix + entityType + "/" + EscapeUriString(id)) =>
|
||||||
Id = id;
|
Id = id;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
88
tests/Umbraco.Tests.Benchmarks/GuidUdiBenchmarks.cs
Normal file
88
tests/Umbraco.Tests.Benchmarks/GuidUdiBenchmarks.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using Umbraco.Cms.Core;
|
||||||
|
using Umbraco.Tests.Benchmarks.Config;
|
||||||
|
|
||||||
|
namespace Umbraco.Tests.Benchmarks
|
||||||
|
{
|
||||||
|
[QuickRunWithMemoryDiagnoserConfig]
|
||||||
|
public class GuidUdiBenchmarks
|
||||||
|
{
|
||||||
|
private readonly Guid _guid = Guid.NewGuid();
|
||||||
|
private readonly string _entityType = Constants.UdiEntityType.DocumentType;
|
||||||
|
|
||||||
|
[Benchmark(Baseline = true)]
|
||||||
|
public Udi CurrentInit()
|
||||||
|
{
|
||||||
|
return new OldGuidUdi(_entityType, _guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark()]
|
||||||
|
public Udi StringPolationInit()
|
||||||
|
{
|
||||||
|
return new StringPolationGuidUdi(_entityType, _guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public Udi NewInit()
|
||||||
|
{
|
||||||
|
return new NewGuidUdi(_entityType, _guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OldGuidUdi : Udi
|
||||||
|
{
|
||||||
|
public Guid Guid { get; }
|
||||||
|
|
||||||
|
public override bool IsRoot => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public OldGuidUdi(string entityType, Guid guid)
|
||||||
|
: base(entityType, "umb://" + entityType + "/" + guid.ToString("N")) =>
|
||||||
|
Guid = guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StringPolationGuidUdi : Udi
|
||||||
|
{
|
||||||
|
public Guid Guid { get; }
|
||||||
|
|
||||||
|
public override bool IsRoot => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public StringPolationGuidUdi(string entityType, Guid guid)
|
||||||
|
: base(entityType, $"umb://{entityType}/{guid:N}") =>
|
||||||
|
Guid = guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NewGuidUdi : Udi
|
||||||
|
{
|
||||||
|
public Guid Guid { get; }
|
||||||
|
|
||||||
|
public override bool IsRoot => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public NewGuidUdi(string entityType, Guid guid) : base(entityType, GetStringValue(entityType, guid))
|
||||||
|
{
|
||||||
|
Guid = guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetStringValue(ReadOnlySpan<char> entityType, Guid guid)
|
||||||
|
{
|
||||||
|
var startUdiLength = Constants.Conventions.Udi.Prefix.Length;
|
||||||
|
var outputSize = entityType.Length + startUdiLength + 32 + 1;
|
||||||
|
Span<char> output = stackalloc char[outputSize];
|
||||||
|
|
||||||
|
Constants.Conventions.Udi.Prefix.CopyTo(output[..startUdiLength]);
|
||||||
|
entityType.CopyTo(output.Slice(startUdiLength, entityType.Length));
|
||||||
|
output[startUdiLength + entityType.Length] = '/';
|
||||||
|
guid.TryFormat(output.Slice(outputSize - 32, 32), out _, "N");
|
||||||
|
|
||||||
|
return new string(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I think we are currently bottlenecked by the 'new Udi(string)' not accepting a span. If it did, then we wouldn't have to create a string mid creation and we would be able to cut back on the allocated data
|
||||||
|
//
|
||||||
|
//| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
|
||||||
|
//|------------------- |---------:|----------:|---------:|------:|--------:|-------:|----------:|
|
||||||
|
//| CurrentInit | 562.2 ns | 242.84 ns | 13.31 ns | 1.00 | 0.00 | 0.1113 | 352 B |
|
||||||
|
//| StringPolationInit | 589.3 ns | 530.08 ns | 29.06 ns | 1.05 | 0.07 | 0.0811 | 264 B |
|
||||||
|
//| NewInit | 533.6 ns | 40.55 ns | 2.22 ns | 0.95 | 0.03 | 0.0808 | 264 B |
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user