Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan
, '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

Improve WebUtility.HtmlEncode / UrlEncode performance - #103737

Merged
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf
Jun 21, 2024
Merged

Improve WebUtility.HtmlEncode / UrlEncode performance#103737
stephentoub merged 2 commits into
dotnet:mainfrom
stephentoub:webutilperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member
  • For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
  • For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
  • For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
  • For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
  • For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
  • Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
  • Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Net;BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);[MemoryDiagnoser]publicclassTests{[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringHtmlEncode(stringinput)=>WebUtility.HtmlEncode(input);[Benchmark][ArgumentsSource(nameof(Inputs))]publicstringUrlEncode(stringinput)=>WebUtility.UrlEncode(input);publicIEnumerable<object[]>Inputs()=>[["this-is-a-very-long-filename-for-an-image-that-should-not-need-encoding.jpg"],["short_name.txt"],["https://www.example.com"],["<test>hello, world</test>"],["לילה טוב"],[""" How much wood could a woodchuck chuck If a woodchuck could chuck wood? A woodchuck would chuck as much wood As much wood as a woodchuck could chuck, If a woodchuck could chuck wood. """]];}
MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exe<test(...)test> [25]81.576 ns1.0096 B1.00
HtmlEncode\pr\corerun.exe<test(...)test> [25]80.366 ns0.9796 B1.00
UrlEncode\main\corerun.exe<test(...)test> [25]118.515 ns1.00160 B1.00
UrlEncode\pr\corerun.exe<test(...)test> [25]103.039 ns0.8796 B0.60
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA
UrlEncode\main\corerun.exeHow (...)ood. [185]537.945 ns1.00664 B1.00
UrlEncode\pr\corerun.exeHow (...)ood. [185]466.832 ns0.86432 B0.65
HtmlEncode\main\corerun.exehttps(...)e.com [23]21.442 ns1.00-NA
HtmlEncode\pr\corerun.exehttps(...)e.com [23]4.732 ns0.22-NA
UrlEncode\main\corerun.exehttps(...)e.com [23]103.053 ns1.00136 B1.00
UrlEncode\pr\corerun.exehttps(...)e.com [23]76.013 ns0.7380 B0.59
HtmlEncode\main\corerun.exeshort_name.txt13.068 ns1.00-NA
HtmlEncode\pr\corerun.exeshort_name.txt4.876 ns0.38-NA
UrlEncode\main\corerun.exeshort_name.txt14.324 ns1.00-NA
UrlEncode\pr\corerun.exeshort_name.txt3.600 ns0.26-NA
HtmlEncode\main\corerun.exethis-(...)g.jpg [75]62.800 ns1.00-NA
HtmlEncode\pr\corerun.exethis-(...)g.jpg [75]6.975 ns0.11-NA
UrlEncode\main\corerun.exethis-(...)g.jpg [75]70.709 ns1.00-NA
UrlEncode\pr\corerun.exethis-(...)g.jpg [75]5.611 ns0.08-NA
HtmlEncode\main\corerun.exeלילה טוב8.436 ns1.00-NA
HtmlEncode\pr\corerun.exeלילה טוב11.427 ns1.36-NA
UrlEncode\main\corerun.exeלילה טוב100.852 ns1.00184 B1.00
UrlEncode\pr\corerun.exeלילה טוב74.544 ns0.74112 B0.61

- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@MihaZupanMihaZupan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always love to see SearchValues ratios like these

MethodToolchaininputMeanRatioAllocatedAlloc Ratio
HtmlEncode\main\corerun.exeHow (...)ood. [185]164.359 ns1.00-NA
HtmlEncode\pr\corerun.exeHow (...)ood. [185]10.142 ns0.06-NA

Comment threadsrc/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs Outdated
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@stephentoub
stephentoub merged commit b1bb871 into dotnet:mainJun 21, 2024
@stephentoub
stephentoub deleted the webutilperf branch June 21, 2024 13:57
rzikm pushed a commit to rzikm/dotnet-runtime that referenced this pull request Jun 24, 2024
* Improve WebUtility.HtmlEncode / UrlEncode performance
- For HtmlEncode, vectorize IndexOfHtmlEncodingChars. Using SearchValues, we can efficiently search for the first ASCII encoding char or the first non-ASCII char, and only then fall back to a scalar loop.
- For HtmlEncode, reduce branching by using a more efficient check to determine whether the ASCII characters need to be encoded.
- For UrlEncode, rather than UTF8-encoding into a new byte[], %-encoding in-place in that, and then creating a string from that, we can use string.Create and just do all the encoding in that buffer.
- For UrlEncode, use SearchValues to vectorize the search for the first non-safe char. Also move the check for ' ' to be inside the if for non-safe char.
- For UrlEncode, use SearchValues to optimize the check for whether an individual character is part of the set (via Contains).
- Simplify IsUrlSafeChar. Rather than multiple checks, one of which is a bitmap, just have a bitmap.
- Remove some leading IsNullOrEmpty checks. Null/empty inputs should be rare, and they're now handled implicitly as part of the subsequent loops.
* Update src/libraries/System.Private.CoreLib/src/System/Net/WebUtility.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
---------
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 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.

3 participants

@stephentoub@EgorBo@MihaZupan