关于 Span 的一切:探索新的 .NET 明星: 4. Span<T> 和 Memory<T> 是如何与 .NET 库集成的?
4. Span<T> 和 Memory<T> 是如何与 .NET 库集成的?
- 1. Span<T> 是什么?
- 2. Span<T> 是如何实现的?
- 3. 什么是 Memory<T>,以及为什么你需要它?
- 4. Span 和 Memory 是如何与 .NET 库集成的?
- 5. NET 运行时
- 6. C# 语言和编译器受到什么影响?
在前面的 Memory<T> 代码片段中,你应该注意到,在调用 Stream.ReadAsync() 的方法中传递了 Memory<byte> 参数。但是现在的 .NET 中的 Stream.ReadAsnc() 实际上接受类型为 byte[]。这是怎么工作的呢?
为了支持 Span<T> 和相关类型,数百个新的成员和类型被添加到 .NET 的各个部分。多数是现在的基于数组和基于字符串的重载方法,其它全新的类型则专注于特性的处理领域。例如,所有的基础类型,例如 Int32 现在在支持字符串的基础上,拥有支持 ReadOnlySpan 的 Parse 重载。想象一下,当你期望处理由两个数字通过逗号连接起来的字符串 ( 例如:123,456
),你希望解析出来这两个数字,现在你可以如下编码:
string input = ...;
int commaPos = input.IndexOf(',');
int first = int.Parse(input.Substring(0, commaPos));
int second = int.Parse(input.Substring(commaPos + 1));
不过,这导致了两次字符串分配。如果你在编写性能敏感的代码,那么两次字符串分配就太多了,相反,你可以如下完成:
string input = ...;
ReadOnlySpan<char> inputSpan = input;
int commaPos = input.IndexOf(',');
int first = int.Parse(inputSpan.Slice(0, commaPos));
int second = int.Parse(inputSpan.Slice(commaPos + 1));
通过使用新的基于 Span 的 Parse 重载,你可以使得整个操作无内存分配发生。类似的解析和格式化方法也出现在基础类型,比如 Int32 中,这贯穿了核心类型,例如 DateTime、TimeSpan 和 Guid 中,以至于更高级别的类型,例如 BigInteger 和 IPAddress 中。
实际上,众多此类方法已经被添加到整个框架中。从 System.Random 到 System.Text.StringBuffer 到 System.Net.Sockets,这些添加的重载使得处理 {ReadOnly}Span<T> 和 {ReadOnly}Memory<T> 更为简单和高效。有些还带来了额外的优点。例如,Stream 现在由这个方法:
public virtual ValueTask<int> ReadAsync(
Memory<byte> destination,
CancellationToken cancellationToken = default) { ... }
你将会注意到与现有的 ReadAsync() 方法接受一个 byte[] 并返回一个 Task<int> 不同,这个重载方法不仅接受一个 Memory\<byte>
来代替 byte[]
,还返回一个 ValueTask\<int>
而不是 Task\<int>
。ValueTask\<int>
是用来帮助避免内存分配的结构,异步方法被频繁期待同步返回的场景,与我们缓存所有通常返回值不同。例如,运行时可以缓存完成的 Task<bool> 的值 true,和另外一个 false,但是它不能对于 Task<int> 所对应的所有整数返回值。
因为对于流的实现很常见的场景是通过某种方式缓存,使得 ReadAsync() 方法调用同步使用,这个新的 ReadAsync() 重载方法返回 ValueTask<int> 值。这意味着同步完成的异步流读取操作可以完全避免内存分配。ValueTask<int> 也用于其它的重载,比如 Socket.ReceiveAsync(),Socket.SendAsync(),WebSocket.ReceiveAsync() 和 TextReader.ReadAsync()。
此外,在某些地方,Span<T> 允许框架包含过去引起内存安全问题的方法。请考虑这样一种场景:你希望创建一个包含随机生成值的字符串,例如某种 ID。今天,你可能会编写需要分配 char 数组的代码,如下所示:
int length = ...;
Random rand = ...;
var chars = new char[length];
for (int i = 0; i < chars.Length; i++)
{
chars[i] = (char)(rand.Next(0, 10) + '0');
}
string id = new string(chars);
你可以使用堆栈分配内存,进而获得 Span<char> 的好处,来避免需要使用不安全的代码。这种方式还可以对接受 ReadOnlySpan<char> 的字符串构造函数获得好处,例如:
int length = ...;
Random rand = ...;
Span<char> chars = stackalloc char[length];
for (int i = 0; i < chars.Length; i++)
{
chars[i] = (char)(rand.Next(0, 10) + '0');
}
string id = new string(chars);
这种方式更好,这样你可以避免堆内存分配,但是你仍然被强制要求复制在堆栈上生成的数据复制到字符串中。这种方式还只能工作在当需要的内存足够小到可以在堆栈分配。如果长度足够小,例如 32 个字节,这样很不错,但是,如果是上千字节的话,会很容易导致堆栈溢出。怎么样可以直接写入到字符串的内存中呢?Span<T> 支持你做到这一点。除了字符串的新构造函数,字符串还有一个新的 Create() 方法:
public static string Create<TState>(
int length, TState state, SpanAction<char, TState> action);
...
public delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg);
该方法被实现为分配字符串空间,然后返回一个可写入的 Span,以便你可以在字符串构造之后填充其内容。注意,Span 本质上在堆栈操作对该场景是优势,生成这个 Span ( 指向字符串的内部存储 ) 将在字符串构造完成之前存在,使得它不可能在构造完成之后使用该 Span 来改变字符串。
int length = ...;
Random rand = ...;
string id = string.Create(length, rand, (Span<char> chars, Random r) =>
{
for (int i = 0; chars.Length; i++)
{
chars[i] = (char)(r.Next(0, 10) + '0');
}
});
现在,你不仅可以避免内存分配,你还直接写入在堆上分配的字符串内存,这意味着你也避免了内存复制,并且也没有被堆栈的尺寸所限制。
除了核心框架类型中新增加的成员,众多新的 .NET 类型也被开发出来与 Span 一起工作来高效处理特定的场景。例如,开发人员寻找编写重度涉及文本处理的高性能的微服务和 Web 站点,当处理 UTF-8 编码字符串的时候,如果不会从字符串编码和解码就会获得显著的性能提升。为支持这种处理,新的类型比如 System.Buffers.Text.Base64、System.Buffers.Text.Utf8Parser 和 System.Buffers.Text.Utf8Formatter 被添加进来。这些对于字节的 Span 操作,不仅避免了 Unicode 编码和解码,而且可以使得它们与原生缓冲区协作,这对于非常低级的多种网络栈来说很常见。
ReadOnlySpan<byte> utf8Text = ...;
if (!Utf8Parser.TryParse(utf8Text, out Guid value,
out int bytesConsumed, standardFormat = 'P'))
throw new InvalidDataException();
所有这些功能不仅可以公共使用,框架本身也从基于 Span<T> 和 Memory<T> 的方法中获得更好的性能。跨 .NET Core 的呼叫站点已切换到使用新的 ReadAsync 重载,以避免不必要的分配。原来完成的通过分配子字符串的解析,现在通过无分配的解析获益。甚至更小的类型,例如 Rfc2898DeriveBytes 也使用了这种方式,在 System.Security.Cryptography.HashAlgorithm 中的 TryComputeHash() 方法使用新的基于 Span 的方式,在分配内存上获得巨大的优势 ( 算法的每次迭代分配一个字节数组,这可能会迭代上千次 )。以及吞吐量的提高。
这些不止于核心 .NET 库;它延展到所有的技术栈。ASP.NET Core 现在重度依赖于 Span,例如,Kestrel 服务器的 HTTP 解析器基于此开发。将来,Span 很可能会从较低级别的 ASP.NET Core(例如其中间件管道)中的公共 API 中公开。
关于 Span 的一切:探索新的 .NET 明星: 4. Span<T> 和 Memory<T> 是如何与 .NET 库集成的?的更多相关文章
- Android开发艺术探索——新的征程,程序人生路漫漫!
Android开发艺术探索--新的征程,程序人生路漫漫! 偶尔写点东西分享,但是我还是比较喜欢写笔记,看书,群英传看完了,是学到了点东西,开始看这本更加深入Android的书籍了,不知道适不适合自己, ...
- Dual Path Networks(DPN)——一种结合了ResNet和DenseNet优势的新型卷积网络结构。深度残差网络通过残差旁支通路再利用特征,但残差通道不善于探索新特征。密集连接网络通过密集连接通路探索新特征,但有高冗余度。
如何评价Dual Path Networks(DPN)? 论文链接:https://arxiv.org/pdf/1707.01629v1.pdf在ImagNet-1k数据集上,浅DPN超过了最好的Re ...
- 探索新冠肺炎(COVID-19)对全球航班的影响
Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 随着今天从欧洲到美国的旅行限制生效,以及为了减缓新冠病毒的传播更 ...
- span 右浮动折行 解决ie6/7中span右浮动折行问题
A floated box is shifted to the left or right until its outer edge touches the containing block edge ...
- span 右浮动折行 解决ie6/7中span右浮动折行问题
RM8005: IE6 IE7 IE8(Q) 中行内元素后相邻的浮动元素在某些情况下会折行放置在之前行内元素所在行框的底部 标准参考 W3C CSS 2.1 规范文档里对于浮动元素与非浮动行内元素相邻 ...
- “display:block-inline形式的Span或Div中添加文字后,导致Span或Div排版掉落、错位”的原因及解决方法
最近在使用3个span(或div)制作带圆角边框的按钮时,按照常识,把span的display设置成inline-block,这样就可以设置span的width和height了,很爽的~ 可是当我在中 ...
- C# - Span 全面介绍:探索 .NET 新增的重要组成部分
假设要公开特殊化排序例程,以就地对内存数据执行操作.可能要公开需要使用数组的方法,并提供对相应 T[] 执行操作的实现.如果方法的调用方有数组,且希望对整个数组进行排序,这样做就非常合适.但如果调用方 ...
- 读bootstrap2.3.2有感1
起步: 下载编译好的bootstrap2文件,百度新版jquery.js,并复制html模版(hello world)放置在同一目录,然后看了下官网上的范例网站,心里还是很激动啊~ <!DOCT ...
- [bootstrap] 栅格系统和布局
1.简介 栅格系统(grid systems),也称为“网格系统”,运用固定的格子设计版面布局,风格工整简洁.是从平面栅格系统演变而来. Bootstrap建立在12列栅格系统.布局.组件之上.以规则 ...
- Boostrap栅格系统
Boostrap排版.链接样式设置了基本的全局样式.分别是:为body元素设置 布局容器:Bootstrap需要为页面内容和栅格系统包裹一个:container容器.Bootstrap提供了两个作此用 ...
随机推荐
- markdown公式关系符
- 在 Node.js 中使用 ES6 模块 import 和 export
1.Node版本 >= 13 2.使用 ES6 模块 a.js export const A = 'A'; index.js import { A } from './a.js'; consol ...
- 高通ADSP USB流程
在高通平台上,ADSP(Audio Digital Signal Processor,音频数字信号处理器)可以通过 USB 接口与主机进行数据传输,以下是大致的 ADSP USB 流程: 主机发起 U ...
- 2024年9月中国数据库流行度排行榜:TiDB重回前三,GoldenDB问鼎前五
9月墨天轮数据社区的中国数据库流行度排行榜如约而至.除了冠亚两位,排名第三至第五的数据库产品均经历了位次的变动.榜单之上,稳健的老牌强者.崛起的新兴产品.以及那些在背后默默积蓄力量.准备厚积薄发的竞争 ...
- iOS比较枚举NSOrderedSame NSOrderedAscending NSOrderedDescending使用小结
项目开发中偶然间看到这种比较枚举,之前没注意过,仔细研究了一下结果还挺有意思,我们可以用升降序相等来帮助理解比较结果. NSString 两个字符串的比较,用 a compare:b 来比,得出的结果 ...
- ssh建立github连接 基于ssh密钥
1. 建立公钥和私钥 ps:公钥放在github上面的,私钥放在自己本地电脑 : 先生成密钥:打开 gitbash 输入命令: ssh-keygen -t rsa -b 4096 -C "z ...
- 强化学习算法笔记之【DDPG算法】
强化学习笔记之[DDPG算法] 目录 强化学习笔记之[DDPG算法] 前言: 原论文伪代码 DDPG 中的四个网络 代码核心更新公式 前言: 本文为强化学习笔记第二篇,第一篇讲的是Q-learning ...
- LiveData
ViewModel 添加依赖 implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' activity_main.xml < ...
- Pandas 空值数据的索引 位置 行号
前言 先说一下什么是pandas, 这个东西其实就是一个处理表格数据的一个库.可以把它看做是一个没有图形化界面的Excel. Pandas中的空值是非常多的,这体现了数据搜集的一个不可避免的方面.由于 ...
- F450 APM2.8 自组无人机手记
由于是初次接触无人机,外加自组需要焊接,做了一些前期的心理建设.但是过程还是异常艰难.(不过,实际操作也就焊20个焊点左右,基本就组装起来了,操作并不复杂) 自组APM无人机是想学习Ardupilot ...