Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Vectorize SearchValues.Create min/max scan using Vector128 - #124667

Closed
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create
Closed

Vectorize SearchValues.Create min/max scan using Vector128#124667
MihaZupan with Copilot wants to merge 3 commits into
mainfrom
copilot/vectorize-searchvalues-create

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

SearchValues.Create walks input values to find min/max (used to select the optimal search implementation) using a scalar loop. This vectorizes that scan to process multiple elements per iteration.

Description

SearchValues.cs — vectorized TryGetSingleRange

Added a GetMinMax<T> helper that computes min and max in a single vectorized pass:

  • Uses Vector128.Min / Vector128.Max to process Vector128<T>.Count elements per iteration (8 ushorts or 16 bytes at once)
  • Handles the tail with a final overlapping vector load
  • Horizontal reduction is a scalar loop over the ≤16 lane results
  • char is not a valid Vector128 element type — the span is cast to ReadOnlySpan<ushort>, and the results are converted back with Unsafe.BitCast<ushort, T>
  • Falls back to a scalar loop when SIMD is unavailable or input is smaller than one vector
// Before: one element at a timeforeach(Tvalueinvalues){min=T.Min(min,value);max=T.Max(max,value);}// After: Vector128<T>.Count elements per iteration, min and max in a single passdo{Vector128<T>v=Vector128.LoadUnsafe(refcurrent);vMin=Vector128.Min(vMin,v);vMax=Vector128.Max(vMax,v);current=refUnsafe.Add(refcurrent,Vector128<T>.Count);}while(Unsafe.IsAddressLessThan(refcurrent,reflastVectorStart));// + overlapping tail + horizontal reduction

Testing

Performance was validated using @EgorBot on Linux/AMD64 and macOS/ARM64 with SearchValues.Create on the 64-character Base64 alphabet as the benchmark input.

Risk

Low — GetMinMax is only called during SearchValues.Create, not on hot search paths. The scalar fallback is identical to the original code.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 20, 2026
@MihaZupanMihaZupan added area-System.Buffers and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Feb 20, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan added the NO-REVIEW Experimental/testing PR, do NOT review it label Feb 20, 2026
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title [WIP] Vectorize min and max computation in SearchValues.CreateVectorize SearchValues.Create min/max scan and ASCII bitmap constructionFeb 20, 2026
CopilotAI requested a review from MihaZupanFebruary 20, 2026 20:12
Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
CopilotAI changed the title Vectorize SearchValues.Create min/max scan and ASCII bitmap constructionVectorize SearchValues.Create min/max scan using Vector128Feb 20, 2026
@MihaZupan

Copy link
Copy Markdown
Member

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Use the benchmark skill to check the performance difference of SearchValues.Create for chars by using the Base64 alphabet as the input.

@EgorBot -linux_amd -osx_arm64

usingSystem.Buffers;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);publicclassBench{// Standard Base64 alphabet (64 chars)privateconststringBase64Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[Benchmark]publicSearchValues<char>Create_Base64Alphabet()=>SearchValues.Create(Base64Chars);}

@danmoseley

Copy link
Copy Markdown
Contributor

EgorBot Benchmark Analysis

Linux AMD64 — AMD EPYC 9V74 (Genoa)

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667133.1 ns1.00
Create_Base64Alphabetmain314.3 ns2.36

macOS ARM64 — Apple M1

MethodToolchainMeanRatio
Create_Base64AlphabetPR #124667130.7 ns1.00
Create_Base64Alphabetmain262.7 ns2.01

Summary: The vectorized Vector128 min/max scan is a clear win on both platforms — ~2.4x faster on AMD64 and ~2.0x faster on ARM64 for SearchValues.Create with the 64-char Base64 alphabet. The PR brings creation time down from ~260-314 ns to ~131-133 ns, which is a solid improvement for a low-risk change (only affects the Create path, not hot search paths).

Full results from EgorBot

@jkotas
jkotas deleted the copilot/vectorize-searchvalues-create branch March 1, 2026 18:32
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.BuffersNO-REVIEWExperimental/testing PR, do NOT review it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@MihaZupan@danmoseley