Skip to content

Commit ac2ffdf

Browse files
authored
Double IndexOf throughput for chars (#78861)
* Add PackedIndexOf for chars * Add Contains and IndexOfValue(3 chars) * Stop using PackedIndexOf on ARM * Improve code comment
1 parent 2619d1c commit ac2ffdf

15 files changed

+1275
-98
lines changed

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,21 +421,25 @@
421421
<Compile Include="$(MSBuildThisFileDirectory)System\IFormattable.cs" />
422422
<Compile Include="$(MSBuildThisFileDirectory)System\Index.cs" />
423423
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\BitVector256.cs" />
424-
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny1Value.cs" />
425-
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny2Values.cs" />
426-
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny3Values.cs" />
424+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny1CharValue.cs" />
425+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny1ByteValue.cs" />
426+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny2ByteValues.cs" />
427+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny2CharValues.cs" />
428+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny3ByteValues.cs" />
429+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny3CharValues.cs" />
427430
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny4Values.cs" />
428431
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny5Values.cs" />
429432
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyAsciiByteValues.cs" />
430433
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyAsciiCharValues.cs" />
431434
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyAsciiSearcher.cs" />
432435
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyByteValues.cs" />
436+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyByteValuesInRange.cs" />
437+
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyCharValuesInRange.cs" />
433438
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyCharValuesProbabilistic.cs" />
434439
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyLatin1CharValues.cs" />
435440
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValues.cs" />
436441
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValues.T.cs" />
437442
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValuesDebugView.cs" />
438-
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValuesInRange.cs" />
439443
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfEmptyValues.cs" />
440444
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\ProbabilisticMap.cs" />
441445
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOutOfRangeException.cs" />
@@ -1029,6 +1033,7 @@
10291033
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Byte.cs" />
10301034
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Char.cs" />
10311035
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.cs" />
1036+
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Packed.cs" />
10321037
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
10331038
<Compile Include="$(MSBuildThisFileDirectory)System\SR.cs" />
10341039
<Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" />

src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,9 @@ internal static int IndexOfOrdinalIgnoreCase(ReadOnlySpan<char> source, ReadOnly
339339
{
340340
// Do a quick search for the first element of "value".
341341
int relativeIndex = isLetter ?
342-
SpanHelpers.IndexOfAnyChar(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceLength) :
342+
PackedSpanHelpers.PackedIndexOfIsSupported
343+
? PackedSpanHelpers.IndexOfAny(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceLength)
344+
: SpanHelpers.IndexOfAnyChar(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceLength) :
343345
SpanHelpers.IndexOfChar(ref Unsafe.Add(ref searchSpace, offset), valueChar, searchSpaceLength);
344346
if (relativeIndex < 0)
345347
{

src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny1Value.cs renamed to src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny1ByteValue.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,40 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using System.Numerics;
65
using System.Runtime.CompilerServices;
76

87
namespace System.Buffers
98
{
10-
internal sealed class IndexOfAny1Value<T> : IndexOfAnyValues<T>
11-
where T : struct, INumber<T>
9+
internal sealed class IndexOfAny1ByteValue : IndexOfAnyValues<byte>
1210
{
13-
private readonly T _e0;
11+
private readonly byte _e0;
1412

15-
public IndexOfAny1Value(ReadOnlySpan<T> values)
13+
public IndexOfAny1ByteValue(ReadOnlySpan<byte> values)
1614
{
1715
Debug.Assert(values.Length == 1);
1816
_e0 = values[0];
1917
}
2018

21-
internal override T[] GetValues() => new[] { _e0 };
19+
internal override byte[] GetValues() => new[] { _e0 };
2220

2321
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24-
internal override bool ContainsCore(T value) =>
22+
internal override bool ContainsCore(byte value) =>
2523
value == _e0;
2624

2725
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28-
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
26+
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
2927
span.IndexOf(_e0);
3028

3129
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32-
internal override int IndexOfAnyExcept(ReadOnlySpan<T> span) =>
30+
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
3331
span.IndexOfAnyExcept(_e0);
3432

3533
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36-
internal override int LastIndexOfAny(ReadOnlySpan<T> span) =>
34+
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
3735
span.LastIndexOf(_e0);
3836

3937
[MethodImpl(MethodImplOptions.AggressiveInlining)]
40-
internal override int LastIndexOfAnyExcept(ReadOnlySpan<T> span) =>
38+
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
4139
span.LastIndexOfAnyExcept(_e0);
4240
}
4341
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
7+
namespace System.Buffers
8+
{
9+
internal sealed class IndexOfAny1CharValue<TShouldUsePacked> : IndexOfAnyValues<char>
10+
where TShouldUsePacked : struct, IndexOfAnyValues.IRuntimeConst
11+
{
12+
private char _e0;
13+
14+
public IndexOfAny1CharValue(char value) =>
15+
_e0 = value;
16+
17+
internal override char[] GetValues() => new[] { _e0 };
18+
19+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20+
internal override bool ContainsCore(char value) =>
21+
value == _e0;
22+
23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
25+
TShouldUsePacked.Value
26+
? PackedSpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), _e0, span.Length)
27+
: SpanHelpers.NonPackedIndexOfValueType<short, SpanHelpers.DontNegate<short>>(
28+
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
29+
Unsafe.As<char, short>(ref _e0),
30+
span.Length);
31+
32+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
33+
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
34+
TShouldUsePacked.Value
35+
? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, span.Length)
36+
: SpanHelpers.NonPackedIndexOfValueType<short, SpanHelpers.Negate<short>>(
37+
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
38+
Unsafe.As<char, short>(ref _e0),
39+
span.Length);
40+
41+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
42+
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
43+
span.LastIndexOf(_e0);
44+
45+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46+
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
47+
span.LastIndexOfAnyExcept(_e0);
48+
}
49+
}

src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny2Values.cs renamed to src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny2ByteValues.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,40 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using System.Numerics;
65
using System.Runtime.CompilerServices;
76

87
namespace System.Buffers
98
{
10-
internal sealed class IndexOfAny2Values<T> : IndexOfAnyValues<T>
11-
where T : struct, INumber<T>
9+
internal sealed class IndexOfAny2ByteValues : IndexOfAnyValues<byte>
1210
{
13-
private readonly T _e0, _e1;
11+
private readonly byte _e0, _e1;
1412

15-
public IndexOfAny2Values(ReadOnlySpan<T> values)
13+
public IndexOfAny2ByteValues(ReadOnlySpan<byte> values)
1614
{
1715
Debug.Assert(values.Length == 2);
1816
(_e0, _e1) = (values[0], values[1]);
1917
}
2018

21-
internal override T[] GetValues() => new[] { _e0, _e1 };
19+
internal override byte[] GetValues() => new[] { _e0, _e1 };
2220

2321
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24-
internal override bool ContainsCore(T value) =>
22+
internal override bool ContainsCore(byte value) =>
2523
value == _e0 || value == _e1;
2624

2725
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28-
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
26+
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
2927
span.IndexOfAny(_e0, _e1);
3028

3129
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32-
internal override int IndexOfAnyExcept(ReadOnlySpan<T> span) =>
30+
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
3331
span.IndexOfAnyExcept(_e0, _e1);
3432

3533
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36-
internal override int LastIndexOfAny(ReadOnlySpan<T> span) =>
34+
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
3735
span.LastIndexOfAny(_e0, _e1);
3836

3937
[MethodImpl(MethodImplOptions.AggressiveInlining)]
40-
internal override int LastIndexOfAnyExcept(ReadOnlySpan<T> span) =>
38+
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
4139
span.LastIndexOfAnyExcept(_e0, _e1);
4240
}
4341
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
7+
namespace System.Buffers
8+
{
9+
internal sealed class IndexOfAny2CharValue<TShouldUsePacked> : IndexOfAnyValues<char>
10+
where TShouldUsePacked : struct, IndexOfAnyValues.IRuntimeConst
11+
{
12+
private char _e0, _e1;
13+
14+
public IndexOfAny2CharValue(char value0, char value1) =>
15+
(_e0, _e1) = (value0, value1);
16+
17+
internal override char[] GetValues() => new[] { _e0, _e1 };
18+
19+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20+
internal override bool ContainsCore(char value) =>
21+
value == _e0 || value == _e1;
22+
23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
25+
TShouldUsePacked.Value
26+
? PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length)
27+
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.DontNegate<short>>(
28+
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
29+
Unsafe.As<char, short>(ref _e0),
30+
Unsafe.As<char, short>(ref _e1),
31+
span.Length);
32+
33+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
34+
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
35+
TShouldUsePacked.Value
36+
? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length)
37+
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.Negate<short>>(
38+
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
39+
Unsafe.As<char, short>(ref _e0),
40+
Unsafe.As<char, short>(ref _e1),
41+
span.Length);
42+
43+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
44+
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
45+
span.LastIndexOfAny(_e0, _e1);
46+
47+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
48+
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
49+
span.LastIndexOfAnyExcept(_e0, _e1);
50+
}
51+
}

src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny3Values.cs renamed to src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAny3ByteValues.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,40 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using System.Numerics;
65
using System.Runtime.CompilerServices;
76

87
namespace System.Buffers
98
{
10-
internal sealed class IndexOfAny3Values<T> : IndexOfAnyValues<T>
11-
where T : struct, INumber<T>
9+
internal sealed class IndexOfAny3ByteValues : IndexOfAnyValues<byte>
1210
{
13-
private readonly T _e0, _e1, _e2;
11+
private readonly byte _e0, _e1, _e2;
1412

15-
public IndexOfAny3Values(ReadOnlySpan<T> values)
13+
public IndexOfAny3ByteValues(ReadOnlySpan<byte> values)
1614
{
1715
Debug.Assert(values.Length == 3);
1816
(_e0, _e1, _e2) = (values[0], values[1], values[2]);
1917
}
2018

21-
internal override T[] GetValues() => new[] { _e0, _e1, _e2 };
19+
internal override byte[] GetValues() => new[] { _e0, _e1, _e2 };
2220

2321
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24-
internal override bool ContainsCore(T value) =>
22+
internal override bool ContainsCore(byte value) =>
2523
value == _e0 || value == _e1 || value == _e2;
2624

2725
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28-
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
26+
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
2927
span.IndexOfAny(_e0, _e1, _e2);
3028

3129
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32-
internal override int IndexOfAnyExcept(ReadOnlySpan<T> span) =>
30+
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
3331
span.IndexOfAnyExcept(_e0, _e1, _e2);
3432

3533
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36-
internal override int LastIndexOfAny(ReadOnlySpan<T> span) =>
34+
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
3735
span.LastIndexOfAny(_e0, _e1, _e2);
3836

3937
[MethodImpl(MethodImplOptions.AggressiveInlining)]
40-
internal override int LastIndexOfAnyExcept(ReadOnlySpan<T> span) =>
38+
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
4139
span.LastIndexOfAnyExcept(_e0, _e1, _e2);
4240
}
4341
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
7+
namespace System.Buffers
8+
{
9+
internal sealed class IndexOfAny3CharValue<TShouldUsePacked> : IndexOfAnyValues<char>
10+
where TShouldUsePacked : struct, IndexOfAnyValues.IRuntimeConst
11+
{
12+
private char _e0, _e1, _e2;
13+
14+
public IndexOfAny3CharValue(char value0, char value1, char value2) =>
15+
(_e0, _e1, _e2) = (value0, value1, value2);
16+
17+
internal override char[] GetValues() => new[] { _e0, _e1, _e2 };
18+
19+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20+
internal override bool ContainsCore(char value) =>
21+
value == _e0 || value == _e1 || value == _e2;
22+
23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
25+
TShouldUsePacked.Value
26+
? PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length)
27+
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.DontNegate<short>>(
28+
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
29+
Unsafe.As<char, short>(ref _e0),
30+
Unsafe.As<char, short>(ref _e1),
31+
Unsafe.As<char, short>(ref _e2),
32+
span.Length);
33+
34+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
35+
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
36+
TShouldUsePacked.Value
37+
? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length)
38+
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.Negate<short>>(
39+
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
40+
Unsafe.As<char, short>(ref _e0),
41+
Unsafe.As<char, short>(ref _e1),
42+
Unsafe.As<char, short>(ref _e2),
43+
span.Length);
44+
45+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46+
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
47+
span.LastIndexOfAny(_e0, _e1, _e2);
48+
49+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
50+
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
51+
span.LastIndexOfAnyExcept(_e0, _e1, _e2);
52+
}
53+
}

0 commit comments

Comments
 (0)