Span 原理探究

ref结构

ref struct是仅在堆栈上的值类型:

  • 表现一个顺序结构的布局;(译注:可以理解为连续内存)
  • 只能在堆栈上使用。即用作方法参数和局部变量;
  • 不能是类或正常结构的静态或实例成员;
  • 不能是异步方法或lambda表达式的方法参数;
  • 不能动态绑定、装箱、拆箱、包装或转换。

ref struct也被称为嵌入式引用。

1. Span 源码分析

灵活运用 Span 解决工作中的实际问题我相信大家应该没什么毛病了,有了这个基础再从 Span 的源码 和 用户态 和大家一起深度剖析,从源码开始吧。


public readonly ref struct Span<T>
{
internal readonly ByReference<T> _pointer; private readonly int _length;
}

上面代码的 ref struct 可以看出,这个 Span 是只可以分配在栈上的值类型,然后就是里面的 _pointer 和 _length 两个实例字段,不知道看完这两个字段脑子里是不是有一幅图,大概是这样的。

可以清晰的看出,Span 就是用来映射一段可以连续访问的内存地址,空间大小由 length 控制,开始位置由 _pointer 指定,是不是像极了指针,是的,语言团队要保证你的程序高性能,还得照护你的人身安全,出了各种手段,真是煞费苦心!

二、Span<T>常规用法

    static void Main()
{ var nums = new int[] { 1, 2, 3, 4, 5, 6,8,9,7,10,11,12,13,14,15,16 };
//用数组来实例化span
var span = new Span<int>(nums); var Slice1 = span.Slice(1, 4);//2 3 4 5
var Slice2= span.Slice(10);// 11,12,13,14,15,16
var Slice3= Slice1.ToArray();//转化成数组
var Slice4 = new Span<int>(new int[Slice1.Length]);//新建一个span,长度不能小于slice1 要不会报错
Slice1.CopyTo(sp);
foreach (var item in Slice2)
{
Console.WriteLine($"{item}");
} }

三:Span 在 String 和 List 的实践

Span的应用场景真的是太多了,不可能在这篇一一列举,这里我就举两个例子吧,让大家能够感受到 Span 的强大即可。

1. 在 String 上的应用

案例:如何高效的计算出用户输入的值 10+20 ?

1) 传统 Substring 做法

传统的做法很简单,截取呗,代码如下:


static void Main(string[] args)
{
var word = "10+20"; var splitIndex = word.IndexOf("+"); var num1 = int.Parse(word.Substring(0, splitIndex)); var num2 = int.Parse(word.Substring(splitIndex + 1)); var sum = num1 + num2; Console.WriteLine($"{num1}+{num2}={sum}"); Console.ReadLine();
}

结果是很轻松的算出来了,但你仔细想想这里是不是有点什么问题,比如说为了从 word 中扣出 num,我用了两次 SubString,就意味着会在 托管堆 上生成两个 string,如果说我执行 1w 次话,那托管堆上会不会有 2w 个 string 呢? 修改代码如下:


for (int i = 0; i < 10000; i++)
{
var num1 = int.Parse(word.Substring(0, splitIndex)); var num2 = int.Parse(word.Substring(splitIndex + 1)); var sum = num1 + num2;
}

然后看一下 托管堆 上 String 的个数


0:000> !dumpheap -type String -stat
Statistics:
MT Count TotalSize Class Name
00007ffc53a81e18 20167 556538 System.String

托管堆上有 20167 个,挺恐怖的,真的是给 GC 添麻烦哈,这里还有 167 个是系统自带的,接下来的问题是有没有办法替换 SubString 从而不生成临时string呢?

2) 新式 Span 做法

如果看懂了 Span 结构图,你就应该会使用 _pointer + length 将 string 进行切片处理,对不对,代码如下:


for (int i = 0; i < 10000; i++)
{
var num1 = int.Parse(word.AsSpan(0, splitIndex)); var num2 = int.Parse(word.AsSpan(splitIndex)); var sum = num1 + num2;
}

然后在 托管堆 验证一下,是不是没有 临时 string 了?


0:000> !dumpheap -type String -stat
Statistics:
MT Count TotalSize Class Name
00007ffc53a51e18 167 36538 System.String

可以看到就只有 167 个系统字符串,性能也得到了不小的提升,。

2. 在 List 上的应用

平时用 Span 的时候,更多的会应用到 Array 上面,毕竟 Array 在托管堆上是连续内存,方便 Span 在上面画一个可视窗口,其实不仅仅是 Array,从 .NET5 开始在 List 上画一个视图也是可以的,截图如下:

因为 List 的 CURD 会导致底层的 Array 忽长忽短或重新分配,也就无法实现物理上的连续内存,所以 Span 应用到 List 之后,希望List是不可变的,这也是官方的建议。

四:总结

总的来说,Span 在 .NET 底层框架中的地位是越来越显著了,相信 netCore 追求更高更快的性能上 Span 一定大有可为,大家赶紧学起来,

原文链接:https://www.cnblogs.com/huangxincheng/p/13974476.html

【C# IO 操作 】Span<T>类的更多相关文章

  1. java的IO操作:System类对IO的支持。

    目标: 1,掌握SYStem对IO的三种支持: system.out system.in system.err 2,掌握system.out及system.err的区别. 3,掌握输入,输出重定向. ...

  2. XML序列化 判断是否是手机 字符操作普通帮助类 验证数据帮助类 IO帮助类 c# Lambda操作类封装 C# -- 使用反射(Reflect)获取dll文件中的类型并调用方法 C# -- 文件的压缩与解压(GZipStream)

    XML序列化   #region 序列化 /// <summary> /// XML序列化 /// </summary> /// <param name="ob ...

  3. Java的IO操作---File类

    目标 1)掌握File类作用 2)可以使用file类中方法对文件进行读写操作. File类 唯一与文件有关的类.使用file类可进行创建或删除操作,要想使用File类,首先观察File类的构造方法. ...

  4. C++ IO操作API及注意事项(包含一个日志类的实现)

    C++是一个抽象程度比C高很多的语言,在使用C++时,编译器做了很多工作,如果我们不对C++的某些特性的实现机制进行了解,那么编程时也许会有很多疑惑,我们也许知道怎样做才是正确的,但不知道为什么要这样 ...

  5. [19/04/04-星期四] IO技术_CommonsIO(通用IO,别人造的轮子,FileUtils类 操作文件 & IOUtilsl类 操作里边的内容 )

    一.概念 JDK中提供的文件操作相关的类,但是功能都非常基础,进行复杂操作时需要做大量编程工作.实际开发中,往往需要 你自己动手编写相关的代码,尤其在遍历目录文件时,经常用到递归,非常繁琐. Apac ...

  6. C# IO操作(二)File类和Directory类的常用方法

    本篇主要介绍一些常用的IO操作,对文件和目录的操作:留给自己复习之用. 1.创建文件 string sPath1=Path.GetDirectoryName(Assembly.GetExecuting ...

  7. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  8. 文件IO操作..修改文件的只读属性

    文件的IO操作..很多同行的IO工具类都是直接写..但是如果文件有只读属性的话..则会写入失败..所以附加了一个只读的判断和修改.. 代码如下: /// <summary> /// 创建文 ...

  9. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  10. JAVASE02-Unit08: 文本数据IO操作 、 异常处理

    Unit08: 文本数据IO操作 . 异常处理 * java.io.ObjectOutputStream * 对象输出流,作用是进行对象序列化 package day08; import java.i ...

随机推荐

  1. vue学习15-自定义组件model使用

    <!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta http ...

  2. 加深对AQS原理的理解示例二:自己设计一个同步工具,同一时刻最多只有两个线程能访问,超过线程将被阻塞

    /** *@Desc 设计一个同步工具,同一时刻最多只有两个线程能访问,超过线程将被阻塞<br> * 思路分析: * 1.共享锁 两个线程及以内能成功获取到锁 * 2. *@Author ...

  3. gin中映射查询字符串或表单参数

    package main import ( "fmt" "github.com/gin-gonic/gin" ) func main() { r := gin. ...

  4. 不难懂------react-flux

    一.什么是Flux Flux 是一种架构思想,专门解决软件的结构问题.它跟MVC 架构是同一类东西,但是更加简单和清晰. 二.flux的基本概念 (1) .Flux由4部分组成 1.View:视图层 ...

  5. freeswitch对接电信线路VOLTE视频通话

    在public.xml上设置视频编码: <action application="export" data="nolocal:absolute_codec_stri ...

  6. pandas目录

    pandas目录 1 Lesson1--Pandas是什么 2 Lesson2--Pandas库下载和安装 3 Lesson3--Pandas Series结构 4 Lesson4--Pandas D ...

  7. 布客&#183;ApacheCN 编程/大数据/数据科学/人工智能学习资源 2020.4

    公告 我们的机器学习群(915394271)正式改名为财务提升群,望悉知. 请关注我们的公众号"ApacheCN",回复"教程/路线/比赛/报告/技术书/课程/轻小说/漫 ...

  8. 聊一聊如何用C#轻松完成一个TCC分布式事务

    背景 银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID ,只能够通过分布式事务来解决. 在 聊一聊如何 ...

  9. HOOK API(四) —— 进程防终止

    0x00        前言 这算是一个实战吧,做的一个应用需要实现进程的防终止保护,查了相关资料后决定用HOOK API的方式实现.起初学习HOOK API的起因是因为要实现对剪切板的监控,后来面对 ...

  10. nodejs process uncaughtException

    用过Node一段时间之后,发现那些在事件主循环里碰到的异常会导致Node进程退出.在许多应用场景下,特别是对那些希望永不当机的服务器程序来说,这都是不接受的.uncaughtException事件会提 ...