你所不知道的.NET性能提升特性Span<T>

最近在学习.NET开源IO的源码,发现用的比较多的一个特性,那就是Span<T>,那么Span特性具体是做什么的呢?

你所不知道的.NET性能提升特性Span<T>

官网定义?
Span<T>是提供任意内存连续区域的类型安全与内存安全表示形式。.
Span<T> 表示任意内存的连续区域。 Span<T>实例通常用于保存数组或数组的一部分的元素。但是,与数组不同, Span<T> 实例可以指向堆栈上托管的内存、本机内存或内存。
从上可以得出,Span<T>表示任意内存的连续区域,可以指向非托管内存和非托管内存,在大多数用到数组的场景用到,并且在使用时属于安全的表达式。Span<T>属于结构体类型。这是在.NET Core 2.1之后的新特性,主要是提高.NET性能,用它包装数组,性能高。.NET6 开源的底层好多都是用的Span,它可以提高托管环境中调用非托管资源的性能,相当于直接调用指针。通过直接安全指针调用,提高效率。查看源码,.net core/.net 5 6 7的io缓冲区是span,用指针写入指针拿取,所以效率高。下面从数组创建一个 Span<Byte>

​​​​​

    var array = new byte[1000000];            var arraySpan = new Span<byte>(array);//装载到span            byte data = 0;            for (int i = 0; i < arraySpan.Length; i++)            {                arraySpan[i] = data++;            }            int arraySum = 0;            foreach (var item in arraySpan)            {                arraySum += item;            }              Console.WriteLine("span结果是: {0}", arraySum);            //span结果是: 127493856
下面创建100000内存的Span<byte>
var native = Marshal.AllocHGlobal(100000);Span<byte> nativeSpan;unsafe{    nativeSpan = new Span<byte>(native.ToPointer(), 100);}byte data = 0;for (int ctr = 0; ctr < nativeSpan.Length; ctr++)    nativeSpan[ctr] = data++;int nativeSum = 0;foreach (var value in nativeSpan)    nativeSum += value;Console.WriteLine("span结果是: {0}", nativeSum );Marshal.FreeHGlobal(native); //span结果是: 127493856

Span<T> 和切片与数组

Span<T> 包括两个重载 Slice 的方法,该方法从从指定索引开始的当前范围外形成切片。这使得可以将数据 Span<T> 视为一组逻辑区块,这些区块可由数据处理管道的一部分根据需要进行处理,且性能影响最小。相当于Substring,但Substring会创建一个新的字符串,并且用原来的字符串复制到新的字符串。

我们来对比一下性能:

var str = "love.NET";var strSpan = str.AsSpan();var sw = new Stopwatch();sw.Start();for (var j = 0; j < 100000; j++){    var strs= strSpan.Slice(4);}sw.Stop();Console.WriteLine("Span耗时:" + sw.ElapsedMilliseconds);// Span<T> 操作 ReadOnlySpan<T>来消除此分配和复制操作sw.Restart();for (var j = 0; j < 100000; j++){    var strs = str.Substring(4);}sw.Stop();Console.WriteLine("Substring耗时:" + sw.ElapsedMilliseconds);

你所不知道的.NET性能提升特性Span<T>

看出差距了吧。

Span<T>还可以包装整个数组,而且还支持整个数组切片,因此可以指定数组的连续范围。我们来看效果。

// 字符串数组 var strs = new string[] { "java", "c++", "c#", "go","python" }; var slice = new Span<string>(strs, 2, 2); for (int ctr = 0; ctr < slice.Length; ctr++)     slice[ctr] += "牛";
 // Examine the original array values. foreach (var value in strs)     Console.Write($"{value}  ");//结果如下:
你所不知道的.NET性能提升特性Span<T>
在对内存指定的一段的处理上, C# 是比较弱的,因为没有了 C++ 的指针,所以微软在.NET Core之后推出了Span这个新特性。你觉得Span怎么样?欢迎留言讨论。