Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster IndexOf for substrings #63285

Merged
merged 48 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
34bf37a
Improve "lastChar == firstChar" case, also, use IndexOf directly if v…
EgorBo Jan 7, 2022
03ae4da
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 7, 2022
5cfdb16
Try plain IndexOf first, to optimize cases where even first char of v…
EgorBo Jan 7, 2022
0638617
Merge branch 'main' of github.com:dotnet/runtime into vectorize-indexof
EgorBo Jan 7, 2022
e36fdc6
add 1-byte implementation
EgorBo Jan 7, 2022
85c2320
copyrights
EgorBo Jan 7, 2022
8918ab6
fix copy-paste mistake
EgorBo Jan 7, 2022
cb32d34
Initial LastIndexOf impl
EgorBo Jan 8, 2022
cda6b50
More efficient LastIndexOf
EgorBo Jan 8, 2022
8af9270
fix bug in Char version (we need two clear two lowest bits in the mas…
EgorBo Jan 8, 2022
87c26d0
use ResetLowestSetBit
EgorBo Jan 8, 2022
652b42d
Fix bug
EgorBo Jan 8, 2022
53cefad
Add two-byte LastIndexOf
EgorBo Jan 8, 2022
d465407
Fix build
EgorBo Jan 8, 2022
22921fd
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 9, 2022
2c851bc
Minor optimizations
EgorBo Jan 9, 2022
9308d82
optimize cases with two-byte/two-char values
EgorBo Jan 9, 2022
dac974a
Remove gotos, fix build
EgorBo Jan 9, 2022
3554ad3
fix bug in LastIndexOf
EgorBo Jan 9, 2022
de87ec2
Make sure String.LastIndexOf is optimized
EgorBo Jan 9, 2022
cb7541f
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 13, 2022
b0b04ad
Use xplat simd helpers - implicit ARM support
EgorBo Jan 13, 2022
141e236
fix arm
EgorBo Jan 14, 2022
e664ad3
Delete \
EgorBo Jan 15, 2022
bff8419
Use Vector128.IsHardwareAccelerated
EgorBo Jan 15, 2022
dcc9d81
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 15, 2022
3def5e0
Fix build
EgorBo Jan 15, 2022
a52138b
Use IsAllZero
EgorBo Jan 15, 2022
f86e323
Address feedback
EgorBo Jan 15, 2022
f2372a0
Address feedback
EgorBo Jan 15, 2022
38ef9a9
micro-optimization, do-while is better here since mask is guaranteed …
EgorBo Jan 15, 2022
f5e6192
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 17, 2022
4827ddc
Address feedabc
EgorBo Jan 18, 2022
d601351
Use clever trick I borrowed from IndexOfAny for trailing elements
EgorBo Jan 18, 2022
3a005bc
give up on +1 bump for SequenceCompare
EgorBo Jan 18, 2022
9fefe81
Clean up
EgorBo Jan 18, 2022
c118dd2
Clean up
EgorBo Jan 18, 2022
7e8b100
fix build
EgorBo Jan 18, 2022
d805701
Merge branch 'main' of github.com:dotnet/runtime into vectorize-indexof
EgorBo Jan 21, 2022
3bb56f0
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 21, 2022
e9df891
Add debug asserts
EgorBo Jan 22, 2022
cb60535
Clean up: give up on the unrolled trick - too little value from code …
EgorBo Jan 22, 2022
7c55951
Add a test
EgorBo Jan 22, 2022
3f0b4c3
Fix build
EgorBo Jan 22, 2022
4d860a1
Merge branch 'main' of https://github.com/dotnet/runtime into vectori…
EgorBo Jan 24, 2022
48f4fc7
Add byte-specific test
EgorBo Jan 24, 2022
7c3b834
Fix build
EgorBo Jan 25, 2022
c68a07a
Update IndexOfSequence.byte.cs
EgorBo Jan 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions THIRD-PARTY-NOTICES.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,35 @@ License for fastmod (https://github.com/lemire/fastmod) and ibm-fpgen (https://g
See the License for the specific language governing permissions and
limitations under the License.

License for sse4-strstr (https://github.com/WojciechMula/sse4-strstr)
--------------------------------------

Copyright (c) 2008-2016, Wojciech Muła
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

License notice for The C++ REST SDK
-----------------------------------

Expand Down
101 changes: 101 additions & 0 deletions src/libraries/System.Memory/tests/Span/IndexOfSequence.byte.cs

Large diffs are not rendered by default.

101 changes: 101 additions & 0 deletions src/libraries/System.Memory/tests/Span/IndexOfSequence.char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Xunit;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace System.SpanTests
{
Expand Down Expand Up @@ -115,5 +117,104 @@ public static void IndexOfSequenceLengthOneValueJustPasttVeryEnd_Char()
int index = span.IndexOf(value);
Assert.Equal(-1, index);
}

public static IEnumerable<object[]> IndexOfSubSeqData_Char()
{
// searchSpace, value, expected IndexOf value, expected LastIndexOf value
yield return new object[] { "11111", "111", 0, 2 };
yield return new object[] { "1111111111", "1x1", -1, -1 };
yield return new object[] { "1111111111", "111", 0, 7 };
yield return new object[] { "11111111111x12111", "1x121", 10, 10 };
yield return new object[] { "11111111111x12111", "11121", -1, -1 };
yield return new object[] { "1111111111x121111", "11121", -1, -1 };
yield return new object[] { "11111x12111111111", "11121", -1, -1 };
yield return new object[] { "11111111111x12111", "1x211", -1, -1 };
yield return new object[] { "11111111111x12111", "11211", -1, -1 };
yield return new object[] { "1111111111x121111", "11211", -1, -1 };
yield return new object[] { "11111x12111111111", "11211", -1, -1 };
yield return new object[] { "11111111111x12111", "12111", 12, 12 };
yield return new object[] { "1111111111x121111", "12111", 11, 11 };
yield return new object[] { "11111x12111111111", "12111", 6, 6 };
yield return new object[] { "1111x1211111111111x12", "11121", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "11121", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "111121", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "1111121", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "1111121", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "1111121", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "1211211", -1, -1 };
yield return new object[] { "1111x1211111111111x12", "1211111", 5, 5 };
yield return new object[] { "1111x1211111111111x12", "1211111", 5, 5 };
yield return new object[] { "1111x1211111111111x12", "1211111", 5, 5 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111", 0, 44 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1111", 0, 43 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11111", 7, 42 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111111", 7, 41 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1111111", 7, 11 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11111111", 7, 10 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111111111", 7, 9 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11111111111", 7, 7 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11111111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111111111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1211", 5, 48 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11121", 44, 44 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "121111", 5, 19 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "12111211", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1111111", 7, 11 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1121121111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1111211111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111111211111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1121111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11122111112111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "1111111211111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "111211111111111111", -1, -1 };
yield return new object[] { "1111x1211111111111x12111122131221221211221111112121121", "11111211111121111111", -1, -1 };
yield return new object[] { "жжжжжжжжжжжжжж", "жжж", 0, 11 };
yield return new object[] { "жжжжжжжжжжжжжжжжжжжжжжжжжжжж", "ж0ж", -1, -1 };
yield return new object[] { "жжжжжаааааааааааааааччччс", "ччччс", 20, 20 };
yield return new object[] { "жжжжжаааааааааааааааччччсссссссчччч", "чччч", 20, 31 };
yield return new object[] { "жжжжжжжжжжжжжжжжжжжжжжжжжжжж", "1112", -1, -1 };
yield return new object[] { "0уза0оцущ0оаз0щцуоазщцуо0азщцуоазщоц0узозцуоазуоцз0щауцз0оазцо", "0оаз0", 9, 9 };
yield return new object[] { "abababababababababababababababbc", "bb", 29, 29 };
yield return new object[] { "abababababababababababababababb", "bb", 29, 29 };
yield return new object[] { "abababababababababababababababbc", "bb", 29, 29 };
yield return new object[] { "babababababababababababababababc", "bb", -1, -1 };
yield return new object[] { "abababababababababababababababbb", "bbb", 29, 29 };
yield return new object[] { "abababababababababababababababbbc", "bbb", 29, 29 };
yield return new object[] { "bbbbabababababababababababababababc", "bbb", 0, 1 };
yield return new object[] { "abababababababababababababababbc", "aa", -1, -1 };
yield return new object[] { "abababababababababababababababb", "aa", -1, -1 };
yield return new object[] { "abababababababababababababababbc", "aa", -1, -1 };
yield return new object[] { "babababababababababababababababc", "aa", -1, -1 };
yield return new object[] { "abababababababababababababababbb", "aaa", -1, -1 };
yield return new object[] { "abababababababababababababababbbc", "aaa", -1, -1 };
yield return new object[] { "bbbbabababababababababababababababc", "aaa", -1, -1 };
yield return new object[] { "ababababababababababababababababbc", "abaa", -1, -1 };
yield return new object[] { "babbbabababababababababababababababc", "babb", 0, 0 };
yield return new object[] { "babbbabababababababababababababababc", "сaсс", -1, -1 };
yield return new object[] { "babbbbbbbbbbbbb", "babbbbbbbbbbbb", 0, 0 };
yield return new object[] { "babbbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbbbbbbb", 0, 15 };
yield return new object[] { "babbbbbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbbbbbbb", 0, 17 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbbbbbbb", 18, 32 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "bbbbbbbbbbbbb", 20, 20 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", 0, 0 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbb", 0, 0 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbbb", -1, -1 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", 0, 0 };
yield return new object[] { "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbb", "babbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbb", 0, 0 };
yield return new object[] { "xxxxxxxxxxxxxxbabbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbbxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxx", 60, 60 };
yield return new object[] { "xxxxxxxxxxxxxxxbabbbbbbxbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbbbbbbxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxx", 0, 0 };
}

[Theory]
[MemberData(nameof(IndexOfSubSeqData_Char))]
public static void ValueStartsAndEndsWithTheSameChars(string searchSpace, string value, int expectedIndexOfValue, int expectedLastIndexOfValue)
{
Assert.Equal(expectedIndexOfValue, searchSpace.AsSpan().IndexOf(value));
Assert.Equal(expectedLastIndexOfValue, searchSpace.AsSpan().LastIndexOf(value));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -580,12 +580,25 @@ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value) where T : IEquatable<T>
{
if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
value.Length);
if (RuntimeHelpers.IsBitwiseEquatable<T>())
{
if (Unsafe.SizeOf<T>() == sizeof(byte))
{
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
value.Length);
}
if (Unsafe.SizeOf<T>() == sizeof(char))
{
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
value.Length);
}
}

return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,5 +708,25 @@ public static nuint RotateRight(nuint value, int offset)
return (nuint)RotateRight((uint)value, offset);
#endif
}

/// <summary>
/// Reset the lowest significant bit in the given value
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static uint ResetLowestSetBit(uint value)
{
// It's lowered to BLSR on x86
return value & (value - 1);
}

/// <summary>
/// Reset specific bit in the given value
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static uint ResetBit(uint value, int bitPos)
{
// TODO: Recognize BTR on x86 and LSL+BIC on ARM
return value & ~(uint)(1 << bitPos);
}
}
}
Loading