关于 Span 的一切:探索新的 .NET 明星: 1 Span<T> 是什么?
关于 Span 的一切:探索新的 .NET 明星
内容列表:
- 1. Span<T> 是什么?
- 2. Span<T> 是如何实现的?
- 3. 什么是 Memory<T>,以及为什么你需要它?
- 4. Span 和 Memory 是如何与 .NET 库集成的?
- 5. NET 运行时
- 6. C# 语言和编译器受到什么影响?
想象一下你正在发布一个特别的排序算法程序,它可以在内存中就地处理数据。你会希望发布一个获得一个数组参数,并提供在数组之上操作 T[] 的实现。如果调用方可以获得这个数组,并且是希望对整个数组进行排序,那么这种方式特别棒。但是,如果调用方仅仅希望对数组的部分进行排序呢,你可能又会出提供一个重载的实现,通过 offset 和 count 参数来支持。不过,如果你又希望支持内存中不是数组的数据,比如说,而是来自原生代码呢?或者是在堆栈上的数据,你只有指向它的指针和长度呢?你又如何开发你的排序方法来操作此类任意的内存区域,而且仍然与处理整个数组,或者数组的子集一样好呢?还要考虑到处理托管数组与非托管数组一样好呢?
或者,我们看另一个例子。你正在实现一个对 System.String 的处理。例如是一个特别的解析方法。你希望获得一个字符串参数,然后提供提供处理字符串的实现。但是,如果你希望还要支持处理该字符串的子集呢?String.Substring() 方法可以用来抽取出感兴趣的一部分字符串,但是这会牵涉到昂贵的操作,导致字符串分配和内存复制。你也可以这样做,如在数组示例中那样,通过一个 offset 和 count 参数来处理。不过,如果调用方并没有得到这个字符串,而是得到了一个 char[] 数组呢?或者调用方得到的是指针 char* 呢?或者是通过调用 stackalloc 使用栈空间呢?或者是调用原生代码得到的结果呢?你又如何开发你的解析方法,不需要强制调用者做任何内存分配或者复制的一种方式呢?并且仍然一致良好地处理各种输入类型,比如字符串、char[] 和 char* 呢?
在这两种场景下,你可能可以使用 unsafe 代码和指针来完成,提供接受指针和长度的输入。不过,这样丢掉了 .NET 的核心的安全保证,打开了问题之门,比如缓冲区溢出,访问冲突等等,这些对大多数 .NET 开发者已经过去的问题。它还引入了额外的性能惩罚,比如需要在处理过程中钉住托管对象,以便你获得的指针保持有效。基于底层不同的数据类型,并不总是可以获得指针。
对于这个谜题的答案就是,Span <T>
什么是 Span<T>?
System.Span<T> 是来自 .NET 核心的新的值类型。它支持表示内存中任意一段连续的区间,不管这段内存属于一个托管对象,还是通过原生代码进行互操作得到,或者是被分配在堆栈上。尽管这样还提供了类似数组操作的高性能的安全访问。
例如,你可以从数组来创建 Span<T>:
var arr = new byte[10];
Span<byte> bytes = arr; // Implicit cast from T[] to Span<T>
从这里开始,你可以简单且高效地利用 Span 的 Slice() 重载方法,创建一个 Span 来表示/指向该数组的一个子集。通过它,你可以通过下标来读、写源数组相关的部分。
Span<byte> slicedBytes = bytes.Slice(start: 5, length: 2);
slicedBytes[0] = 42;
slicedBytes[1] = 43;
Assert.Equal(42, slicedBytes[0]);
Assert.Equal(43, slicedBytes[1]);
Assert.Equal(arr[5], slicedBytes[0]);
Assert.Equal(arr[6], slicedBytes[1]);
slicedBytes[2] = 44; // Throws IndexOutOfRangeException
bytes[2] = 45; // OK
Assert.Equal(arr[2], bytes[2]);
Assert.Equal(45, arr[2]);
如前所述,Span 还提供了访问数组子集的一种方式。它也可以用来指向堆栈上的数据。例如:
Span<byte> bytes = stackalloc byte[2]; // Using C# 7.2 stackalloc support for spans
bytes[0] = 42;
bytes[1] = 43;
Assert.Equal(42, bytes[0]);
Assert.Equal(43, bytes[1]);
bytes[2] = 44; // throws IndexOutOfRangeException
更为方便的是,它可以用来指向任意的指针和长度,例如通过本地堆分配的内存,例如:
IntPtr ptr = Marshal.AllocHGlobal(1);
try
{
Span<byte> bytes;
unsafe { bytes = new Span<byte>((byte*)ptr, 1); }
bytes[0] = 42;
Assert.Equal(42, bytes[0]);
Assert.Equal(Marshal.ReadByte(ptr), bytes[0]);
bytes[1] = 43; // Throws IndexOutOfRangeException
}
finally { Marshal.FreeHGlobal(ptr); }
Span<T> 的索引器借助于被称为 ref return 的从 C# 7.0 引入的 C# 特性。该索引器使用 ref T 返回类型定义。它提供了类似于索引数组的语法,返回实际存储位置的引用,而不是在该位置内存的复制品。
public ref T this[int index] { get { ... } }
通过该示例,该 ref-returning 索引器的影响显而易见,例如与 List 索引器相比,它不是 ref returning 的。下面是一个示例:
struct MutableStruct { public int Value; }
...
Span<MutableStruct> spanOfStructs = new MutableStruct[1];
spanOfStructs[0].Value = 42;
Assert.Equal(42, spanOfStructs[0].Value);
var listOfStructs = new List<MutableStruct> { new MutableStruct() };
listOfStructs[0].Value = 42; // Error CS1612: the return value is not a variable
Span<T> 的一种变体,被称为 System.ReadOnlySpan<T>,支持只读访问。该类型与 Span<T> 类似,除了借助于 C#7.2 中引入的 ref readonly T 特性,而不是 ref T。使得它可以处理不变的数据类型,比如 System.String。ReadOnlySpan<T> 使得可以非常高效地处理字符串切片,而不需要分配或者复制内存,例如:
string str = "hello, world";
string worldString = str.Substring(startIndex: 7, length: 5); // Allocates
ReadOnlySpan<char> worldSpan =
str.AsSpan().Slice(start: 7, length: 5); // No allocation
Assert.Equal('w', worldSpan[0]);
worldSpan[0] = 'a'; // Error CS0200: indexer cannot be assigned to
除了这些已经介绍的特性,Span 还提供了多种优点。例如,Span 支持类型转换符号,意味着你可以强制一个 Span<byte> 到 Span<int> ( 这里 Span<int> 的 0 下标映射到 Span<byte> 第一个 4 字节 )。这样如果你读取 bytes 缓冲区,你可以安全且高效地将它传递给操作一组字节,例如 int 类型。
关于 Span 的一切:探索新的 .NET 明星: 1 Span<T> 是什么?的更多相关文章
- 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了,很爽的~ 可是当我在中 ...
- 读bootstrap2.3.2有感1
起步: 下载编译好的bootstrap2文件,百度新版jquery.js,并复制html模版(hello world)放置在同一目录,然后看了下官网上的范例网站,心里还是很激动啊~ <!DOCT ...
- [bootstrap] 栅格系统和布局
1.简介 栅格系统(grid systems),也称为“网格系统”,运用固定的格子设计版面布局,风格工整简洁.是从平面栅格系统演变而来. Bootstrap建立在12列栅格系统.布局.组件之上.以规则 ...
- Boostrap栅格系统
Boostrap排版.链接样式设置了基本的全局样式.分别是:为body元素设置 布局容器:Bootstrap需要为页面内容和栅格系统包裹一个:container容器.Bootstrap提供了两个作此用 ...
- HTML+CSS实现页面
使用HTML和CSS实现以下页面: 抽屉首页 个人博客首页 小米官网首页 登录注册页面 一.抽屉首页 1.实现目标:https://dig.chouti.com/ 2.代码: HTML: <!- ...
随机推荐
- Windows系统之“关闭windows 文件夹分组”
不知道怎么就按出来了 文件夹分组 ,也不知道怎么关闭,别扭了好久 解决办法: 右键点击文件夹空白处,选择 "分组依据" --> 然后选择 "无"
- 将 LLMs 精调至 1.58 比特: 使极端量化变简单
随着大语言模型 (LLMs) 规模和复杂性的增长,寻找减少它们的计算和能耗的方法已成为一个关键挑战.一种流行的解决方案是量化,其中参数的精度从标准的 16 位浮点 (FP16) 或 32 位浮点 (F ...
- manim边学边做--形状匹配
manim中有几个特殊的用于形状匹配的对象,它们的作用是标记和注释已有的对象,本身一般不单独使用. 形状匹配对象一共有4种: BackgroundRectangle:为已有的对象提供一个矩形的背景 C ...
- 0608-nn和autograd的区别
0608-nn和autograd的区别 目录 一.nn 和 autograd 的区别 二.Function 和 Module 在实际中使用的情况 pytorch完整教程目录:https://www.c ...
- kubernetes拉取私有镜像仓库的镜像
kubernetes拉取私有镜像仓库时需要使用镜像仓库的账号密码 方式: apiVersion: v1 kind: Pod metadata: name: private-reg spec: cont ...
- MySQL数据的导入
我们在帖子MySQL数据的导出 - brucexia - 博客园 (cnblogs.com)中讲了MySQL数据的导出,本文讲讲解MySQL数据的导入. MySQL数据的导入包括使用LOAD DATA ...
- Linux 基础-文本处理命令
概述 find 文件查找 grep 文本搜索 参考资料 概述 Linux 下使用 Shell 处理文本时最常用的工具有: find.grep.xargs.sort.uniq.tr.cut.paste. ...
- Next.js 从零入门到实战 3:2024最新完整教程 | 包含SSR、API路由和性能优化
CSS样式学习 上一篇文章讲到如何创建一个脚手架程序,目前我们已经有了一个nextjs的基本框架,因此我们可以在这个基础上进行改造.打开我们项目中的page.tsx页面,这里是程序的主页面,也就是我们 ...
- 2024/10/3 CSP-S模拟赛20241003
A 恶心恶心恶心,赛时写了一个二分+线段树的复杂度错了,当时yzh和lyz就一会骗我一会说实话的,搞得很懵,自己水平也是菜,那线段树分析复杂度怎么不把递归次数乘上呢?大傻逼grz 思路其实还挺好的. ...
- web上线部署系统 Walle
Walle瓦力是基于git和rsync实现的一个web部署系统工具. 用户分身份注册.登录 开发者发起上线任务申请 管理者审核上线任务 支持多项目部署 快速回滚 部署前准备任务(前置检查) 代码检出后 ...