【发布时间】:2018-12-24 05:31:48
【问题描述】:
using System.Buffers;
const byte carriageReturn = (byte)'\r';
const int arbitrarySliceStart = 5;
// using Memory<T>
async Task<int> ReadAsyncWithMemory(Stream sourceStream, int bufferSize)
{
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
var bytesRead = await sourceStream.ReadAsync(buffer);
var memory = buffer.AsMemory(arbitrarySliceStart, bytesRead);
var endOfNumberIndex = memory.Span.IndexOf(carriageReturn);
var memoryChunk = memory.Slice(0, endOfNumberIndex);
var number = BitConverter.ToInt32(memoryChunk.Span);
ArrayPool<byte>.Shared.Return(buffer);
return number;
}
// using Span<T> without assigning to variable
async Task<int> ReadAsyncWithSpan(Stream sourceStream, int bufferSize)
{
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
var bytesRead = await sourceStream.ReadAsync(buffer);
var endOfNumberIndex = buffer.AsSpan(arbitrarySliceStart, bytesRead).IndexOf(carriageReturn);
var number = BitConverter.ToInt32(buffer.AsSpan(arbitrarySliceStart, bytesRead).Slice(0, endOfNumberIndex));
ArrayPool<byte>.Shared.Return(buffer);
return number;
}
// using Span<T> with additional local or private function
async Task<int> ReadAsyncWithSpanAndAdditionalFunction(Stream sourceStream, int bufferSize)
{
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
var bytesRead = await sourceStream.ReadAsync(buffer);
var number = SliceNumer();
ArrayPool<byte>.Shared.Return(buffer);
return number;
int SliceNumer()
{
var span = buffer.AsSpan(arbitrarySliceStart, bytesRead);
var endOfNumberIndex = span.IndexOf(carriageReturn);
var numberSlice = span.Slice(0, endOfNumberIndex);
return BitConverter.ToInt32(numberSlice);
}
}
我阅读了关于Span<T> 的MSDN 和CodeMag 文章,但我仍然对它们的性能有疑问。
我知道Span<T> 比Memory<T> 性能更高,但我想我想知道到什么程度。我发布了 3 个示例方法,我想知道哪种方法最好。
1.仅限Memory<T>
第一个函数ReadAsyncWithMemory,只使用Memory<T>来处理工作,非常简单。
2。 Span<T> 没有局部变量
在第二个函数中,改用ReadAsyncWithSpan,Span<T>,但是没有创建局部变量,并且调用了两次buffer.AsSpan(arbitrarySliceStart, bytesRead),看起来很笨拙。但是,如果 Span<T> 比 Memory<T> 性能更高,是否值得双重调用?
2。 Span<T> 有附加功能
在第三个函数ReadAsyncWithSpanAndAdditionalFunction中,引入了一个局部函数,以便Span<T>可以用于内存操作。现在的问题是,调用一个新函数并引入一个新堆栈帧是否值得使用Span<T> 而不是Memory<T> 带来的性能提升?
最后的问题
- 为跨度添加局部变量会导致额外开销吗?
- 只内联
Span<T>而不将其分配给变量是否值得失去可读性?
- 只内联
- 为了使用
Span<T>而不是Memory<T>调用附加函数是否值得新函数和堆栈帧的开销? - 当
Memory<T>仅被限制为堆栈帧而不分配给堆时,其性能是否明显低于Span<T>?
【问题讨论】:
-
您是否尝试过使用 benchmarkDotNet 或类似工具对此进行基准测试?
-
我不介意这样做,但我想听听一些解释,说明为什么一个比另一个更好。我想这三个的表现也很相似。
-
buffer.AsSpan(arbitrarySliceStart, bytesRead).Slice(0, endOfNumberIndex)与buffer.AsSpan(arbitrarySliceStart, endOfNumberIndex)相同。
标签: c# .net memory memory-management .net-core