') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Expand small value sets to all case permutations in SearchValues<string> by MihaZupan · Pull Request #98902 · dotnet/runtime · GitHub
Skip to content

Expand small value sets to all case permutations in SearchValues<string> - #98902

Merged
MihaZupan merged 2 commits into
dotnet:mainfrom
MihaZupan:searchvalues-string-shortCasePermutations
Mar 2, 2024
Merged

Expand small value sets to all case permutations in SearchValues<string>#98902
MihaZupan merged 2 commits into
dotnet:mainfrom
MihaZupan:searchvalues-string-shortCasePermutations

Conversation

@MihaZupan

@MihaZupanMihaZupan commented Feb 25, 2024

Copy link
Copy Markdown
Member

Implements #98791 (comment)

If we have a set of values like ["ab", "c!"], we can expand it to ["ab", "Ab" "aB", "AB", "c!", "C!"] and switch to case-sensitive searching.
As long as we're making use of buckets that would otherwise have been empty, this is going to be an improvement as the prefix search loop is a bit simpler due to not needing to deal with casing. In the below benchmark, it means eliminating this step from the loop.

This optimization is a bit niche (unlikely to be applicable often), but it's really cheap to check whether it could apply, and can help cases with many non-letter characters in their prefixes.

publicclassIgnoreCaseToOrdinal{privatestaticreadonlySearchValues<string>s_values=SearchValues.Create(["ab","c!"],StringComparison.OrdinalIgnoreCase);privatereadonlystring_text=new('\n',1000);[Benchmark]publicintIndexOfAny()=>_text.AsSpan().IndexOfAny(s_values);}
MethodToolchainMeanErrorRatio
IndexOfAny\main\corerun.exe82.42 ns0.596 ns1.00
IndexOfAny\pr\corerun.exe67.83 ns0.369 ns0.82

@MihaZupanMihaZupan added this to the 9.0.0 milestone Feb 25, 2024
@MihaZupanMihaZupan self-assigned this Feb 25, 2024
@ghost

Copy link
Copy Markdown

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

Issue Details

Implements #98791 (comment)

If we have a set of values like ["ab", "c!"], we can expand it to ["ab", "Ab" "aB", "AB", "c!", "C!"] and switch to case-sensitive searching.
As long as we're making use of buckets that would otherwise have been empty, this is going to be an improvement as both the prefix search loop and the verification steps are a bit simpler due to not needing to deal with casing.

This optimization is a bit niche (unlikely to be applicable often), but it's really cheap to check whether it could apply, and can help cases with many non-letter characters in their prefixes.

publicclassIgnoreCaseToOrdinal{privatestaticreadonlySearchValues<string>s_values=SearchValues.Create(["ab","c!"],StringComparison.OrdinalIgnoreCase);privatereadonlystring_text=new('\n',1000);[Benchmark]publicintIndexOfAny()=>_text.AsSpan().IndexOfAny(s_values);}
MethodToolchainMeanErrorRatio
IndexOfAny\main\corerun.exe82.42 ns0.596 ns1.00
IndexOfAny\pr\corerun.exe67.83 ns0.369 ns0.82
Author:MihaZupan
Assignees:MihaZupan
Labels:

area-System.Buffers

Milestone:9.0.0

@MihaZupan
MihaZupanforce-pushed the searchvalues-string-shortCasePermutations branch from 54e8e3d to ac5966cCompareMarch 2, 2024 03:13
@MihaZupan
MihaZupan merged commit 8aff565 into dotnet:mainMar 2, 2024
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 2, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MihaZupan@stephentoub