') + ')', '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); } })(); })(); Vectorize BMP string by vcsjones · Pull Request #131170 · dotnet/runtime · GitHub
Skip to content

Vectorize BMP string - #131170

Merged
vcsjones merged 2 commits into
dotnet:mainfrom
vcsjones:vector-bmp-string
Jul 29, 2026
Merged

Vectorize BMP string#131170
vcsjones merged 2 commits into
dotnet:mainfrom
vcsjones:vector-bmp-string

Conversation

@vcsjones

@vcsjonesvcsjones commented Jul 21, 2026

Copy link
Copy Markdown
Member

As a follow up to #131109, this introduces vectorization for the BMPEncoding.

BMPEncoding is big-endian UCS-2 encoding. It is a subset of UTF-16-BE, with the exception that it is truly fixed width and only supports plane 0 without the surrogate pairs.

Performance is largely improved across the board. Small inputs, like country codes, regress ~0.5ns simply because the method body itself has slightly more register pressure. Other inputs like common names, etc. see notable performance improvements.

This also incorporates some feedback about making the SIMD more JIT-friendly into the existing IA5 and VisibleString encoding.

Performance Benchmarks

Source: https://gist.github.com/vcsjones/4b91a33bcae337333cb26d46fd087bf9


BenchmarkDotNet v0.15.8, macOS Tahoe 26.5.2 (25F84) [Darwin 25.5.0]
Apple M1 Ultra, 1 CPU, 20 logical and 20 physical cores
[Host] : .NET 11.0.0 (11.0.0-dev, 42.42.42.42424), Arm64 RyuJIT armv8.0-a
Main : .NET 11.0.0 (11.0.0-dev, 42.42.42.42424), Arm64 RyuJIT armv8.0-a
Vector : .NET 11.0.0 (11.0.0-dev, 42.42.42.42424), Arm64 RyuJIT armv8.0-a
Runtime=.NET 10.0 IterationCount=15 WarmupCount=5 
MethodJobToolchainAlignmentInputMeanErrorStdDevRatioRatioSDGen0Gen1AllocatedAlloc Ratio
DecodeMainMainAlignedCountryCode14.27 ns0.037 ns0.031 ns1.000.000.0051-32 B1.00
DecodeVectorVectorAlignedCountryCode14.97 ns0.031 ns0.029 ns1.050.000.0051-32 B1.00
TryDecodeMainMainAlignedCountryCode11.34 ns0.012 ns0.011 ns1.000.00---NA
TryDecodeVectorVectorAlignedCountryCode11.74 ns0.007 ns0.006 ns1.040.00---NA
WriteMainMainAlignedCountryCode16.13 ns0.043 ns0.040 ns1.000.000.0127-80 B1.00
WriteVectorVectorAlignedCountryCode16.79 ns0.044 ns0.039 ns1.040.000.0127-80 B1.00
DecodeMainMainAlignedCommonName56.85 ns0.073 ns0.068 ns1.000.000.0127-80 B1.00
DecodeVectorVectorAlignedCommonName26.71 ns0.040 ns0.035 ns0.470.000.0127-80 B1.00
TryDecodeMainMainAlignedCommonName52.77 ns0.033 ns0.029 ns1.000.00---NA
TryDecodeVectorVectorAlignedCommonName21.71 ns0.038 ns0.034 ns0.410.00---NA
WriteMainMainAlignedCommonName43.31 ns0.067 ns0.060 ns1.000.000.0216-136 B1.00
WriteVectorVectorAlignedCommonName26.72 ns0.394 ns0.349 ns0.620.010.0217-136 B1.00
DecodeMainMainAlignedUnicodeName28.28 ns0.078 ns0.069 ns1.000.000.0076-48 B1.00
DecodeVectorVectorAlignedUnicodeName19.37 ns0.037 ns0.034 ns0.680.000.0076-48 B1.00
TryDecodeMainMainAlignedUnicodeName24.90 ns0.039 ns0.032 ns1.000.00---NA
TryDecodeVectorVectorAlignedUnicodeName15.75 ns0.024 ns0.020 ns0.630.00---NA
WriteMainMainAlignedUnicodeName25.70 ns0.397 ns0.371 ns1.000.020.0166-104 B1.00
WriteVectorVectorAlignedUnicodeName20.71 ns0.040 ns0.037 ns0.810.010.0166-104 B1.00
DecodeMainMainAlignedLength823.64 ns0.012 ns0.011 ns1.000.000.0063-40 B1.00
DecodeVectorVectorAlignedLength814.63 ns0.044 ns0.041 ns0.620.000.0063-40 B1.00
TryDecodeMainMainAlignedLength820.37 ns0.020 ns0.019 ns1.000.00---NA
TryDecodeVectorVectorAlignedLength811.74 ns0.020 ns0.016 ns0.580.00---NA
WriteMainMainAlignedLength822.18 ns0.062 ns0.058 ns1.000.000.0153-96 B1.00
WriteVectorVectorAlignedLength817.24 ns0.064 ns0.057 ns0.780.000.0153-96 B1.00
DecodeMainMainAlignedLength1636.07 ns0.010 ns0.008 ns1.000.000.0089-56 B1.00
DecodeVectorVectorAlignedLength1616.58 ns0.027 ns0.025 ns0.460.000.0089-56 B1.00
TryDecodeMainMainAlignedLength1632.38 ns0.046 ns0.043 ns1.000.00---NA
TryDecodeVectorVectorAlignedLength1613.21 ns0.030 ns0.025 ns0.410.00---NA
WriteMainMainAlignedLength1630.14 ns0.054 ns0.051 ns1.000.000.0178-112 B1.00
WriteVectorVectorAlignedLength1619.41 ns0.356 ns0.315 ns0.640.010.0179-112 B1.00
DecodeMainMainAlignedLength3261.89 ns0.310 ns0.290 ns1.000.010.0139-88 B1.00
DecodeVectorVectorAlignedLength3221.06 ns0.647 ns0.605 ns0.340.010.0140-88 B1.00
TryDecodeMainMainAlignedLength3258.35 ns0.375 ns0.332 ns1.000.01---NA
TryDecodeVectorVectorAlignedLength3216.27 ns0.343 ns0.268 ns0.280.00---NA
WriteMainMainAlignedLength3247.59 ns0.814 ns0.680 ns1.000.020.0229-144 B1.00
WriteVectorVectorAlignedLength3223.47 ns0.717 ns0.671 ns0.490.020.0229-144 B1.00
DecodeMainMainAlignedLength64123.09 ns1.822 ns1.704 ns1.000.020.0241-152 B1.00
DecodeVectorVectorAlignedLength6430.67 ns0.297 ns0.278 ns0.250.000.0242-152 B1.00
TryDecodeMainMainAlignedLength64117.53 ns0.437 ns0.409 ns1.000.00---NA
TryDecodeVectorVectorAlignedLength6423.48 ns0.103 ns0.091 ns0.200.00---NA
WriteMainMainAlignedLength6483.22 ns0.652 ns0.578 ns1.000.010.0331-208 B1.00
WriteVectorVectorAlignedLength6431.31 ns0.323 ns0.286 ns0.380.000.0331-208 B1.00
DecodeMainMainAlignedLength256460.07 ns0.758 ns0.709 ns1.000.000.0854-536 B1.00
DecodeVectorVectorAlignedLength25673.86 ns0.125 ns0.104 ns0.160.000.0854-536 B1.00
TryDecodeMainMainAlignedLength256447.23 ns0.333 ns0.311 ns1.000.00---NA
TryDecodeVectorVectorAlignedLength25654.62 ns0.059 ns0.055 ns0.120.00---NA
WriteMainMainAlignedLength256297.00 ns0.250 ns0.234 ns1.000.000.0939-592 B1.00
WriteVectorVectorAlignedLength25671.91 ns0.080 ns0.071 ns0.240.000.09430.0001592 B1.00
DecodeMainMainAlignedLength10241,769.30 ns1.602 ns1.420 ns1.000.000.3300-2072 B1.00
DecodeVectorVectorAlignedLength1024270.31 ns0.690 ns0.611 ns0.150.000.3300-2072 B1.00
TryDecodeMainMainAlignedLength10241,693.69 ns1.338 ns1.252 ns1.000.00---NA
TryDecodeVectorVectorAlignedLength1024195.24 ns0.092 ns0.086 ns0.120.00---NA
WriteMainMainAlignedLength10241,119.74 ns1.917 ns1.700 ns1.000.000.33760.00192128 B1.00
WriteVectorVectorAlignedLength1024250.16 ns1.218 ns1.080 ns0.220.000.33900.00242128 B1.00
DecodeMainMainUnalignedCountryCode14.42 ns0.127 ns0.119 ns1.000.010.0051-32 B1.00
DecodeVectorVectorUnalignedCountryCode15.08 ns0.040 ns0.033 ns1.050.010.0051-32 B1.00
TryDecodeMainMainUnalignedCountryCode11.31 ns0.021 ns0.018 ns1.000.00---NA
TryDecodeVectorVectorUnalignedCountryCode11.63 ns0.021 ns0.020 ns1.030.00---NA
WriteMainMainUnalignedCountryCode16.89 ns0.182 ns0.170 ns1.000.010.0127-80 B1.00
WriteVectorVectorUnalignedCountryCode17.38 ns0.043 ns0.038 ns1.030.010.0127-80 B1.00
DecodeMainMainUnalignedCommonName56.72 ns0.067 ns0.056 ns1.000.000.0127-80 B1.00
DecodeVectorVectorUnalignedCommonName26.71 ns0.057 ns0.053 ns0.470.000.0127-80 B1.00
TryDecodeMainMainUnalignedCommonName52.68 ns0.068 ns0.064 ns1.000.00---NA
TryDecodeVectorVectorUnalignedCommonName21.68 ns0.018 ns0.017 ns0.410.00---NA
WriteMainMainUnalignedCommonName43.62 ns0.035 ns0.033 ns1.000.000.0216-136 B1.00
WriteVectorVectorUnalignedCommonName26.82 ns0.068 ns0.064 ns0.610.000.0217-136 B1.00
DecodeMainMainUnalignedUnicodeName28.22 ns0.061 ns0.057 ns1.000.000.0076-48 B1.00
DecodeVectorVectorUnalignedUnicodeName19.32 ns0.033 ns0.031 ns0.680.000.0076-48 B1.00
TryDecodeMainMainUnalignedUnicodeName24.74 ns0.022 ns0.021 ns1.000.00---NA
TryDecodeVectorVectorUnalignedUnicodeName15.72 ns0.023 ns0.022 ns0.640.00---NA
WriteMainMainUnalignedUnicodeName25.81 ns0.032 ns0.030 ns1.000.000.0166-104 B1.00
WriteVectorVectorUnalignedUnicodeName21.11 ns0.033 ns0.031 ns0.820.000.0166-104 B1.00
DecodeMainMainUnalignedLength823.47 ns0.032 ns0.030 ns1.000.000.0063-40 B1.00
DecodeVectorVectorUnalignedLength814.49 ns0.036 ns0.034 ns0.620.000.0063-40 B1.00
TryDecodeMainMainUnalignedLength820.27 ns0.025 ns0.022 ns1.000.00---NA
TryDecodeVectorVectorUnalignedLength811.39 ns0.004 ns0.003 ns0.560.00---NA
WriteMainMainUnalignedLength822.72 ns0.080 ns0.062 ns1.000.000.0153-96 B1.00
WriteVectorVectorUnalignedLength818.17 ns0.627 ns0.586 ns0.800.030.0153-96 B1.00
DecodeMainMainUnalignedLength1636.14 ns0.537 ns0.476 ns1.000.020.0089-56 B1.00
DecodeVectorVectorUnalignedLength1616.50 ns0.147 ns0.114 ns0.460.010.0089-56 B1.00
TryDecodeMainMainUnalignedLength1632.57 ns0.478 ns0.447 ns1.000.02---NA
TryDecodeVectorVectorUnalignedLength1612.89 ns0.022 ns0.020 ns0.400.01---NA
WriteMainMainUnalignedLength1631.11 ns0.646 ns0.604 ns1.000.030.0178-112 B1.00
WriteVectorVectorUnalignedLength1619.64 ns0.068 ns0.064 ns0.630.010.0179-112 B1.00
DecodeMainMainUnalignedLength3261.77 ns0.095 ns0.084 ns1.000.000.0139-88 B1.00
DecodeVectorVectorUnalignedLength3220.65 ns0.032 ns0.028 ns0.330.000.0140-88 B1.00
TryDecodeMainMainUnalignedLength3257.64 ns0.049 ns0.046 ns1.000.00---NA
TryDecodeVectorVectorUnalignedLength3215.87 ns0.013 ns0.012 ns0.280.00---NA
WriteMainMainUnalignedLength3247.41 ns0.314 ns0.294 ns1.000.010.0229-144 B1.00
WriteVectorVectorUnalignedLength3223.44 ns0.072 ns0.068 ns0.490.000.0229-144 B1.00
DecodeMainMainUnalignedLength64123.07 ns0.224 ns0.199 ns1.000.000.0241-152 B1.00
DecodeVectorVectorUnalignedLength6429.79 ns0.068 ns0.063 ns0.240.000.0242-152 B1.00
TryDecodeMainMainUnalignedLength64117.86 ns1.290 ns1.077 ns1.000.01---NA
TryDecodeVectorVectorUnalignedLength6423.68 ns0.010 ns0.008 ns0.200.00---NA
WriteMainMainUnalignedLength6485.83 ns0.225 ns0.211 ns1.000.000.0331-208 B1.00
WriteVectorVectorUnalignedLength6431.51 ns0.062 ns0.058 ns0.370.000.0331-208 B1.00
DecodeMainMainUnalignedLength256462.01 ns0.529 ns0.495 ns1.000.000.0854-536 B1.00
DecodeVectorVectorUnalignedLength25673.40 ns0.129 ns0.114 ns0.160.000.0854-536 B1.00
TryDecodeMainMainUnalignedLength256436.59 ns0.388 ns0.344 ns1.000.00---NA
TryDecodeVectorVectorUnalignedLength25653.95 ns0.054 ns0.051 ns0.120.00---NA
WriteMainMainUnalignedLength256299.77 ns0.426 ns0.398 ns1.000.000.0939-592 B1.00
WriteVectorVectorUnalignedLength25673.73 ns0.133 ns0.124 ns0.250.000.09430.0001592 B1.00
DecodeMainMainUnalignedLength10241,770.61 ns1.237 ns1.033 ns1.000.000.3300-2072 B1.00
DecodeVectorVectorUnalignedLength1024269.57 ns0.657 ns0.583 ns0.150.000.3300-2072 B1.00
TryDecodeMainMainUnalignedLength10241,692.96 ns1.859 ns1.739 ns1.000.00---NA
TryDecodeVectorVectorUnalignedLength1024194.38 ns0.181 ns0.169 ns0.110.00---NA
WriteMainMainUnalignedLength10241,127.43 ns1.654 ns1.548 ns1.000.000.33760.00192128 B1.00
WriteVectorVectorUnalignedLength1024251.62 ns0.766 ns0.717 ns0.220.000.33900.00242128 B1.00

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-formats-asn1, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 introduces a vectorized implementation of ASN.1 BMPString (big-endian UCS-2 without surrogates) encode/decode logic in BMPEncoding, aiming to improve throughput on larger inputs while preserving scalar behavior for validation and exact error indexing.

Changes:

  • Add Vector<ushort>-based fast paths for BMPString encode (GetBytes) and decode (GetChars), including big-endian handling and surrogate detection.
  • Split BMPEncoding into partial implementations for net vs downlevel builds, keeping max-count logic in the shared file.
  • Add reader/writer tests covering vector boundary lengths, bounds safety (BoundedMemory), and surrogate detection index correctness.
Show a summary per file
FileDescription
src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.net.csAdds vectorized BMPEncoding encode/decode with endian swap + surrogate scanning.
src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.downlevel.csRestores scalar BMPEncoding implementation for downlevel targets.
src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.csMakes BMPEncoding partial and retains max byte/char count logic.
src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.csAdds vector-boundary + surrogate-index writer regression tests and bounds checks.
src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.csAdds vector-boundary + surrogate-index reader regression tests and bounds checks.

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 1

@EgorBo

EgorBo commented Jul 21, 2026

Copy link
Copy Markdown
Member

@vcsjones not sure if it matters, but currently for loop shapes that load more than one element (either SIMD or BinaryOperations.*) it's more jit friendly to use code like

while(span.Length>=<const>){varvec=Vector.Create(span);// or any other operation that loads from/saves to span slice.span=span.Slice(const);}

the ones you used will lead to redundant checks (hopefully will be optimized in .NET 12.0).

Regardless, thanks for using safe code for that 🙂

CopilotAI review requested due to automatic review settings July 21, 2026 22:42
@vcsjones

Copy link
Copy Markdown
MemberAuthor

not sure if it matters, but currently for loop shapes that load more than one element (either SIMD or BinaryOperations.*) it's more jit friendly to use code like

Hm yeah that gave a nice 20~25% performance bump.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 2

@vcsjones
vcsjones merged commit 328cee3 into dotnet:mainJul 29, 2026
85 checks passed
@vcsjones
vcsjones deleted the vector-bmp-string branch July 29, 2026 18:49
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 29, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 29, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@EgorBo@bartonjs