Skip to content

SIMDify ToLowerInvariant/ToUpperInvariant - #78262

Merged
EgorBo merged 6 commits into
dotnet:mainfrom
EgorBo:simd-lowercaser
Nov 24, 2022
Merged

SIMDify ToLowerInvariant/ToUpperInvariant#78262
EgorBo merged 6 commits into
dotnet:mainfrom
EgorBo:simd-lowercaser

Conversation

@EgorBo

@EgorBoEgorBo commented Nov 12, 2022

Copy link
Copy Markdown
Member

This PR does:

  • Adds more tests
  • SIMDify large inputs for ToLowerInvariant/ToUpperInvariant (for spans)
publicIEnumerable<string>TestData(){yieldreturn"Ab";// 2 chars: no SIMD pathyieldreturn"Abcd";// 4 chars: no SIMD pathyieldreturn"Abcd123";// 7 chars: no SIMD pathyieldreturn"Abcd1234";// 8 chars: 1xV128yieldreturn"Abcd1234Ab";// 10 chars: 1xV128 + 2 trailing charsyieldreturn"Abcd1234Abcd123";// 15 chars: 1xV128 + 7 trailing charsyieldreturn"Licensed to the NET Foundation";// 32 charsyieldreturn"We always welcome bug reports, API proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible.";}privatestaticreadonlychar[]OutputBuffer=newchar[1024];[Benchmark][ArgumentsSource(nameof(TestData))]publicvoidToLowerInvariant(stringstr)=>str.AsSpan().ToLowerInvariant(OutputBuffer.AsSpan());
MethodToolchainstrMeanRatio
ToLowerInvariant\Core_Root\corerun.exeAb6.924 ns1.03
ToLowerInvariant\Core_Root_base\corerun.exeAb6.778 ns1.00
ToLowerInvariant\Core_Root\corerun.exeAbcd7.623 ns1.03
ToLowerInvariant\Core_Root_base\corerun.exeAbcd7.379 ns1.00
ToLowerInvariant\Core_Root\corerun.exeAbcd1238.461 ns0.97
ToLowerInvariant\Core_Root_base\corerun.exeAbcd1238.683 ns1.00
ToLowerInvariant\Core_Root\corerun.exeAbcd12346.275 ns0.73
ToLowerInvariant\Core_Root_base\corerun.exeAbcd12348.580 ns1.00
ToLowerInvariant\Core_Root\corerun.exeAbcd1234Ab6.858 ns0.75
ToLowerInvariant\Core_Root_base\corerun.exeAbcd1234Ab9.165 ns1.00
ToLowerInvariant\Core_Root\corerun.exeAbcd1234Abcd1237.001 ns0.64
ToLowerInvariant\Core_Root_base\corerun.exeAbcd1234Abcd12310.871 ns1.00
ToLowerInvariant\Core_Root\corerun.exeLicen(...)ation [30]9.250 ns0.61
ToLowerInvariant\Core_Root_base\corerun.exeLicen(...)ation [30]15.027 ns1.00
ToLowerInvariant\Core_Root\corerun.exeWe a(...)ble. [153]20.832 ns0.42
ToLowerInvariant\Core_Root_base\corerun.exeWe a(...)ble. [153]49.970 ns1.00

ToUpperInvariant() shows the same numbers.

@ghost

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost assigned EgorBoNov 12, 2022
Comment threadsrc/libraries/Common/tests/Tests/System/StringTests.cs Outdated
Comment threadsrc/libraries/Common/tests/Tests/System/StringTests.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs Outdated
@stephentoub

Copy link
Copy Markdown
Member

numbers.

What's the overhead when non-ASCII is encountered?

@EgorBo

Copy link
Copy Markdown
MemberAuthor

numbers.

What's the overhead when non-ASCII is encountered?

The worst case for this algorithm is a short (>7 chars) full non-ASCII string, but the cost of SIMD "is ascii" check is not too big.
ASCII with non-ASCII can be faster with this PR depending how many ASCII characters we manage to process in SIMD before we encounter non-ASCII.

publicIEnumerable<string>TestData(){// worst case: short full non-ASCIIyieldreturn"Привет Мир";yieldreturn"ASCII-string with non-ASCII chars: ыц!";}privatestaticreadonlychar[]OutputBuffer=newchar[1024];[Benchmark][ArgumentsSource(nameof(TestData))]publicvoidToLowerInvariant(stringstr)=>str.AsSpan().ToLowerInvariant(OutputBuffer.AsSpan());
MethodToolchainstrMean
ToLowerInvariant\Core_Root\corerun.exeПривет Мир52.19 ns
ToLowerInvariant\Core_Root_base\corerun.exeПривет Мир48.52 ns
ToLowerInvariant\Core_Root\corerun.exeASCII(...): ыц! [38]32.36 ns
ToLowerInvariant\Core_Root_base\corerun.exeASCII(...): ыц! [38]34.92 ns

I've pushed a change to call the Scalar path if we encounter non-ASCII in a vector - because we still want to process as many ASCII chars as we can before we switch to extremely slow NLS/ICU fallback.

@EgorBo
EgorBo merged commit 4b6380d into dotnet:mainNov 24, 2022
@EgorBo
EgorBo deleted the simd-lowercaser branch November 24, 2022 21:42
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@EgorBo@stephentoub@am11@gfoidl@jkotas