using System.Buffers;const byte carriageReturn = (byte)\r;const int arbitrarySliceStart = 5;// using MemoryTasync Taskint ReadAsyncWithMemory(Stream sourceStream, int bufferSize){var buffer =...
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.存储器< T>只要
第一个函数ReadAsyncWithMemory仅使用Memory< T>.处理工作,非常简单.
2.跨度< T>没有局部变量
在第二个函数中,ReadAsyncWithSpan,Span< T>代替使用,但没有创建局部变量,并且调用buffer.AsSpan(arbitrarySliceStart,bytesRead)两次,这看起来很笨拙.但是,如果Span< T>比Memory< T>更高效,是否值得双重调用?
2.跨度< T>附加功能
在第三个函数ReadAsyncWithSpanAndAdditionalFunction中,引入了局部函数,使得Span< T>可用于内存操作.现在的问题是,调用一个新函数并引入一个新的堆栈帧,值得使用Span< T>的性能提升.超过记忆< T>?
最后的问题
>为跨度添加局部变量是否会导致额外开销?
>仅仅内联Span< T>是否值得失去可读性没有将它分配给变量?
>调用附加功能以使用Span< T>超过记忆< T>值得新功能和堆栈框架的开销?
>记忆< T>比Span< T>显着更低的性能.什么时候它只被限制在堆栈框架而没有分配给堆?
解决方法:
错误:您的示例中存在一些错误/干扰(如果在问题之外进行了删除,请删除此部分).
> AsMemory / AsSpan取一个起始索引和长度,所以buffer.AsSpan(arbitrarySliceStart,bytesRead)是一个bug,可能只是buffer.AsSpan(0,bytesRead).如果你打算跳过第一个任意的SliceStart字节读取它应该是buffer.AsSpan(arbitrarySliceStart,bytesRead-arbitrarySliceStart),可能需要检查(bytesRead< arbitrarySliceStart).
>一个完整的例子,希望读取一个整数文本字段,从一个固定的偏移量开始流入一个流并由一个回车终止,这需要一个循环来确保读取“足够的”数据(……如果“太多”被读取则处理等等),但这不在手头的主题之内.
这个问题似乎是围绕编译器在异步函数中禁止Span局部变量.希望,如果Span变量的使用/生命周期不等待“调用”,未来的版本将不会强制执行此限制.
>为跨度添加局部变量是否会导致额外开销?
没有.
好吧,它可能导致组成Span的底层指针和长度字段的额外赋值/复制操作(尽管不是它们引用的内存范围).但即使这样也应该被优化掉,或者无论如何只需要中间/临时.
这不是编译器“不喜欢”Span变量的原因.跨度变量必须保留在堆栈上,或者引用的内存可能从它们下面收集,即只要它们保留在堆栈上,引用内存的SOMETHING ELSE必须仍然在堆栈上“低于它们”.异步/等待“函数”在每次等待调用时返回,然后在“等待”任务完成时作为继续/状态机调用继续.
注意:这不仅仅是托管内存和GC,否则必须检查Spans以获取对GC跟踪对象的引用.跨度可以指非托管内存或跟踪对象的块.
>在不将其分配给变量的情况下内联Span是否值得失去可读性?
嗯,这直接是一个风格/意见问题.但是,“重新创建”Span意味着函数调用但没有分配(只是堆栈操作和访问/复制几个整数大小的项目);并且呼叫本身将是JIT内联的良好候选者.
>调用一个额外的函数,以便使用Span over Memory值得新函数和堆栈帧的开销吗?
好吧,获取内存将需要函数调用和堆栈帧(以及堆内存分配).所以它取决于你重用那个内存的程度.并且……如果它没有被置于循环中或需要IO那么正常,那么性能可能不是问题.
但是,要小心你如何形成额外的功能.如果关闭变量(如示例中所示),编译器可能会发出堆分配来进行调用.
>当内存仅限于堆栈帧而未分配给堆时,内存的性能是否明显低于Span?
好吧,我不认为你可以堆叠内存< T> (本身),这是什么意思?
但是,与内存相比,Span避免了对索引进行一次偏移调整,因此如果循环执行大量索引,则在该循环外创建跨度将支付红利.这可能是为什么像SpanOf这样的方法是在Span上提供的,而不是在Memory上提供的.
>原始问题:哪个最好:内存< T> ;,没有区域设置变量,附加功能?
同样,这是一个样式/意见问题(除非您实际上描述了一个表现不佳的应用程序).
我的观点:仅在函数边界使用Span< T> s.仅将Memory< T> s用于成员变量.对于“内部”代码,只需使用开始/长度或开始/结束索引变量,并且名称清楚.清晰的名称将有助于避免更多的错误,而不是制作大量的Spans /“Slices”.如果函数太长以至于不再清楚变量意味着什么,那么无论如何都是时候考虑子函数了.
本文标题为:C#.NET Core 2.1跨度和内存性能注意事项
基础教程推荐
- ZooKeeper的安装及部署教程 2023-01-22
- C#控制台实现飞行棋小游戏 2023-04-22
- 一个读写csv文件的C#类 2022-11-06
- C# List实现行转列的通用方案 2022-11-02
- unity实现动态排行榜 2023-04-27
- C# windows语音识别与朗读实例 2023-04-27
- C#类和结构详解 2023-05-30
- winform把Office转成PDF文件 2023-06-14
- linux – 如何在Debian Jessie中安装dotnet core sdk 2023-09-26
- C# 调用WebService的方法 2023-03-09