Skip to content

Commit

Permalink
Double IndexOf throughput for chars (#78861)
Browse files Browse the repository at this point in the history
* Add PackedIndexOf for chars

* Add Contains and IndexOfValue(3 chars)

* Stop using PackedIndexOf on ARM

* Improve code comment
  • Loading branch information
MihaZupan committed Jan 3, 2023
1 parent 2619d1c commit ac2ffdf
Show file tree
Hide file tree
Showing 15 changed files with 1,275 additions and 98 deletions.
Expand Up @@ -421,21 +421,25 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IFormattable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Index.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\BitVector256.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny1Value.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny2Values.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny3Values.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny1CharValue.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny1ByteValue.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny2ByteValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny2CharValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny3ByteValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny3CharValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny4Values.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAny5Values.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyAsciiByteValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyAsciiCharValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyAsciiSearcher.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyByteValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyByteValuesInRange.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyCharValuesInRange.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyCharValuesProbabilistic.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyLatin1CharValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValues.T.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValuesDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfAnyValuesInRange.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\IndexOfEmptyValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOfAnyValues\ProbabilisticMap.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOutOfRangeException.cs" />
Expand Down Expand Up @@ -1029,6 +1033,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Byte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Char.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Packed.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SR.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" />
Expand Down
Expand Up @@ -339,7 +339,9 @@ internal static int IndexOfOrdinalIgnoreCase(ReadOnlySpan<char> source, ReadOnly
{
// Do a quick search for the first element of "value".
int relativeIndex = isLetter ?
SpanHelpers.IndexOfAnyChar(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceLength) :
PackedSpanHelpers.PackedIndexOfIsSupported
? PackedSpanHelpers.IndexOfAny(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceLength)
: SpanHelpers.IndexOfAnyChar(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceLength) :
SpanHelpers.IndexOfChar(ref Unsafe.Add(ref searchSpace, offset), valueChar, searchSpaceLength);
if (relativeIndex < 0)
{
Expand Down
Expand Up @@ -2,42 +2,40 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;

namespace System.Buffers
{
internal sealed class IndexOfAny1Value<T> : IndexOfAnyValues<T>
where T : struct, INumber<T>
internal sealed class IndexOfAny1ByteValue : IndexOfAnyValues<byte>
{
private readonly T _e0;
private readonly byte _e0;

public IndexOfAny1Value(ReadOnlySpan<T> values)
public IndexOfAny1ByteValue(ReadOnlySpan<byte> values)
{
Debug.Assert(values.Length == 1);
_e0 = values[0];
}

internal override T[] GetValues() => new[] { _e0 };
internal override byte[] GetValues() => new[] { _e0 };

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(T value) =>
internal override bool ContainsCore(byte value) =>
value == _e0;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
span.IndexOf(_e0);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<T> span) =>
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
span.IndexOfAnyExcept(_e0);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<T> span) =>
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
span.LastIndexOf(_e0);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<T> span) =>
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
span.LastIndexOfAnyExcept(_e0);
}
}
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Buffers
{
internal sealed class IndexOfAny1CharValue<TShouldUsePacked> : IndexOfAnyValues<char>
where TShouldUsePacked : struct, IndexOfAnyValues.IRuntimeConst
{
private char _e0;

public IndexOfAny1CharValue(char value) =>
_e0 = value;

internal override char[] GetValues() => new[] { _e0 };

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(char value) =>
value == _e0;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
TShouldUsePacked.Value
? PackedSpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), _e0, span.Length)
: SpanHelpers.NonPackedIndexOfValueType<short, SpanHelpers.DontNegate<short>>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<char, short>(ref _e0),
span.Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
TShouldUsePacked.Value
? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, span.Length)
: SpanHelpers.NonPackedIndexOfValueType<short, SpanHelpers.Negate<short>>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<char, short>(ref _e0),
span.Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
span.LastIndexOf(_e0);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
span.LastIndexOfAnyExcept(_e0);
}
}
Expand Up @@ -2,42 +2,40 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;

namespace System.Buffers
{
internal sealed class IndexOfAny2Values<T> : IndexOfAnyValues<T>
where T : struct, INumber<T>
internal sealed class IndexOfAny2ByteValues : IndexOfAnyValues<byte>
{
private readonly T _e0, _e1;
private readonly byte _e0, _e1;

public IndexOfAny2Values(ReadOnlySpan<T> values)
public IndexOfAny2ByteValues(ReadOnlySpan<byte> values)
{
Debug.Assert(values.Length == 2);
(_e0, _e1) = (values[0], values[1]);
}

internal override T[] GetValues() => new[] { _e0, _e1 };
internal override byte[] GetValues() => new[] { _e0, _e1 };

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(T value) =>
internal override bool ContainsCore(byte value) =>
value == _e0 || value == _e1;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
span.IndexOfAny(_e0, _e1);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<T> span) =>
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
span.IndexOfAnyExcept(_e0, _e1);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<T> span) =>
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
span.LastIndexOfAny(_e0, _e1);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<T> span) =>
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
span.LastIndexOfAnyExcept(_e0, _e1);
}
}
@@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Buffers
{
internal sealed class IndexOfAny2CharValue<TShouldUsePacked> : IndexOfAnyValues<char>
where TShouldUsePacked : struct, IndexOfAnyValues.IRuntimeConst
{
private char _e0, _e1;

public IndexOfAny2CharValue(char value0, char value1) =>
(_e0, _e1) = (value0, value1);

internal override char[] GetValues() => new[] { _e0, _e1 };

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(char value) =>
value == _e0 || value == _e1;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
TShouldUsePacked.Value
? PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length)
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.DontNegate<short>>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<char, short>(ref _e0),
Unsafe.As<char, short>(ref _e1),
span.Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
TShouldUsePacked.Value
? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length)
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.Negate<short>>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<char, short>(ref _e0),
Unsafe.As<char, short>(ref _e1),
span.Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
span.LastIndexOfAny(_e0, _e1);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
span.LastIndexOfAnyExcept(_e0, _e1);
}
}
Expand Up @@ -2,42 +2,40 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;

namespace System.Buffers
{
internal sealed class IndexOfAny3Values<T> : IndexOfAnyValues<T>
where T : struct, INumber<T>
internal sealed class IndexOfAny3ByteValues : IndexOfAnyValues<byte>
{
private readonly T _e0, _e1, _e2;
private readonly byte _e0, _e1, _e2;

public IndexOfAny3Values(ReadOnlySpan<T> values)
public IndexOfAny3ByteValues(ReadOnlySpan<byte> values)
{
Debug.Assert(values.Length == 3);
(_e0, _e1, _e2) = (values[0], values[1], values[2]);
}

internal override T[] GetValues() => new[] { _e0, _e1, _e2 };
internal override byte[] GetValues() => new[] { _e0, _e1, _e2 };

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(T value) =>
internal override bool ContainsCore(byte value) =>
value == _e0 || value == _e1 || value == _e2;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
span.IndexOfAny(_e0, _e1, _e2);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<T> span) =>
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
span.IndexOfAnyExcept(_e0, _e1, _e2);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<T> span) =>
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
span.LastIndexOfAny(_e0, _e1, _e2);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<T> span) =>
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
span.LastIndexOfAnyExcept(_e0, _e1, _e2);
}
}
@@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Buffers
{
internal sealed class IndexOfAny3CharValue<TShouldUsePacked> : IndexOfAnyValues<char>
where TShouldUsePacked : struct, IndexOfAnyValues.IRuntimeConst
{
private char _e0, _e1, _e2;

public IndexOfAny3CharValue(char value0, char value1, char value2) =>
(_e0, _e1, _e2) = (value0, value1, value2);

internal override char[] GetValues() => new[] { _e0, _e1, _e2 };

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(char value) =>
value == _e0 || value == _e1 || value == _e2;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
TShouldUsePacked.Value
? PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length)
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.DontNegate<short>>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<char, short>(ref _e0),
Unsafe.As<char, short>(ref _e1),
Unsafe.As<char, short>(ref _e2),
span.Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
TShouldUsePacked.Value
? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length)
: SpanHelpers.NonPackedIndexOfAnyValueType<short, SpanHelpers.Negate<short>>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<char, short>(ref _e0),
Unsafe.As<char, short>(ref _e1),
Unsafe.As<char, short>(ref _e2),
span.Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
span.LastIndexOfAny(_e0, _e1, _e2);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
span.LastIndexOfAnyExcept(_e0, _e1, _e2);
}
}

0 comments on commit ac2ffdf

Please sign in to comment.