-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Optimize SlugService #10923
Optimize SlugService #10923
Conversation
The benchmark result shows that there's a slightly bit improvments, after the new modification:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you removed features which seems odd?
src/OrchardCore.Modules/OrchardCore.Liquid/Services/SlugService.cs
Outdated
Show resolved
Hide resolved
OK I went through the thing and the problem was that the ZString didn't didn't have BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=6.0.101
[Host] : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT
DefaultJob : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT
Original (main branch)
StringBuilder (this PR)
ZString (only StringBuilder changed to using ZString)
ZString v2
Code for ZString v2This was my experiment to show how to optimize the implementation, I might have introduced a bug with ToLowerInvariant change, but it should still be faster even without it. The output stayed the same though for given example. I don't know if this PR has the correct implementation either, just testing 😉 I've commented the code to show what's changed. General easy rule (when implicit usings aren't being used): If there's public class SlugService : ISlugService
{
private const char Hyphen = '-';
private const int MaxLength = 1000;
public string Slugify(string text)
{
if (String.IsNullOrEmpty(text))
{
return text;
}
// one thing to consider here, if text < 20 or some other threshold should the code do pre-check
// with foreach if all the chars are valid already (letter, digit or hyphen) and skip whole process?
// this is why it's important to study common input and optimize usual cases
// You need to have using directive in order for the pooling to work
using var slug = ZString.CreateStringBuilder();
var appendHyphen = false;
// removed ToLowerInvariant from here, I'm unsure if I break something here
var normalizedText = text.Normalize(NormalizationForm.FormKD);
for (var i = 0; i < normalizedText.Length; i++)
{
// do ToLowerInvariant for each char so I don't need to create a new string above
var currentChar = Char.ToLowerInvariant(normalizedText[i]);
if (CharUnicodeInfo.GetUnicodeCategory(currentChar) == UnicodeCategory.NonSpacingMark)
{
continue;
}
if (Char.IsLetterOrDigit(currentChar))
{
slug.Append(currentChar);
appendHyphen = true;
}
// split the Contains to two different branches, you shouldn't use LINQ method Contains for an
// array, it's really bad for performance
else if (currentChar is Hyphen)
{
if (appendHyphen && i != normalizedText.Length - 1)
{
slug.Append(currentChar);
appendHyphen = false;
}
}
// fast char equality
else if (currentChar is '_' or '~')
{
slug.Append(currentChar);
}
else
{
if (appendHyphen)
{
slug.Append(Hyphen);
appendHyphen = false;
}
}
}
// old code was doing a ToString() and then a Substring (two string allocations)
// we can get a span from the builder and construct new string based on that
return new string(slug.AsSpan()[..Math.Min(slug.Length, MaxLength)]).Normalize(NormalizationForm.FormC);
}
} |
Thanks a lot @lahma, I need to test your changes with the suggestion that you did in a BiG string, could you please let me know the CLI command that you use to make sure I don't get another results |
I just do |
I though you mark the |
How this else if (currentChar is '_' or '~') working with you, are you changed the lang version? |
I got the below results using a normal or condition
|
Addresses #10922