Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Performance benchmark references #15182

Closed
MikeAlhayek opened this issue Jan 27, 2024 · 3 comments
Closed

Performance benchmark references #15182

MikeAlhayek opened this issue Jan 27, 2024 · 3 comments

Comments

@MikeAlhayek
Copy link
Member

MikeAlhayek commented Jan 27, 2024

Use string interpolation when concatenation few strings.

A benchmark on a medium size string

image

That output of the following benchmark

[MemoryDiagnoser]
public class MediumStringConcatenationBenchmarks
{
    private const string Phrase1 = "In the vast expanse of the cosmos, stars dance in celestial choreography";
    private const string Phrase2 = "weaving tales of cosmic marvels, while on Earth, the tapestry of life unfolds";
    private const string Phrase3 = "intertwining moments of joy, sorrow, and discovery, echoing the symphony of existence itself.";
    private const string Separator = ", ";

    [Benchmark(Baseline = true)]
    public string StringConcatenation()
    {
        var result = string.Empty;

        result += Phrase1;
        result += Separator;
        result += Phrase2;
        result += Separator;
        result += Phrase3;

        return result;
    }

    [Benchmark]
    public string StringConcatenationOneLine()
    {
        var result = Phrase1 + Separator + Phrase2 + Separator + Phrase3;

        return result;
    }

    [Benchmark]
    public string StringInterpolation()
    {
        var result = $"{Phrase1}{Separator}{Phrase2}{Separator}{Phrase3}";

        return result;
    }

    [Benchmark]
    public string StringBuilder()
    {
        var builder = new StringBuilder();
        builder.Append(Phrase1);
        builder.Append(Separator);
        builder.Append(Phrase2);
        builder.Append(Separator);
        builder.Append(Phrase3);

        return builder.ToString();
    }
}

But, when we are doing many concatenations, the results are not as good.

image

Here is the benchmark I used

[MemoryDiagnoser]
public class SmallStringConcatenationBenchmarks
{
    private const string Phrase1 = "One";
    private const string Phrase2 = "Two";
    private const string Phrase3 = "Three";
    private const string Phrase4 = "Four";
    private const string Phrase5 = "Five";
    private const string Phrase6 = "Six";
    private const string Phrase7 = "Seven";
    private const string Phrase8 = "Eight";
    private const string Phrase9 = "Nine";
    private const string Phrase10 = "Ten";
    private const char Separator = ' ';

    [Benchmark(Baseline = true)]
    public string StringConcatenation()
    {
        var result = string.Empty;

        result += Phrase1;
        result += Separator;
        result += Phrase2;
        result += Separator;
        result += Phrase3;
        result += Separator;
        result += Phrase4;
        result += Separator;
        result += Phrase5;
        result += Separator;
        result += Phrase6;
        result += Separator;
        result += Phrase7;
        result += Separator;
        result += Phrase8;
        result += Separator;
        result += Phrase9;
        result += Separator;
        result += Phrase10;

        return result;
    }

    [Benchmark]
    public string StringConcatenationOneLine()
    {
        var result = Phrase1 + Separator
            + Phrase2 + Separator
            + Phrase3 + Separator
            + Phrase3 + Separator
            + Phrase4 + Separator
            + Phrase5 + Separator
            + Phrase6 + Separator
            + Phrase7 + Separator
            + Phrase8 + Separator
            + Phrase9 + Separator
            + Phrase10;

        return result;
    }

    [Benchmark]
    public string StringInterpolation()
    {
        var result = $"{Phrase1}{Separator}{Phrase2}{Separator}{Phrase3}{Separator}{Phrase4}{Separator}{Phrase5}{Separator}{Phrase6}{Separator}{Phrase7}{Separator}{Phrase8}{Separator}{Phrase9}{Separator}{Phrase10}";

        return result;
    }

    [Benchmark]
    public string StringBuilder()
    {
        var builder = new StringBuilder();
        builder.Append(Phrase1);
        builder.Append(Separator);
        builder.Append(Phrase2);
        builder.Append(Separator);
        builder.Append(Phrase3);
        builder.Append(Separator);
        builder.Append(Phrase4);
        builder.Append(Separator);
        builder.Append(Phrase5);
        builder.Append(Separator);
        builder.Append(Phrase6);
        builder.Append(Separator);
        builder.Append(Phrase7);
        builder.Append(Separator);
        builder.Append(Phrase8);
        builder.Append(Separator);
        builder.Append(Phrase9);
        builder.Append(Separator);
        builder.Append(Phrase10);

        return builder.ToString();
    }
}

/cc @deanmarcussen

@MikeAlhayek
Copy link
Member Author

MikeAlhayek commented Feb 2, 2024

Use IEnumerable when possible and avoid having to convert with .ToList() or .ToArray() if possible.

image

Using List is faster but comes at the cost of allocation more as you expected. I'll modify the code to no use the list.

@MikeAlhayek
Copy link
Member Author

Use .ToArray() over .ToList() when possible

As you can see from the benchmark below, .ToArray() is slightly faster that .ToList() with slightly less allocations.
image

@sebastienros we had the opposite in mind.

@MikeAlhayek MikeAlhayek changed the title Use string interpolation when concatenation few strings. Performance Benchmark references Feb 2, 2024
@MikeAlhayek MikeAlhayek changed the title Performance Benchmark references Performance benchmark references Feb 2, 2024
@MikeAlhayek
Copy link
Member Author

Creating the same object very often can be costly.

In places like INavigationProvider where we call the handler on every single request, creating the same object over and over can be expensive.

As simple as one constant variable saves 12 mb in memory allocation over 100k requests.

image

For example, the below AdminMenu class, we store the RouteValues in a const variable which is created only one during the entire life of the app. This object will be used by every single request (from any tenant). This small change will reduce lots of allocations over the life of the app. Make sure you do not store client specific data in a const variable!

public class AdminMenu : INavigationProvider
{
    private static readonly RouteValueDictionary _routeValues = new()
    {
        { "area", "OrchardCore.Settings" },
        { "groupId", AdminSiteSettingsDisplayDriver.GroupId },
    };

    protected readonly IStringLocalizer S;

    public AdminMenu(IStringLocalizer<AdminMenu> stringLocalizer)
    {
        S = stringLocalizer;
    }

    public Task BuildNavigationAsync(string name, NavigationBuilder builder)
    {
        if (!NavigationHelper.IsAdminMenu(name))
        {
            return Task.CompletedTask;
        }

        builder
            .Add(S["Configuration"], configuration => configuration
                .Add(S["Settings"], settings => settings
                    .Add(S["Admin"], S["Admin"].PrefixPosition(), admin => admin
                        .AddClass("admin")
                        .Id("admin")
                        .Action("Index", "Admin", _routeValues)
                        .Permission(PermissionsAdminSettings.ManageAdminSettings)
                        .LocalNav()
                    )
                )
            );

        return Task.CompletedTask;
    }
}

@OrchardCMS OrchardCMS locked and limited conversation to collaborators Feb 2, 2024
@MikeAlhayek MikeAlhayek converted this issue into discussion #15240 Feb 2, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant