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

Optimize Span<T>.Fill implementation #51365

Merged
merged 5 commits into from
Apr 17, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
129 changes: 94 additions & 35 deletions src/libraries/System.Memory/tests/Span/Fill.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Xunit;
using static System.TestHelpers;
Expand Down Expand Up @@ -151,52 +149,113 @@ public static unsafe void FillNativeBytes()
}
}

public static IEnumerable<object[]> FillData
[Fact]
public static void FillWithRecognizedType()
{
RunTest<sbyte>(0x20);
RunTest<byte>(0x20);
RunTest<bool>(true);
RunTest<short>(0x1234);
RunTest<ushort>(0x1234);
RunTest<char>('x');
RunTest<int>(0x12345678);
RunTest<uint>(0x12345678);
RunTest<long>(0x0123456789abcdef);
RunTest<ulong>(0x0123456789abcdef);
RunTest<nint>(unchecked((nint)0x0123456789abcdef));
RunTest<nuint>(unchecked((nuint)0x0123456789abcdef));
RunTest<Half>((Half)1.0);
RunTest<float>(1.0f);
RunTest<double>(1.0);
RunTest<StringComparison>(StringComparison.CurrentCultureIgnoreCase); // should be treated as underlying primitive
RunTest<string>("Hello world!"); // ref type, no SIMD
RunTest<decimal>(1.0m); // 128-bit struct
RunTest<Guid>(new Guid("29e07627-2481-4f43-8fbf-09cf21180239")); // 128-bit struct
RunTest<My96BitStruct>(new(0x11111111, 0x22222222, 0x33333333)); // 96-bit struct, no SIMD
RunTest<My256BitStruct>(new(0x1111111111111111, 0x2222222222222222, 0x3333333333333333, 0x4444444444444444));
RunTest<My512BitStruct>(new(
0x1111111111111111, 0x2222222222222222, 0x3333333333333333, 0x4444444444444444,
0x5555555555555555, 0x6666666666666666, 0x7777777777777777, 0x8888888888888888)); // 512-bit struct, no SIMD
RunTest<MyRefContainingStruct>(new("Hello world!")); // struct contains refs, no SIMD

static void RunTest<T>(T value)
{
T[] arr = new T[128];

// Run tests for lengths := 0 to 64, ensuring we don't overrun our buffer

for (int i = 0; i <= 64; i++)
{
arr.AsSpan(0, i).Fill(value);
Assert.Equal(Enumerable.Repeat(value, i), arr.Take(i)); // first i entries should've been populated with 'value'
Assert.Equal(Enumerable.Repeat(default(T), arr.Length - i), arr.Skip(i)); // remaining entries should contain default(T)
Array.Clear(arr, 0, arr.Length);
}
}
}

private readonly struct My96BitStruct
{
get
public My96BitStruct(int data0, int data1, int data2)
{
yield return new object[] { typeof(sbyte), (sbyte)0x20 };
yield return new object[] { typeof(byte), (byte)0x20 };
yield return new object[] { typeof(bool), true };
yield return new object[] { typeof(short), (short)0x1234 };
yield return new object[] { typeof(ushort), (ushort)0x1234 };
yield return new object[] { typeof(char), 'x' };
yield return new object[] { typeof(int), (int)0x12345678 };
yield return new object[] { typeof(uint), (uint)0x12345678 };
yield return new object[] { typeof(long), (long)0x0123456789abcdef };
yield return new object[] { typeof(ulong), (ulong)0x0123456789abcdef };
yield return new object[] { typeof(nint), unchecked((nint)0x0123456789abcdef) };
yield return new object[] { typeof(nuint), unchecked((nuint)0x0123456789abcdef) };
yield return new object[] { typeof(float), 1.0f };
yield return new object[] { typeof(double), 1.0d };
yield return new object[] { typeof(string), "Hello world!" }; // shouldn't go down SIMD path
yield return new object[] { typeof(decimal), 1.0m }; // shouldn't go down SIMD path
Data0 = data0;
Data1 = data1;
Data2 = data2;
}

public readonly int Data0;
public readonly int Data1;
public readonly int Data2;
}

[Theory]
[MemberData(nameof(FillData))]
public static void FillWithRecognizedType(Type expectedType, object value)
private readonly struct My256BitStruct
{
Assert.IsType(expectedType, value);
typeof(SpanTests).GetMethod(nameof(FillWithRecognizedType_RunTest), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(expectedType)
.Invoke(null, BindingFlags.DoNotWrapExceptions, null, new object[] { value }, null);
public My256BitStruct(ulong data0, ulong data1, ulong data2, ulong data3)
{
Data0 = data0;
Data1 = data1;
Data2 = data2;
Data3 = data3;
}

public readonly ulong Data0;
public readonly ulong Data1;
public readonly ulong Data2;
public readonly ulong Data3;
}

private static void FillWithRecognizedType_RunTest<T>(T value)
private readonly struct My512BitStruct
{
T[] arr = new T[128];
public My512BitStruct(ulong data0, ulong data1, ulong data2, ulong data3, ulong data4, ulong data5, ulong data6, ulong data7)
{
Data0 = data0;
Data1 = data1;
Data2 = data2;
Data3 = data3;
Data4 = data4;
Data5 = data5;
Data6 = data6;
Data7 = data7;
}

// Run tests for lengths := 0 to 64, ensuring we don't overrun our buffer
public readonly ulong Data0;
public readonly ulong Data1;
public readonly ulong Data2;
public readonly ulong Data3;
public readonly ulong Data4;
public readonly ulong Data5;
public readonly ulong Data6;
public readonly ulong Data7;
}

for (int i = 0; i <= 64; i++)
private readonly struct MyRefContainingStruct
{
public MyRefContainingStruct(object data)
{
arr.AsSpan(0, i).Fill(value);
Assert.Equal(Enumerable.Repeat(value, i), arr.Take(i)); // first i entries should've been populated with 'value'
Assert.Equal(Enumerable.Repeat(default(T), arr.Length - i), arr.Skip(i)); // remaining entries should contain default(T)
Array.Clear(arr, 0, arr.Length);
Data = data;
}

public readonly object Data;
}
}
}
Loading