Skip to content

Commit d590107

Browse files
committed
Avoid bloom filter checks for IndexOfAnyExcept in ProbabilisticMap
1 parent f510c5a commit d590107

File tree

2 files changed

+83
-81
lines changed

2 files changed

+83
-81
lines changed

src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticCharSearchValues.cs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,30 +34,16 @@ public ProbabilisticCharSearchValues(scoped ReadOnlySpan<char> values)
3434
internal override bool ContainsCore(char value) =>
3535
ProbabilisticMap.Contains(ref Unsafe.As<ProbabilisticMap, uint>(ref _map), _values, value);
3636

37-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3837
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
39-
IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
38+
ProbabilisticMap.IndexOfAny(ref Unsafe.As<ProbabilisticMap, uint>(ref _map), ref MemoryMarshal.GetReference(span), span.Length, _values);
4039

41-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4240
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
43-
IndexOfAny<IndexOfAnyAsciiSearcher.Negate>(ref MemoryMarshal.GetReference(span), span.Length);
41+
ProbabilisticMap.IndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.Negate>(ref MemoryMarshal.GetReference(span), span.Length, _values);
4442

45-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4643
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
47-
LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
44+
ProbabilisticMap.LastIndexOfAny(ref Unsafe.As<ProbabilisticMap, uint>(ref _map), ref MemoryMarshal.GetReference(span), span.Length, _values);
4845

49-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5046
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
51-
LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate>(ref MemoryMarshal.GetReference(span), span.Length);
52-
53-
[MethodImpl(MethodImplOptions.NoInlining)]
54-
private int IndexOfAny<TNegator>(ref char searchSpace, int searchSpaceLength)
55-
where TNegator : struct, IndexOfAnyAsciiSearcher.INegator =>
56-
ProbabilisticMap.IndexOfAny<TNegator>(ref Unsafe.As<ProbabilisticMap, uint>(ref _map), ref searchSpace, searchSpaceLength, _values);
57-
58-
[MethodImpl(MethodImplOptions.NoInlining)]
59-
private int LastIndexOfAny<TNegator>(ref char searchSpace, int searchSpaceLength)
60-
where TNegator : struct, IndexOfAnyAsciiSearcher.INegator =>
61-
ProbabilisticMap.LastIndexOfAny<TNegator>(ref Unsafe.As<ProbabilisticMap, uint>(ref _map), ref searchSpace, searchSpaceLength, _values);
47+
ProbabilisticMap.LastIndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.Negate>(ref MemoryMarshal.GetReference(span), span.Length, _values);
6248
}
6349
}

src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticMap.cs

Lines changed: 79 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -207,116 +207,96 @@ private static bool ShouldUseSimpleLoop(int searchSpaceLength, int valuesLength)
207207
|| (searchSpaceLength < 20 && searchSpaceLength < (valuesLength >> 1));
208208
}
209209

210-
public static int IndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength) =>
211-
IndexOfAny<SpanHelpers.DontNegate<char>>(ref searchSpace, searchSpaceLength, ref values, valuesLength);
212-
213-
public static int IndexOfAnyExcept(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength) =>
214-
IndexOfAny<SpanHelpers.Negate<char>>(ref searchSpace, searchSpaceLength, ref values, valuesLength);
215-
216-
public static int LastIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength) =>
217-
LastIndexOfAny<SpanHelpers.DontNegate<char>>(ref searchSpace, searchSpaceLength, ref values, valuesLength);
218-
219-
public static int LastIndexOfAnyExcept(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength) =>
220-
LastIndexOfAny<SpanHelpers.Negate<char>>(ref searchSpace, searchSpaceLength, ref values, valuesLength);
221-
222-
private static int IndexOfAny<TNegator>(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
223-
where TNegator : struct, SpanHelpers.INegator<char>
210+
public static int IndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
224211
{
225212
var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
226213

227214
// If the search space is relatively short compared to the needle, do a simple O(n * m) search.
228215
if (ShouldUseSimpleLoop(searchSpaceLength, valuesLength))
229216
{
230-
ref char searchSpaceEnd = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
231-
ref char cur = ref searchSpace;
217+
return IndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.DontNegate>(ref searchSpace, searchSpaceLength, valuesSpan);
218+
}
232219

233-
while (!Unsafe.AreSame(ref cur, ref searchSpaceEnd))
234-
{
235-
char c = cur;
236-
if (TNegator.NegateIfNeeded(Contains(valuesSpan, c)))
237-
{
238-
return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref cur) / sizeof(char));
239-
}
220+
if (IndexOfAnyAsciiSearcher.TryIndexOfAny(ref searchSpace, searchSpaceLength, valuesSpan, out int index))
221+
{
222+
return index;
223+
}
240224

241-
cur = ref Unsafe.Add(ref cur, 1);
242-
}
225+
return ProbabilisticIndexOfAny(ref searchSpace, searchSpaceLength, ref values, valuesLength);
226+
}
243227

244-
return -1;
245-
}
228+
public static int IndexOfAnyExcept(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
229+
{
230+
var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
246231

247-
if (typeof(TNegator) == typeof(SpanHelpers.DontNegate<char>)
248-
? IndexOfAnyAsciiSearcher.TryIndexOfAny(ref searchSpace, searchSpaceLength, valuesSpan, out int index)
249-
: IndexOfAnyAsciiSearcher.TryIndexOfAnyExcept(ref searchSpace, searchSpaceLength, valuesSpan, out index))
232+
if (IndexOfAnyAsciiSearcher.IsVectorizationSupported &&
233+
!ShouldUseSimpleLoop(searchSpaceLength, valuesLength) &&
234+
IndexOfAnyAsciiSearcher.TryIndexOfAnyExcept(ref searchSpace, searchSpaceLength, valuesSpan, out int index))
250235
{
251236
return index;
252237
}
253238

254-
return ProbabilisticIndexOfAny<TNegator>(ref searchSpace, searchSpaceLength, ref values, valuesLength);
239+
return IndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.Negate>(ref searchSpace, searchSpaceLength, valuesSpan);
255240
}
256241

257-
private static int LastIndexOfAny<TNegator>(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
258-
where TNegator : struct, SpanHelpers.INegator<char>
242+
public static int LastIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
259243
{
260244
var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
261245

262246
// If the search space is relatively short compared to the needle, do a simple O(n * m) search.
263247
if (ShouldUseSimpleLoop(searchSpaceLength, valuesLength))
264248
{
265-
for (int i = searchSpaceLength - 1; i >= 0; i--)
266-
{
267-
char c = Unsafe.Add(ref searchSpace, i);
268-
if (TNegator.NegateIfNeeded(Contains(valuesSpan, c)))
269-
{
270-
return i;
271-
}
272-
}
249+
return LastIndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.DontNegate>(ref searchSpace, searchSpaceLength, valuesSpan);
250+
}
273251

274-
return -1;
252+
if (IndexOfAnyAsciiSearcher.TryLastIndexOfAny(ref searchSpace, searchSpaceLength, valuesSpan, out int index))
253+
{
254+
return index;
275255
}
276256

277-
if (typeof(TNegator) == typeof(SpanHelpers.DontNegate<char>)
278-
? IndexOfAnyAsciiSearcher.TryLastIndexOfAny(ref searchSpace, searchSpaceLength, valuesSpan, out int index)
279-
: IndexOfAnyAsciiSearcher.TryLastIndexOfAnyExcept(ref searchSpace, searchSpaceLength, valuesSpan, out index))
257+
return ProbabilisticLastIndexOfAny(ref searchSpace, searchSpaceLength, ref values, valuesLength);
258+
}
259+
260+
public static int LastIndexOfAnyExcept(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
261+
{
262+
var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
263+
264+
if (IndexOfAnyAsciiSearcher.IsVectorizationSupported &&
265+
!ShouldUseSimpleLoop(searchSpaceLength, valuesLength) &&
266+
IndexOfAnyAsciiSearcher.TryLastIndexOfAnyExcept(ref searchSpace, searchSpaceLength, valuesSpan, out int index))
280267
{
281268
return index;
282269
}
283270

284-
return ProbabilisticLastIndexOfAny<TNegator>(ref searchSpace, searchSpaceLength, ref values, valuesLength);
271+
return LastIndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.Negate>(ref searchSpace, searchSpaceLength, valuesSpan);
285272
}
286273

287274
[MethodImpl(MethodImplOptions.NoInlining)]
288-
private static int ProbabilisticIndexOfAny<TNegator>(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
289-
where TNegator : struct, SpanHelpers.INegator<char>
275+
private static int ProbabilisticIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
290276
{
291277
var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
292278

293279
var map = new ProbabilisticMap(valuesSpan);
294280
ref uint charMap = ref Unsafe.As<ProbabilisticMap, uint>(ref map);
295281

296-
return typeof(TNegator) == typeof(SpanHelpers.DontNegate<char>)
297-
? IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref charMap, ref searchSpace, searchSpaceLength, valuesSpan)
298-
: IndexOfAny<IndexOfAnyAsciiSearcher.Negate>(ref charMap, ref searchSpace, searchSpaceLength, valuesSpan);
282+
return IndexOfAny(ref charMap, ref searchSpace, searchSpaceLength, valuesSpan);
299283
}
300284

301285
[MethodImpl(MethodImplOptions.NoInlining)]
302-
private static int ProbabilisticLastIndexOfAny<TNegator>(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
303-
where TNegator : struct, SpanHelpers.INegator<char>
286+
private static int ProbabilisticLastIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
304287
{
305288
var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
306289

307290
var map = new ProbabilisticMap(valuesSpan);
308291
ref uint charMap = ref Unsafe.As<ProbabilisticMap, uint>(ref map);
309292

310-
return typeof(TNegator) == typeof(SpanHelpers.DontNegate<char>)
311-
? LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref charMap, ref searchSpace, searchSpaceLength, valuesSpan)
312-
: LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate>(ref charMap, ref searchSpace, searchSpaceLength, valuesSpan);
293+
return LastIndexOfAny(ref charMap, ref searchSpace, searchSpaceLength, valuesSpan);
313294
}
314295

315296
[MethodImpl(MethodImplOptions.AggressiveInlining)]
316-
internal static int IndexOfAny<TNegator>(ref uint charMap, ref char searchSpace, int searchSpaceLength, ReadOnlySpan<char> values)
317-
where TNegator : struct, IndexOfAnyAsciiSearcher.INegator
297+
internal static int IndexOfAny(ref uint charMap, ref char searchSpace, int searchSpaceLength, ReadOnlySpan<char> values)
318298
{
319-
if ((Sse41.IsSupported || AdvSimd.Arm64.IsSupported) && typeof(TNegator) == typeof(IndexOfAnyAsciiSearcher.DontNegate) && searchSpaceLength >= 16)
299+
if ((Sse41.IsSupported || AdvSimd.Arm64.IsSupported) && searchSpaceLength >= 16)
320300
{
321301
return IndexOfAnyVectorized(ref charMap, ref searchSpace, searchSpaceLength, values);
322302
}
@@ -327,7 +307,7 @@ internal static int IndexOfAny<TNegator>(ref uint charMap, ref char searchSpace,
327307
while (!Unsafe.AreSame(ref cur, ref searchSpaceEnd))
328308
{
329309
int ch = cur;
330-
if (TNegator.NegateIfNeeded(Contains(ref charMap, values, ch)))
310+
if (Contains(ref charMap, values, ch))
331311
{
332312
return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref cur) / sizeof(char));
333313
}
@@ -339,13 +319,12 @@ internal static int IndexOfAny<TNegator>(ref uint charMap, ref char searchSpace,
339319
}
340320

341321
[MethodImpl(MethodImplOptions.AggressiveInlining)]
342-
internal static int LastIndexOfAny<TNegator>(ref uint charMap, ref char searchSpace, int searchSpaceLength, ReadOnlySpan<char> values)
343-
where TNegator : struct, IndexOfAnyAsciiSearcher.INegator
322+
internal static int LastIndexOfAny(ref uint charMap, ref char searchSpace, int searchSpaceLength, ReadOnlySpan<char> values)
344323
{
345324
for (int i = searchSpaceLength - 1; i >= 0; i--)
346325
{
347326
int ch = Unsafe.Add(ref searchSpace, i);
348-
if (TNegator.NegateIfNeeded(Contains(ref charMap, values, ch)))
327+
if (Contains(ref charMap, values, ch))
349328
{
350329
return i;
351330
}
@@ -461,5 +440,42 @@ private static int IndexOfAnyVectorized(ref uint charMap, ref char searchSpace,
461440

462441
return -1;
463442
}
443+
444+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
445+
internal static int IndexOfAnySimpleLoop<TNegator>(ref char searchSpace, int searchSpaceLength, ReadOnlySpan<char> values)
446+
where TNegator : struct, IndexOfAnyAsciiSearcher.INegator
447+
{
448+
ref char searchSpaceEnd = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
449+
ref char cur = ref searchSpace;
450+
451+
while (!Unsafe.AreSame(ref cur, ref searchSpaceEnd))
452+
{
453+
char c = cur;
454+
if (TNegator.NegateIfNeeded(Contains(values, c)))
455+
{
456+
return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref cur) / sizeof(char));
457+
}
458+
459+
cur = ref Unsafe.Add(ref cur, 1);
460+
}
461+
462+
return -1;
463+
}
464+
465+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
466+
internal static int LastIndexOfAnySimpleLoop<TNegator>(ref char searchSpace, int searchSpaceLength, ReadOnlySpan<char> values)
467+
where TNegator : struct, IndexOfAnyAsciiSearcher.INegator
468+
{
469+
for (int i = searchSpaceLength - 1; i >= 0; i--)
470+
{
471+
char c = Unsafe.Add(ref searchSpace, i);
472+
if (TNegator.NegateIfNeeded(Contains(values, c)))
473+
{
474+
return i;
475+
}
476+
}
477+
478+
return -1;
479+
}
464480
}
465481
}

0 commit comments

Comments
 (0)