') + ')', '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); } })(); })(); Use IndexOfAny in StreamReader ReadLine by yesmey · Pull Request #75646 · dotnet/runtime · GitHub
Skip to content

Use IndexOfAny in StreamReader ReadLine - #75646

Closed
yesmey wants to merge 1 commit into
dotnet:mainfrom
yesmey:streamreadline
Closed

Use IndexOfAny in StreamReader ReadLine#75646
yesmey wants to merge 1 commit into
dotnet:mainfrom
yesmey:streamreadline

Conversation

@yesmey

Copy link
Copy Markdown
Contributor

Refactored StreamReader ReadLine/ReadLineAsync to use Span.IndexOfAny

dotnet/performance benchmarks
BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.2006/21H2/November2021Update)
AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100-preview.7.22377.5
[Host] : .NET 7.0.0 (7.0.22.37506), X64 RyuJIT AVX2
Job-QTLAVP : .NET 8.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-OYFQVY : .NET 8.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodBranchLineLengthRangeMeanRatio
ReadLinemain[ 0, 0]46.832 μs1.00
ReadLinePR[ 0, 0]93.316 μs1.99
ReadLineAsyncmain[ 0, 0]257.142 μs1.00
ReadLineAsyncPR[ 0, 0]279.500 μs1.09
ReadLinemain[ 0, 1024]13.952 μs1.00
ReadLinePR[ 0, 1024]6.018 μs0.43
ReadLineAsyncmain[ 0, 1024]19.032 μs1.00
ReadLineAsyncPR[ 0, 1024]7.964 μs0.42
ReadLinemain[ 1, 1]64.128 μs1.00
ReadLinePR[ 1, 1]91.517 μs1.43
ReadLineAsyncmain[ 1, 1]176.924 μs1.00
ReadLineAsyncPR[ 1, 1]224.320 μs1.27
ReadLinemain[ 1, 8]54.430 μs1.00
ReadLinePR[ 1, 8]60.738 μs1.12
ReadLineAsyncmain[ 1, 8]123.299 μs1.00
ReadLineAsyncPR[ 1, 8]128.427 μs1.04
ReadLinemain[ 9, 32]26.354 μs1.00
ReadLinePR[ 9, 32]19.343 μs0.73
ReadLineAsyncmain[ 9, 32]47.567 μs1.00
ReadLineAsyncPR[ 9, 32]42.869 μs0.90
ReadLinemain[ 33, 128]19.228 μs1.00
ReadLinePR[ 33, 128]8.441 μs0.43
ReadLineAsyncmain[ 33, 128]25.486 μs1.00
ReadLineAsyncPR[ 33, 128]16.054 μs0.63
ReadLinemain[ 129, 1024]13.449 μs1.00
ReadLinePR[ 129, 1024]5.831 μs0.43
ReadLineAsyncmain[ 129, 1024]19.295 μs1.00
ReadLineAsyncPR[ 129, 1024]7.740 μs0.40
ReadLinemain[1025, 2048]15.227 μs1.00
ReadLinePR[1025, 2048]6.938 μs0.46
ReadLineAsyncmain[1025, 2048]19.946 μs1.00
ReadLineAsyncPR[1025, 2048]8.205 μs0.41

Source

There are regressions in benchmarks with close continuous line-endings (e.g [0,0] which is "\r\n\r\n" repeated), because IndexOfAny will almost always try to use SIMD - since the input buffer is typically large enough to fit in a vector.
I don't know if its a very common scenario though

@ghostghost added area-System.IO community-contribution Indicates that the PR has been added by a community member labels Sep 14, 2022
@ghost

Copy link
Copy Markdown

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

Issue Details

Refactored StreamReader ReadLine/ReadLineAsync to use Span.IndexOfAny

dotnet/performance benchmarks
BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19044.2006/21H2/November2021Update)
AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100-preview.7.22377.5
[Host] : .NET 7.0.0 (7.0.22.37506), X64 RyuJIT AVX2
Job-QTLAVP : .NET 8.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-OYFQVY : .NET 8.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodBranchLineLengthRangeMeanRatio
ReadLinemain[ 0, 0]46.832 μs1.00
ReadLinePR[ 0, 0]93.316 μs1.99
ReadLineAsyncmain[ 0, 0]257.142 μs1.00
ReadLineAsyncPR[ 0, 0]279.500 μs1.09
ReadLinemain[ 0, 1024]13.952 μs1.00
ReadLinePR[ 0, 1024]6.018 μs0.43
ReadLineAsyncmain[ 0, 1024]19.032 μs1.00
ReadLineAsyncPR[ 0, 1024]7.964 μs0.42
ReadLinemain[ 1, 1]64.128 μs1.00
ReadLinePR[ 1, 1]91.517 μs1.43
ReadLineAsyncmain[ 1, 1]176.924 μs1.00
ReadLineAsyncPR[ 1, 1]224.320 μs1.27
ReadLinemain[ 1, 8]54.430 μs1.00
ReadLinePR[ 1, 8]60.738 μs1.12
ReadLineAsyncmain[ 1, 8]123.299 μs1.00
ReadLineAsyncPR[ 1, 8]128.427 μs1.04
ReadLinemain[ 9, 32]26.354 μs1.00
ReadLinePR[ 9, 32]19.343 μs0.73
ReadLineAsyncmain[ 9, 32]47.567 μs1.00
ReadLineAsyncPR[ 9, 32]42.869 μs0.90
ReadLinemain[ 33, 128]19.228 μs1.00
ReadLinePR[ 33, 128]8.441 μs0.43
ReadLineAsyncmain[ 33, 128]25.486 μs1.00
ReadLineAsyncPR[ 33, 128]16.054 μs0.63
ReadLinemain[ 129, 1024]13.449 μs1.00
ReadLinePR[ 129, 1024]5.831 μs0.43
ReadLineAsyncmain[ 129, 1024]19.295 μs1.00
ReadLineAsyncPR[ 129, 1024]7.740 μs0.40
ReadLinemain[1025, 2048]15.227 μs1.00
ReadLinePR[1025, 2048]6.938 μs0.46
ReadLineAsyncmain[1025, 2048]19.946 μs1.00
ReadLineAsyncPR[1025, 2048]8.205 μs0.41

Source

There are regressions in benchmarks with close continuous line-endings (e.g [0,0] which is "\r\n\r\n" repeated), because IndexOfAny will almost always try to use SIMD - since the input buffer is typically large enough to fit in a vector.
I don't know if its a very common scenario though

Author:yesmey
Assignees:-
Labels:

area-System.IO

Milestone:-

@stephentoub

Copy link
Copy Markdown
Member

Thank you. There's already a PR doing this, though:
#69888

@danmoseley

Copy link
Copy Markdown
Contributor

cc @GrabYourPitchforks

@yesmey

Copy link
Copy Markdown
ContributorAuthor

Can't believe I missed it. I clearly need to get better at going over open pull requests!
No worries though. Feel free to close this in favor for 69888 and @GrabYourPitchforks can just check if there's something useful to bring over when he has time to get back to it again

@stephentoub

Copy link
Copy Markdown
Member

Can't believe I missed it. I clearly need to get better at going over open pull requests!

No worries at all. We're striving to do better at reducing our open PR count.

Feel free to close this in favor for 69888

Ok. Thanks, again.

@danmoseley

Copy link
Copy Markdown
Contributor

@yesmey I know @GrabYourPitchforks has another project currently. If you're interested in looking at the other PR to help nudge it along, that would more probably be welcome.

@ghostghost locked as resolved and limited conversation to collaborators Oct 15, 2022
@yesmey
yesmey deleted the streamreadline branch May 9, 2023 18:11
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.IOcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@yesmey@stephentoub@danmoseley