diff --git a/scripts/PerfHarness/PerfHarness.csproj b/scripts/PerfHarness/PerfHarness.csproj
index b0481ffeb45..f20842d91b4 100644
--- a/scripts/PerfHarness/PerfHarness.csproj
+++ b/scripts/PerfHarness/PerfHarness.csproj
@@ -1,7 +1,7 @@
- netcoreapp1.0
+ netcoreapp1.1
portable
PerfHarness
Exe
diff --git a/src/System.Buffers.Experimental/System.Buffers.Experimental.csproj b/src/System.Buffers.Experimental/System.Buffers.Experimental.csproj
index 68093f5e0bb..8ed8a853784 100644
--- a/src/System.Buffers.Experimental/System.Buffers.Experimental.csproj
+++ b/src/System.Buffers.Experimental/System.Buffers.Experimental.csproj
@@ -13,5 +13,6 @@
+
diff --git a/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs b/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs
index 5e2f66363c7..b8f10a949e4 100644
--- a/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs
+++ b/src/System.Buffers.Experimental/System/Buffers/BufferExtensions.cs
@@ -3,6 +3,7 @@
using System.Collections.Sequences;
using System.Diagnostics;
+using System.Numerics;
using System.Runtime.CompilerServices;
namespace System.Buffers
@@ -117,5 +118,107 @@ internal static int IndexOfStraddling(this ReadOnlySpan first, IReadOnlyMe
return -1;
}
+
+ static readonly int s_longSize = Vector.Count;
+ static readonly int s_byteSize = Vector.Count;
+
+ public static int IndexOfVectorized(this Span buffer, byte value)
+ {
+ Debug.Assert(s_longSize == 4 || s_longSize == 2);
+
+ var byteSize = s_byteSize;
+
+ if (buffer.Length < byteSize * 2 || !Vector.IsHardwareAccelerated) return buffer.IndexOf(value);
+
+ Vector match = new Vector(value);
+ var vectors = buffer.NonPortableCast>();
+ var zero = Vector.Zero;
+
+ for (int vectorIndex = 0; vectorIndex < vectors.Length; vectorIndex++)
+ {
+ var vector = vectors.GetItem(vectorIndex);
+ var result = Vector.Equals(vector, match);
+ if (result != zero)
+ {
+ var longer = Vector.AsVectorUInt64(result);
+ Debug.Assert(s_longSize == 4 || s_longSize == 2);
+
+ var candidate = longer[0];
+ if (candidate != 0) return vectorIndex * byteSize + IndexOf(candidate);
+ candidate = longer[1];
+ if (candidate != 0) return 8 + vectorIndex * byteSize + IndexOf(candidate);
+ if (s_longSize == 4)
+ {
+ candidate = longer[2];
+ if (candidate != 0) return 16 + vectorIndex * byteSize + IndexOf(candidate);
+ candidate = longer[3];
+ if (candidate != 0) return 24 + vectorIndex * byteSize + IndexOf(candidate);
+ }
+ }
+ }
+
+ var processed = vectors.Length * byteSize;
+ var index = buffer.Slice(processed).IndexOf(value);
+ if (index == -1) return -1;
+ return index + processed;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int IndexOfVectorized(this ReadOnlySpan buffer, byte value)
+ {
+ Debug.Assert(s_longSize == 4 || s_longSize == 2);
+
+ var byteSize = s_byteSize;
+
+ if (buffer.Length < byteSize * 2 || !Vector.IsHardwareAccelerated) return buffer.IndexOf(value);
+
+ Vector match = new Vector(value);
+ var vectors = buffer.NonPortableCast>();
+ var zero = Vector.Zero;
+
+ for (int vectorIndex = 0; vectorIndex < vectors.Length; vectorIndex++)
+ {
+ var vector = vectors[vectorIndex];
+ var result = Vector.Equals(vector, match);
+ if (result != zero)
+ {
+ var longer = Vector.AsVectorUInt64(result);
+ var candidate = longer[0];
+ if (candidate != 0) return vectorIndex * byteSize + IndexOf(candidate);
+ candidate = longer[1];
+ if (candidate != 0) return 8 + vectorIndex * byteSize + IndexOf(candidate);
+ if (s_longSize == 4)
+ {
+ candidate = longer[2];
+ if (candidate != 0) return 16 + vectorIndex * byteSize + IndexOf(candidate);
+ candidate = longer[3];
+ if (candidate != 0) return 24 + vectorIndex * byteSize + IndexOf(candidate);
+ }
+ }
+ }
+
+ var processed = vectors.Length * byteSize;
+ var index = buffer.Slice(processed).IndexOf(value);
+ if (index == -1) return -1;
+ return index + processed;
+ }
+
+ // used by IndexOfVectorized
+ static int IndexOf(ulong next)
+ {
+ // Flag least significant power of two bit
+ var powerOfTwoFlag = (next ^ (next - 1));
+ // Shift all powers of two into the high byte and extract
+ var foundByteIndex = (int)((powerOfTwoFlag * _xorPowerOfTwoToHighByte) >> 57);
+ return foundByteIndex;
+ }
+
+ const ulong _xorPowerOfTwoToHighByte = (0x07ul |
+ 0x06ul << 8 |
+ 0x05ul << 16 |
+ 0x04ul << 24 |
+ 0x03ul << 32 |
+ 0x02ul << 40 |
+ 0x01ul << 48) + 1;
}
}
\ No newline at end of file
diff --git a/tests/Benchmarks/Benchmarks.csproj b/tests/Benchmarks/Benchmarks.csproj
index 62fa2d33b56..e8494532356 100644
--- a/tests/Benchmarks/Benchmarks.csproj
+++ b/tests/Benchmarks/Benchmarks.csproj
@@ -1,6 +1,6 @@
- netcoreapp1.0
+ netcoreapp1.1
False
../../tools/test_key.snk
true
diff --git a/tests/Benchmarks/IndexOf.cs b/tests/Benchmarks/IndexOf.cs
new file mode 100644
index 00000000000..9ce0431a444
--- /dev/null
+++ b/tests/Benchmarks/IndexOf.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Xunit.Performance;
+using System;
+using System.Buffers;
+using System.Numerics;
+using System.Text;
+
+public class IndexOfBench
+{
+ static int s_bufferLength = 1000;
+ static byte[] s_buffer = new byte[s_bufferLength];
+ static int s_loops = 1000;
+
+ static IndexOfBench()
+ {
+ s_buffer[s_bufferLength - 100] = 255;
+ }
+
+ [Benchmark]
+ static int SpanIndexOf()
+ {
+ Span buffer = s_buffer;
+ int index = 0;
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ using (iteration.StartMeasurement())
+ {
+ for(int i=0; i buffer = s_buffer;
+ int index = 0;
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ using (iteration.StartMeasurement())
+ {
+ for(int i=0; i span = buffer;
+ Assert.Equal(0, span.IndexOfVectorized(1));
+ Assert.Equal(len/2, span.IndexOfVectorized(2));
+ Assert.Equal(len-1, span.IndexOfVectorized(3));
+ Assert.Equal(-1, span.IndexOfVectorized(4));
+ }
+
+ [Fact]
+ public void ReadOnlySpanIndexOf()
+ {
+ int len = 10000;
+ byte[] buffer = new byte[len];
+ buffer[0] = 1;
+ buffer[len / 2] = 2;
+ buffer[len - 1] = 3;
+
+ ReadOnlySpan span = buffer;
+ Assert.Equal(0, span.IndexOfVectorized(1));
+ Assert.Equal(len/2, span.IndexOfVectorized(2));
+ Assert.Equal(len-1, span.IndexOfVectorized(3));
+ Assert.Equal(-1, span.IndexOfVectorized(4));
+ }
+
+ [Fact]
+ public void EmptySpanIndexOf()
+ {
+ int len = 0;
+ byte[] buffer = new byte[len];
+ Span span = buffer;
+ Assert.Equal(-1, span.IndexOfVectorized(4));
+ }
+
+ [Fact]
+ public void EmptyReadOnlySpanIndexOf()
+ {
+ int len = 10000;
+ byte[] buffer = new byte[len];
+ buffer[0] = 1;
+ buffer[len / 2] = 2;
+ buffer[len - 1] = 3;
+
+ ReadOnlySpan span = buffer;
+ Assert.Equal(-1, span.IndexOfVectorized(4));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/System.Text.Formatting.Tests/System.Text.Formatting.Tests.csproj b/tests/System.Text.Formatting.Tests/System.Text.Formatting.Tests.csproj
index 99d39cc2c4e..669b8be1b01 100644
--- a/tests/System.Text.Formatting.Tests/System.Text.Formatting.Tests.csproj
+++ b/tests/System.Text.Formatting.Tests/System.Text.Formatting.Tests.csproj
@@ -16,6 +16,7 @@
+