Skip to content

Optimize Guid formatting#125499

Open
pentp wants to merge 1 commit intodotnet:mainfrom
pentp:guid-format
Open

Optimize Guid formatting#125499
pentp wants to merge 1 commit intodotnet:mainfrom
pentp:guid-format

Conversation

@pentp
Copy link
Contributor

@pentp pentp commented Mar 12, 2026

Streamline the default path and use AVX-512 VBMI+VL for even faster formatting.

Method Mean (main) Error Mean (PR) Error
GuidToString 15.938 ns 0.345 ns 14.087 ns 0.320 ns
TryFormat 1.679 ns 0.043 ns 0.845 ns 0.021 ns
Utf8TryFormat 1.926 ns 0.057 ns 0.461 ns 0.025 ns
Benchmark code

public class Bench
{
    private Guid _guid = new("a8a110d5-fc49-43c5-bf46-802db8f843ff");
    private readonly byte[] _buffer = new byte[100];
    private readonly char[] _chars = new char[100];

    [Benchmark]
    public string GuidToString() => _guid.ToString();

    [Benchmark]
    public bool TryFormat() => _guid.TryFormat(_chars, out _);

    [Benchmark]
    public bool Utf8TryFormat() => Utf8Formatter.TryFormat(_guid, _buffer, out _);
}

Copilot AI review requested due to automatic review settings March 12, 2026 15:43
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Mar 12, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Optimizes Guid formatting by streamlining the default ToString path and adding an AVX-512 VBMI+VL vectorized implementation for fast hex formatting, with related tweaks in Utf8Formatter for Guid.

Changes:

  • Reworks Guid.ToString / TryFormatCore to use a flags-based fast path and adds an AVX-512 VBMI+VL implementation for D/N/B/P formats.
  • Refactors Guid comparison operators / CompareTo to use unsigned comparisons for _b / _c and a big-endian ulong compare for the trailing bytes.
  • Updates Utf8Formatter.TryFormat(Guid, ...) to compute and pass the new Guid formatting flags directly.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/libraries/System.Private.CoreLib/src/System/Guid.cs Adds new vectorized formatting path and adjusts comparison logic and ToString fast path to use flags-based formatting.
src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs Updates Utf8Formatter’s Guid formatting to use flags directly and streamline default-format handling.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +31 to +36
int flags = format.Symbol;
if (flags is '\0' or 'D')
{
case 'D': // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn
flags = 36 + Guid.TryFormatFlags_UseDashes;
break;

case 'B': // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}
flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_CurlyBraces;
break;
// nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn
flags = 36 + Guid.TryFormatFlags_UseDashes;
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utf8Formatter.TryFormat(Guid, ..., StandardFormat) now treats format.Symbol == '\0' as the default 'D' format without also checking that the StandardFormat is actually default (i.e., Precision == 0). This changes behavior for values like new StandardFormat('\0', 1), which previously would have thrown FormatException but will now format as 'D'.

Use format.IsDefault (or replicate FormattingHelpers.GetSymbolOrDefault logic) so that only the true default StandardFormat maps to 'D'.

Copilot uses AI. Check for mistakes.
Comment on lines +1127 to +1138
if ((ushort)value._b != (ushort)_b)
{
return GetResult(_d, value._d);
return ((ushort)_b).CompareTo((ushort)value._b);
}

if (value._e != _e)
if ((ushort)value._c != (ushort)_c)
{
return GetResult(_e, value._e);
return ((ushort)_c).CompareTo((ushort)value._c);
}

if (value._f != _f)
{
return GetResult(_f, value._f);
}

if (value._g != _g)
{
return GetResult(_g, value._g);
}

if (value._h != _h)
{
return GetResult(_h, value._h);
}

if (value._i != _i)
{
return GetResult(_i, value._i);
}

if (value._j != _j)
{
return GetResult(_j, value._j);
}

if (value._k != _k)
{
return GetResult(_k, value._k);
}

return 0;
return BinaryPrimitives.ReadUInt64BigEndian(MemoryMarshal.CreateReadOnlySpan(in _d, 8))
.CompareTo(BinaryPrimitives.ReadUInt64BigEndian(MemoryMarshal.CreateReadOnlySpan(in value._d, 8)));
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison logic now treats _b / _c as ushort (and the tail as a big-endian ulong). This changes ordering vs the previous implementation for GUIDs where _b or _c have the high bit set (e.g., values corresponding to negative shorts).

There are GuidTests.CompareTo_TestData cases for _b and _c, but they only cover small positive values. Please add test cases covering boundary values (e.g., short.MinValue, -1, 0x8000) to lock in the intended ordering and ensure this behavior change is validated.

Copilot uses AI. Check for mistakes.
@jkotas jkotas added the tenet-performance Performance related issue label Mar 12, 2026
@tannergooding
Copy link
Member

This seems like a lot of churn for what amounts to about a nanosecond of savings and which will only light up on a small portion of available hardware today.

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

Labels

area-System.Runtime community-contribution Indicates that the PR has been added by a community member tenet-performance Performance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants