mirror of
https://github.com/chylex/.NET-Community-Toolkit.git
synced 2025-04-13 20:15:45 +02:00
Code refactoring to support Memory<T> tests
This commit is contained in:
parent
9ace1401d5
commit
9dc5481b47
UnitTests/UnitTests.HighPerformance.Shared
Buffers/Internals
Extensions
Helpers
@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -12,7 +13,7 @@ namespace UnitTests.HighPerformance.Shared.Buffers.Internals
|
||||
/// An owner for a buffer of an unmanaged type, recycling <see cref="byte"/> arrays to save memory.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of items to store in the rented buffers.</typeparam>
|
||||
internal sealed unsafe class UnmanagedSpanOwner<T> : IDisposable
|
||||
internal sealed unsafe class UnmanagedSpanOwner<T> : MemoryManager<T>
|
||||
where T : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
@ -45,13 +46,8 @@ public UnmanagedSpanOwner(int size)
|
||||
/// </summary>
|
||||
public T* Ptr => (T*)this.ptr;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Memory{T}"/> for the current instance.
|
||||
/// </summary>
|
||||
public Span<T> Span => new Span<T>((void*)this.ptr, this.length);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
IntPtr ptr = this.ptr;
|
||||
|
||||
@ -64,5 +60,23 @@ public void Dispose()
|
||||
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Span<T> GetSpan()
|
||||
{
|
||||
return new Span<T>((void*)this.ptr, this.length);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MemoryHandle Pin(int elementIndex = 0)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Unpin()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,8 +178,8 @@ private static void TestForType<T>(T value, Func<int, T, UnmanagedSpanOwner<T>>
|
||||
{
|
||||
using UnmanagedSpanOwner<T> data = provider(count, value);
|
||||
|
||||
int result = data.Span.Count(value);
|
||||
int expected = CountWithForeach(data.Span, value);
|
||||
int result = data.GetSpan().Count(value);
|
||||
int expected = CountWithForeach(data.GetSpan(), value);
|
||||
|
||||
Assert.AreEqual(result, expected, $"Failed {typeof(T)} test with count {count}: got {result} instead of {expected}");
|
||||
}
|
||||
@ -224,7 +224,7 @@ private static UnmanagedSpanOwner<T> CreateRandomData<T>(int count, T value)
|
||||
|
||||
UnmanagedSpanOwner<T> data = new UnmanagedSpanOwner<T>(count);
|
||||
|
||||
foreach (ref byte n in MemoryMarshal.AsBytes(data.Span))
|
||||
foreach (ref byte n in MemoryMarshal.AsBytes(data.GetSpan()))
|
||||
{
|
||||
n = (byte)random.Next(0, byte.MaxValue);
|
||||
}
|
||||
@ -232,7 +232,7 @@ private static UnmanagedSpanOwner<T> CreateRandomData<T>(int count, T value)
|
||||
// Fill at least 20% of the items with a matching value
|
||||
int minimum = count / 20;
|
||||
|
||||
Span<T> span = data.Span;
|
||||
Span<T> span = data.GetSpan();
|
||||
|
||||
for (int i = 0; i < minimum; i++)
|
||||
{
|
||||
@ -255,7 +255,7 @@ private static UnmanagedSpanOwner<T> CreateFilledData<T>(int count, T value)
|
||||
{
|
||||
UnmanagedSpanOwner<T> data = new UnmanagedSpanOwner<T>(count);
|
||||
|
||||
data.Span.Fill(value);
|
||||
data.GetSpan().Fill(value);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -102,8 +102,8 @@ private static void TestForType<T>()
|
||||
{
|
||||
using UnmanagedSpanOwner<T> data = CreateRandomData<T>(count);
|
||||
|
||||
int hash1 = HashCode<T>.Combine(data.Span);
|
||||
int hash2 = HashCode<T>.Combine(data.Span);
|
||||
int hash1 = HashCode<T>.Combine(data.GetSpan());
|
||||
int hash2 = HashCode<T>.Combine(data.GetSpan());
|
||||
|
||||
Assert.AreEqual(hash1, hash2, $"Failed {typeof(T)} test with count {count}: got {hash1} and then {hash2}");
|
||||
}
|
||||
@ -123,7 +123,7 @@ private static UnmanagedSpanOwner<T> CreateRandomData<T>(int count)
|
||||
|
||||
UnmanagedSpanOwner<T> data = new UnmanagedSpanOwner<T>(count);
|
||||
|
||||
foreach (ref byte n in MemoryMarshal.AsBytes(data.Span))
|
||||
foreach (ref byte n in MemoryMarshal.AsBytes(data.GetSpan()))
|
||||
{
|
||||
n = (byte)random.Next(0, byte.MaxValue);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
using Microsoft.Toolkit.HighPerformance.Extensions;
|
||||
using Microsoft.Toolkit.HighPerformance.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using UnitTests.HighPerformance.Shared.Buffers.Internals;
|
||||
|
||||
namespace UnitTests.HighPerformance.Helpers
|
||||
{
|
||||
@ -19,15 +20,17 @@ public partial class Test_ParallelHelper
|
||||
|
||||
[TestCategory("ParallelHelper")]
|
||||
[TestMethod]
|
||||
public void Test_ParallelHelper_ForWithIndices()
|
||||
public unsafe void Test_ParallelHelper_ForWithIndices()
|
||||
{
|
||||
foreach (int count in TestForCounts)
|
||||
{
|
||||
int[] data = new int[count];
|
||||
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(count);
|
||||
|
||||
ParallelHelper.For(0, data.Length, new Assigner(data));
|
||||
data.GetSpan().Clear();
|
||||
|
||||
foreach (var item in data.Enumerate())
|
||||
ParallelHelper.For(0, data.Length, new Assigner(data.Length, data.Ptr));
|
||||
|
||||
foreach (var item in data.GetSpan().Enumerate())
|
||||
{
|
||||
if (item.Index != item.Value)
|
||||
{
|
||||
@ -56,15 +59,17 @@ public void Test_ParallelHelper_ForInvalidRange_RangeAll()
|
||||
|
||||
[TestCategory("ParallelHelper")]
|
||||
[TestMethod]
|
||||
public void Test_ParallelHelper_ForWithRanges()
|
||||
public unsafe void Test_ParallelHelper_ForWithRanges()
|
||||
{
|
||||
foreach (int count in TestForCounts)
|
||||
{
|
||||
int[] data = new int[count];
|
||||
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(count);
|
||||
|
||||
ParallelHelper.For(..data.Length, new Assigner(data));
|
||||
data.GetSpan().Clear();
|
||||
|
||||
foreach (var item in data.Enumerate())
|
||||
ParallelHelper.For(..data.Length, new Assigner(data.Length, data.Ptr));
|
||||
|
||||
foreach (var item in data.GetSpan().Enumerate())
|
||||
{
|
||||
if (item.Index != item.Value)
|
||||
{
|
||||
@ -78,21 +83,31 @@ public void Test_ParallelHelper_ForWithRanges()
|
||||
/// <summary>
|
||||
/// A type implementing <see cref="IAction"/> to initialize an array
|
||||
/// </summary>
|
||||
private readonly struct Assigner : IAction
|
||||
private readonly unsafe struct Assigner : IAction
|
||||
{
|
||||
private readonly int[] array;
|
||||
private readonly int length;
|
||||
private readonly int* ptr;
|
||||
|
||||
public Assigner(int[] array) => this.array = array;
|
||||
public Assigner(int length, int* ptr)
|
||||
{
|
||||
this.length = length;
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Invoke(int i)
|
||||
{
|
||||
if (this.array[i] != 0)
|
||||
if ((uint)i >= (uint)this.length)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid target position {i}, was {this.array[i]} instead of 0");
|
||||
throw new IndexOutOfRangeException($"The target position was out of range, was {i} and should've been in [0, {this.length})");
|
||||
}
|
||||
|
||||
this.array[i] = i;
|
||||
if (this.ptr[i] != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid target position {i}, was {this.ptr[i]} instead of 0");
|
||||
}
|
||||
|
||||
this.ptr[i] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
using System.Drawing;
|
||||
using Microsoft.Toolkit.HighPerformance.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using UnitTests.HighPerformance.Shared.Buffers.Internals;
|
||||
|
||||
namespace UnitTests.HighPerformance.Helpers
|
||||
{
|
||||
@ -27,21 +28,23 @@ public partial class Test_ParallelHelper
|
||||
|
||||
[TestCategory("ParallelHelper")]
|
||||
[TestMethod]
|
||||
public void Test_ParallelHelper_For2DWithIndices()
|
||||
public unsafe void Test_ParallelHelper_For2DWithIndices()
|
||||
{
|
||||
foreach (var size in TestFor2DSizes)
|
||||
{
|
||||
int[,] data = new int[size.Height, size.Width];
|
||||
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(size.Height * size.Width);
|
||||
|
||||
ParallelHelper.For2D(0, size.Height, 0, size.Width, new Assigner2D(data));
|
||||
data.GetSpan().Clear();
|
||||
|
||||
ParallelHelper.For2D(0, size.Height, 0, size.Width, new Assigner2D(size.Height, size.Width, data.Ptr));
|
||||
|
||||
for (int i = 0; i < size.Height; i++)
|
||||
{
|
||||
for (int j = 0; j < size.Width; j++)
|
||||
{
|
||||
if (data[i, j] != unchecked(i * 397 ^ j))
|
||||
if (data.Ptr[(i * size.Width) + j] != unchecked(i * 397 ^ j))
|
||||
{
|
||||
Assert.Fail($"Invalid item at position [{i},{j}], value was {data[i, j]} instead of {unchecked(i * 397 ^ j)}");
|
||||
Assert.Fail($"Invalid item at position [{i},{j}], value was {data.Ptr[(i * size.Width) + j]} instead of {unchecked(i * 397 ^ j)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,21 +70,23 @@ public void Test_ParallelHelper_For2DInvalidRange_RangeAll()
|
||||
|
||||
[TestCategory("ParallelHelper")]
|
||||
[TestMethod]
|
||||
public void Test_ParallelHelper_For2DWithRanges()
|
||||
public unsafe void Test_ParallelHelper_For2DWithRanges()
|
||||
{
|
||||
foreach (var size in TestFor2DSizes)
|
||||
{
|
||||
int[,] data = new int[size.Height, size.Width];
|
||||
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(size.Height * size.Width);
|
||||
|
||||
ParallelHelper.For2D(..size.Height, ..size.Width, new Assigner2D(data));
|
||||
data.GetSpan().Clear();
|
||||
|
||||
ParallelHelper.For2D(..size.Height, ..size.Width, new Assigner2D(size.Height, size.Width, data.Ptr));
|
||||
|
||||
for (int i = 0; i < size.Height; i++)
|
||||
{
|
||||
for (int j = 0; j < size.Width; j++)
|
||||
{
|
||||
if (data[i, j] != unchecked(i * 397 ^ j))
|
||||
if (data.Ptr[(i * size.Width) + j] != unchecked(i * 397 ^ j))
|
||||
{
|
||||
Assert.Fail($"Invalid item at position [{i},{j}], value was {data[i, j]} instead of {unchecked(i * 397 ^ j)}");
|
||||
Assert.Fail($"Invalid item at position [{i},{j}], value was {data.Ptr[(i * size.Width) + j]} instead of {unchecked(i * 397 ^ j)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,21 +97,34 @@ public void Test_ParallelHelper_For2DWithRanges()
|
||||
/// <summary>
|
||||
/// A type implementing <see cref="IAction"/> to initialize a 2D array
|
||||
/// </summary>
|
||||
private readonly struct Assigner2D : IAction2D
|
||||
private readonly unsafe struct Assigner2D : IAction2D
|
||||
{
|
||||
private readonly int[,] array;
|
||||
private readonly int height;
|
||||
private readonly int width;
|
||||
private readonly int* ptr;
|
||||
|
||||
public Assigner2D(int[,] array) => this.array = array;
|
||||
public Assigner2D(int height, int width, int* ptr)
|
||||
{
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Invoke(int i, int j)
|
||||
{
|
||||
if (this.array[i, j] != 0)
|
||||
if ((uint)i >= (uint)this.height ||
|
||||
(uint)j >= (uint)this.width)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid target position [{i},{j}], was {this.array[i, j]} instead of 0");
|
||||
throw new IndexOutOfRangeException($"The target position was invalid, was [{i}, {j}], should've been in [0, {this.height}] and [0, {this.width}]");
|
||||
}
|
||||
|
||||
this.array[i, j] = unchecked(i * 397 ^ j);
|
||||
if (this.ptr[(i * this.width) + j] != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid target position [{i},{j}], was {this.ptr[(i * this.width) + j]} instead of 0");
|
||||
}
|
||||
|
||||
this.ptr[(i * this.width) + j] = unchecked(i * 397 ^ j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user