Skip to content

Performance: Add Identifier helper class#9493

Merged
henon merged 4 commits intoMudBlazor:devfrom
xC0dex:performance/identifier
Jul 28, 2024
Merged

Performance: Add Identifier helper class#9493
henon merged 4 commits intoMudBlazor:devfrom
xC0dex:performance/identifier

Conversation

@xC0dex
Copy link
Member

@xC0dex xC0dex commented Jul 24, 2024

Hey,

this PR adds a new helper class Identifier which provides a Create method to create a unique string with fewer allocations than the current approach.

// Current
private string _elementId = "checkbox" + Guid.NewGuid().ToString().Substring(0, 8);

// After
private string _elementId = Identifier.Create("checkbox");

Benchmark:

Method Mean Error StdDev Ratio Allocated Alloc Ratio
Guid_SubString 85.88 ns 0.717 ns 0.635 ns 1.00 192 B 1.00
Identifier_Create 78.02 ns 0.371 ns 0.347 ns 0.91 56 B 0.29

Currently, I only updated the MudCheckBox component. Depending on your feedback, I would update all usages.

How Has This Been Tested?

Added tests.

Type of Changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • The PR is submitted to the correct branch (dev).
  • My code follows the code style of this project.
  • I've added relevant tests.

@github-actions github-actions bot added enhancement Adds a new feature or enhances existing functionality (not fixing a defect) in the main library PR: needs review labels Jul 24, 2024
@xC0dex xC0dex changed the title Performance/identifier Performance: Add Identifier class Jul 24, 2024
@xC0dex xC0dex changed the title Performance: Add Identifier class Performance: Add Identifier helper class Jul 24, 2024
@xC0dex
Copy link
Member Author

xC0dex commented Jul 24, 2024

Hey @henon @ScarletKuro what's your opinion on this idea?

@codecov
Copy link

codecov bot commented Jul 24, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 90.57%. Comparing base (28bc599) to head (5b1b285).
Report is 385 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #9493      +/-   ##
==========================================
+ Coverage   89.82%   90.57%   +0.74%     
==========================================
  Files         412      407       -5     
  Lines       11878    12715     +837     
  Branches     2364     2463      +99     
==========================================
+ Hits        10670    11516     +846     
+ Misses        681      638      -43     
- Partials      527      561      +34     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@xC0dex xC0dex added performance Time/memory/CPU/allocation performance characteristics and removed enhancement Adds a new feature or enhances existing functionality (not fixing a defect) in the main library labels Jul 24, 2024
@ScarletKuro
Copy link
Member

ScarletKuro commented Jul 25, 2024

Wouldn't something like this be better if we chase performance / allocation?

private const string Chars = "abcdefghijklmnopqrstuvwxyz0123456789";
private const int RandomStringLength = 8;

internal static string Create(ReadOnlySpan<char> prefix)
{
    Span<char> identifierSpan = stackalloc char[prefix.Length + RandomStringLength];
    prefix.CopyTo(identifierSpan);

    Span<byte> randomBytes = stackalloc byte[RandomStringLength];
    RandomNumberGenerator.Fill(randomBytes);

    for (int i = 0; i < RandomStringLength; i++)
    {
        identifierSpan[prefix.Length + i] = Chars[randomBytes[i] % Chars.Length];
    }

    return identifierSpan.ToString();
}

I feel like GUID is too heavy just to get 8 chars from a 32-char GUID. I don't think we need super uniqueness, and RandomNumberGenerator is a true cryptographic random generator.

@henon
Copy link
Contributor

henon commented Jul 25, 2024

Sure, would suffice. I do something similar in my apps actually.

@xC0dex
Copy link
Member Author

xC0dex commented Jul 25, 2024

You are right. I didn't think about that, great idea. However, the allocations on the heap are unchanged, but it's faster. Now I have the following code, but "only" the Random instance:

private const string Chars = "abcdefghijklmnopqrstuvwxyz0123456789";
private const int CharsLength = 35;
private const int RandomStringLength = 8;

internal static string Create(ReadOnlySpan<char> prefix)
{
    Span<char> identifierSpan = stackalloc char[prefix.Length + RandomStringLength];
    prefix.CopyTo(identifierSpan);

    for (var i = 0; i < RandomStringLength; i++)
    {
        var index = Random.Shared.Next(CharsLength);
        identifierSpan[prefix.Length + i] = Chars[index];
    }

    return identifierSpan.ToString();
}

with these results:

Method Mean Error StdDev Ratio Allocated Alloc Ratio
SubString 88.83 ns 1.344 ns 1.192 ns 1.00 192 B 1.00
Create 66.75 ns 1.304 ns 1.220 ns 0.75 56 B 0.29

@xC0dex
Copy link
Member Author

xC0dex commented Jul 25, 2024

I would update all references where the identifier can be used 👍

@xC0dex
Copy link
Member Author

xC0dex commented Jul 26, 2024

I have three notes:

  • I'm not sure about the prefix in MudComponentBase. Currently, it's mudinput but maybe mud or the default prefix would fit better.
  • Should the new identifier class be public? Then others could use it as well.
  • Are you ok with the name of the class and the method?

@ScarletKuro
Copy link
Member

  • I'm not sure about the prefix in MudComponentBase. Currently, it's mudinput but maybe mud or the default prefix would fit better.

As long as it doesn't break any unit tests, etc., then I'm okay with the rename. The shorter, the better.

  • Should the new identifier class be public? Then others could use it as well.

No.

  • Are you ok with the name of the class and the method?

Yes.

@xC0dex
Copy link
Member Author

xC0dex commented Jul 26, 2024

As long as it doesn't break any unit tests

Okay, they do break. I'll leave it as it is. PR is ready 👍.

@henon henon merged commit a05653f into MudBlazor:dev Jul 28, 2024
@henon
Copy link
Contributor

henon commented Jul 28, 2024

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Time/memory/CPU/allocation performance characteristics

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants