Skip to content

Use IndexOfAnyWhiteSpace in Guid.EatAllWhitespace#127123

Merged
MihaZupan merged 2 commits intomainfrom
copilot/replace-char-is-whitespace-uses
Apr 20, 2026
Merged

Use IndexOfAnyWhiteSpace in Guid.EatAllWhitespace#127123
MihaZupan merged 2 commits intomainfrom
copilot/replace-char-is-whitespace-uses

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

Description

Replace the manual for loop scanning for whitespace in Guid.EatAllWhitespace with the new vectorized IndexOfAnyWhiteSpace helper added in #111439.

-int i;
-for (i = 0; i < charSpan.Length && !char.IsWhiteSpace(charSpan[i]); i++) ;
-if (i == charSpan.Length)
+int i = charSpan.IndexOfAnyWhiteSpace();
+if (i < 0)
Method Toolchain Mean Error
ParseExactX_NoWhitespace main 87.085 ns 0.4839 ns
ParseExactX_NoWhitespace PR #127123 60.859 ns 0.0391 ns
ParseExactX_WithWhitespace main 107.613 ns 0.2588 ns
ParseExactX_WithWhitespace PR #127123 113.060 ns 0.0679 ns

Copilot AI and others added 2 commits April 19, 2026 21:28
…helpers

Replace manual for-loops that scan for whitespace/non-whitespace characters
with calls to IndexOfAnyWhiteSpace, IndexOfAnyExceptWhiteSpace,
LastIndexOfAnyExceptWhiteSpace, which use vectorized SearchValues internally.

Changed:
- MemoryExtensions.IsWhiteSpace
- MemoryExtensions.Trim/TrimStart/TrimEnd (ReadOnlySpan and Span overloads)
- MemoryExtensions.ClampStart/ClampEnd
- MemoryExtensions.TrimSplitEntry
- String.IsNullOrWhiteSpace
- String.TrimWhiteSpaceHelper
- String.MakeSeparatorListAny (whitespace case)
- Guid.EatAllWhitespace
- StringSegment.TrimStart/TrimEnd

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/41ef7392-01aa-4cd7-8f27-9439896279e3

Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
Keep only the IndexOfAnyWhiteSpace change in Guid.EatAllWhitespace,
reverting all other replacements as requested.

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/2e3e7d3b-f6ce-480c-a749-c51bd5124c9f

Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 19, 2026 22:23
Copilot AI review requested due to automatic review settings April 19, 2026 22:23
@MihaZupan MihaZupan added this to the 11.0.0 milestone Apr 19, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

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

@MihaZupan
Copy link
Copy Markdown
Member

This seems like one of the only places where using a vectorized helper should be a perf improvement.
Other places like Trim commonly deal with very early exits, whereas the fast path in this case is when there are no matches.

@MihaZupan
Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to test the impact of this change to Guid parsing

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 19, 2026

@copilot Use the benchmark skill to test the impact of this change to Guid parsing

Note

This comment was generated by Copilot.

@EgorBot -linux_amd -osx_arm64

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);

[MemoryDiagnoser]
public class Bench
{
    private string _noWhitespace;
    private string _withWhitespace;
    private string _dFormat;

    [GlobalSetup]
    public void Setup()
    {
        var guid = Guid.NewGuid();
        // X format: {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
        _noWhitespace = guid.ToString("X");
        // Same but with whitespace scattered throughout (EatAllWhitespace must strip it)
        _withWhitespace = "  " + _noWhitespace.Insert(10, " ").Insert(20, "  ") + "  ";
        // D format for comparison (doesn't use EatAllWhitespace)
        _dFormat = guid.ToString("D");
    }

    [Benchmark]
    public Guid ParseExactX_NoWhitespace() => Guid.ParseExact(_noWhitespace, "X");

    [Benchmark]
    public Guid ParseExactX_WithWhitespace() => Guid.ParseExact(_withWhitespace, "X");

    [Benchmark(Baseline = true)]
    public Guid ParseExactD() => Guid.ParseExact(_dFormat, "D");
}

Copilot AI requested a review from MihaZupan April 19, 2026 22:33
@MihaZupan MihaZupan marked this pull request as ready for review April 19, 2026 23:08
Copilot AI review requested due to automatic review settings April 19, 2026 23:08
Copy link
Copy Markdown
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

This PR updates Guid.EatAllWhitespace to use the newer vectorized MemoryExtensions.IndexOfAnyWhiteSpace helper (introduced in #111439) instead of a manual loop when scanning ReadOnlySpan<char> for the first whitespace.

Changes:

  • Replace manual whitespace scan loop with IndexOfAnyWhiteSpace() in the char fast-path.
  • Update the “no whitespace found” check to use i < 0 (the IndexOf* not-found convention).

@EgorBo
Copy link
Copy Markdown
Member

EgorBo commented Apr 20, 2026

@copilot, benchmark results are ready:

Please analyze the results and act accordingly.
NOTE: some benchmarks may be flaky or bi-modal, so use your judgment when interpreting small differences.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 20, 2026

@copilot, benchmark results are ready:

macOS ARM64 (Apple M4) results:

Method main PR Change
ParseExactX_NoWhitespace 87.09 ns 60.86 ns -30.1%
ParseExactX_WithWhitespace 107.61 ns 113.06 ns +5.1%
ParseExactD (baseline) 8.04 ns 8.11 ns unchanged

The fast path (no whitespace) sees a ~30% improvement from vectorized IndexOfAnyWhiteSpace, which aligns with @MihaZupan's observation that this is one of the places where vectorization helps since the common case is scanning the entire input without finding any whitespace.

The WithWhitespace case shows a small ~5% regression which is marginal and expected — the vectorized search has slightly more setup overhead that doesn't pay off when whitespace is found early.

The linux_amd run timed out (infrastructure issue, not related to the change).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • bot.egorbo.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from EgorBo April 20, 2026 00:42
@MihaZupan MihaZupan merged commit 14247f4 into main Apr 20, 2026
155 of 163 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants