我想插入几个Memory / Span缓冲区。是否可以在不重复访问内部循环中的Span属性的情况下执行此操作?
public static void Interleave(StreamWriter s, ReadOnlyMemory<float>[] data)
{
for (int i = 0; i < data.First().Length; i++)
{
for (int j = 0; j < data.Length; j++)
{
s.Write(data[j].Span[i]);
}
}
}
答案 0 :(得分:0)
我没有找到一种表示Span<int>[]
或Span<Span<int>>
的方法,但是我使用了一个递归不安全的函数来通过int**
指针来实现类似的结果。不幸的是,它不能很好地概括。
| Method | Mean | Error | StdDev |
|----------------------- |---------:|----------:|----------:|
| InterleaveArray | 1.405 ms | 0.0125 ms | 0.0111 ms |
| InterleaveMemorySpan | 4.232 ms | 0.0659 ms | 0.0616 ms |
| UnsafeInterleaveMemory | 1.374 ms | 0.0143 ms | 0.0134 ms |
请注意,重复访问Memory上的Span属性比其他两种实现慢3倍。这是我使用的代码:
public class InterleaveTests
{
private Action<int> act = x => { };
public static int[][] CreateBuffers(int BufferCount, int BufferSize)
{
return Enumerable.Range(0, BufferCount)
.Select(i => Enumerable.Range(0, BufferSize)
.Select(j => j * BufferCount + i).ToArray()).ToArray();
}
[Benchmark]
[ArgumentsSource(nameof(MemoryNumbers))]
public void InterleaveMemorySpan(Memory<int>[] data)
{
var rows = data.First().Length;
for (int i = 0; i < rows; i++)
for (int j = 0; j < data.Length; j++)
act(data[j].Span[i]);
}
[Benchmark]
[ArgumentsSource(nameof(MemoryNumbers))]
public unsafe void UnsafeInterleaveMemory(Memory<int>[] data)
{
var dataBuffers = stackalloc int*[data.Length];
Pin(data.AsSpan(), 0);
void Pin(Span<Memory<int>> buffers, int bufferIndex)
{
if (buffers.Length == 0)
{
InterleaveMemory(data.First().Length, data.Length, dataBuffers);
}
else
{
fixed (int* pData = buffers[0].Span)
{
dataBuffers[bufferIndex] = pData;
Pin(buffers.Slice(1), bufferIndex + 1);
}
}
}
void InterleaveMemory(int rows, int columns, int** dataSpans)
{
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
act(dataSpans[j][i]);
}
}
[Benchmark]
[ArgumentsSource(nameof(ArrayNumbers))]
public void InterleaveArray(int[][] data)
{
var rows = data.First().Length;
for (int i = 0; i < rows; i++)
for (int j = 0; j < data.Length; j++)
act(data[j][i]);
}
public IEnumerable<int[][]> ArrayNumbers()
{
yield return CreateBuffers(16, 32 * 1024);
}
public IEnumerable<Memory<int>[]> MemoryNumbers()
{
yield return CreateBuffers(16, 32 * 1024).Select(x => x.AsMemory()).ToArray();
}
}