Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 018df68

Browse files
benaadamstannergooding
authored andcommitted
Additionally Vectorize string.IndexOfAny for value lengths 2,3,4,5 (#19790)
* Vectorize string.IndexOfAny * Vectorize string.IndexOfAny [4,5] * Feedback * Call order preference
1 parent c0aa74f commit 018df68

File tree

3 files changed

+554
-68
lines changed

3 files changed

+554
-68
lines changed

‎src/System.Private.CoreLib/shared/System/MemoryExtensions.cs‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,12 @@ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
486486
Unsafe.As<T, byte>(ref value0),
487487
Unsafe.As<T, byte>(ref value1),
488488
span.Length);
489+
if (typeof(T) == typeof(char))
490+
return SpanHelpers.IndexOfAny(
491+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
492+
Unsafe.As<T, char>(ref value0),
493+
Unsafe.As<T, char>(ref value1),
494+
span.Length);
489495

490496
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
491497
}
@@ -508,6 +514,13 @@ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
508514
Unsafe.As<T, byte>(ref value1),
509515
Unsafe.As<T, byte>(ref value2),
510516
span.Length);
517+
if (typeof(T) == typeof(char))
518+
return SpanHelpers.IndexOfAny(
519+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
520+
Unsafe.As<T, char>(ref value0),
521+
Unsafe.As<T, char>(ref value1),
522+
Unsafe.As<T, char>(ref value2),
523+
span.Length);
511524

512525
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
513526
}
@@ -522,11 +535,67 @@ public static int IndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
522535
where T : IEquatable<T>
523536
{
524537
if (typeof(T) == typeof(byte))
538+
{
525539
return SpanHelpers.IndexOfAny(
526540
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
527541
span.Length,
528542
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
529543
values.Length);
544+
}
545+
if (typeof(T) == typeof(char))
546+
{
547+
ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
548+
if (values.Length == 5)
549+
{
550+
// Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
551+
return SpanHelpers.IndexOfAny(
552+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
553+
valueRef,
554+
Unsafe.Add(ref valueRef, 1),
555+
Unsafe.Add(ref valueRef, 2),
556+
Unsafe.Add(ref valueRef, 3),
557+
Unsafe.Add(ref valueRef, 4),
558+
span.Length);
559+
}
560+
else if (values.Length == 2)
561+
{
562+
// Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
563+
return SpanHelpers.IndexOfAny(
564+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
565+
valueRef,
566+
Unsafe.Add(ref valueRef, 1),
567+
span.Length);
568+
}
569+
else if (values.Length == 4)
570+
{
571+
// Length 4 before 3 as 3 has an explicit overload
572+
return SpanHelpers.IndexOfAny(
573+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
574+
valueRef,
575+
Unsafe.Add(ref valueRef, 1),
576+
Unsafe.Add(ref valueRef, 2),
577+
Unsafe.Add(ref valueRef, 3),
578+
span.Length);
579+
}
580+
else if (values.Length == 3)
581+
{
582+
return SpanHelpers.IndexOfAny(
583+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
584+
valueRef,
585+
Unsafe.Add(ref valueRef, 1),
586+
Unsafe.Add(ref valueRef, 2),
587+
span.Length);
588+
}
589+
else if (values.Length == 1)
590+
{
591+
// Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
592+
// is already throwing away a bunch of performance vs just calling IndexOf
593+
return SpanHelpers.IndexOf(
594+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
595+
valueRef,
596+
span.Length);
597+
}
598+
}
530599

531600
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
532601
}
@@ -547,6 +616,12 @@ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
547616
Unsafe.As<T, byte>(ref value0),
548617
Unsafe.As<T, byte>(ref value1),
549618
span.Length);
619+
if (typeof(T) == typeof(char))
620+
return SpanHelpers.IndexOfAny(
621+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
622+
Unsafe.As<T, char>(ref value0),
623+
Unsafe.As<T, char>(ref value1),
624+
span.Length);
550625

551626
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
552627
}
@@ -569,6 +644,13 @@ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
569644
Unsafe.As<T, byte>(ref value1),
570645
Unsafe.As<T, byte>(ref value2),
571646
span.Length);
647+
if (typeof(T) == typeof(char))
648+
return SpanHelpers.IndexOfAny(
649+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
650+
Unsafe.As<T, char>(ref value0),
651+
Unsafe.As<T, char>(ref value1),
652+
Unsafe.As<T, char>(ref value2),
653+
span.Length);
572654

573655
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
574656
}
@@ -589,6 +671,61 @@ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
589671
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
590672
values.Length);
591673

674+
if (typeof(T) == typeof(char))
675+
{
676+
ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
677+
if (values.Length == 5)
678+
{
679+
// Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
680+
return SpanHelpers.IndexOfAny(
681+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
682+
valueRef,
683+
Unsafe.Add(ref valueRef, 1),
684+
Unsafe.Add(ref valueRef, 2),
685+
Unsafe.Add(ref valueRef, 3),
686+
Unsafe.Add(ref valueRef, 4),
687+
span.Length);
688+
}
689+
else if (values.Length == 2)
690+
{
691+
// Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
692+
return SpanHelpers.IndexOfAny(
693+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
694+
valueRef,
695+
Unsafe.Add(ref valueRef, 1),
696+
span.Length);
697+
}
698+
else if (values.Length == 4)
699+
{
700+
// Length 4 before 3 as 3 has an explicit overload
701+
return SpanHelpers.IndexOfAny(
702+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
703+
valueRef,
704+
Unsafe.Add(ref valueRef, 1),
705+
Unsafe.Add(ref valueRef, 2),
706+
Unsafe.Add(ref valueRef, 3),
707+
span.Length);
708+
}
709+
else if (values.Length == 3)
710+
{
711+
return SpanHelpers.IndexOfAny(
712+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
713+
valueRef,
714+
Unsafe.Add(ref valueRef, 1),
715+
Unsafe.Add(ref valueRef, 2),
716+
span.Length);
717+
}
718+
else if (values.Length == 1)
719+
{
720+
// Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
721+
// is already throwing away a bunch of performance vs just calling IndexOf
722+
return SpanHelpers.IndexOf(
723+
ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
724+
valueRef,
725+
span.Length);
726+
}
727+
}
728+
592729
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
593730
}
594731

0 commit comments

Comments
 (0)