Span和Memory都是包装了可以在pipeline上使用的结构化数据的内存缓冲器,他们被设计用于在pipeline中高效传递数据。
建网站原本是网站策划师、网络程序员、网页设计师等,应用各种网络程序开发技术和网页设计技术配合操作的协同工作。创新互联公司专业提供成都网站设计、成都网站制作,网页设计,网站制作(企业站、成都响应式网站建设、电商门户网站)等服务,从网站深度策划、搜索引擎友好度优化到用户体验的提升,我们力求做到极致!
这里面许多定语,值得我们细细揣摩:
1. 指向任意连续内存空间:支持托管堆,原生内存、堆栈, 这个可从Span的几个重载构造函数窥视一二。
2. 类型安全:Span 是一个泛型。
3. 内存安全: Span[1]是一个readonly ref struct数据结构,用于表征一段连续内存的关键属性被设置成只读readonly, 保证了所有的操作只能在这段内存内。
// 截取自Span源码
public readonly ref struct Span
{
// 表征一段连续内存的关键属性 Pointer & Length 都只能从构造函数赋值
///A byref or a native ptr.
internal readonly ByReference_reference;
///The number of elements this Span contains.
private readonly int _length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span(T[]? array)
{
if (array == null)
{
this = default;
return; // returns default
}
if (!typeof(T).IsValueType && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
_reference = new ByReference(ref MemoryMarshal.GetArrayDataReference(array));
_length = array.Length;
}
}
4. 视图:操作结果会直接体现到底层的连续内存。
至此我们来看一个简单的用法, 利用span操作指向一段堆栈空间。
static void Main()
{
SpanarraySpan = stackalloc byte[100]; // 包含指针和Length的只读指针, 类似于go里面的切片
byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
arraySpan[ctr] = data++;
arraySpan.Fill(1);
var arraySum = Sum(arraySpan);
Console.WriteLine($"The sum is {arraySum}"); // 输出100
arraySpan.Clear();
var slice = arraySpan.Slice(0,50); // 因为是只读属性, 内部New Span<>(), 产生新的切片
arraySum = Sum(slice);
Console.WriteLine($"The sum is {arraySum}"); // 输出0
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static int Sum(Spanarray)
{
int arraySum = 0;
foreach (var value in array)
arraySum += value;
return arraySum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanSlice(int start)
{
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
return new Span(ref Unsafe.Add(ref _reference.Value, (nint)(uint)start /* force zero-extension */), _length - start);
}
从Slice切片源码可以看到,实质是利用原ptr & length 产生包含新的ptr & length的操作视图, ptr其实是指针的移动,也就是定位新的数据块, 但是终归是在原始数据块内部。
我们再细看Span的定义, 有几个关键词建议大家温故而知新。
从C#7.2开始,你可以将readonly作用在struct上,指示该struct不可改变。
span 被定义为readonly struct,内部属性自然也是readonly,从上面的分析和实例看我们可以针对Span表征的特定连续内存空间做内容更新操作;
如果想限制更新该连续内存空间的内容, C#提供了ReadOnlySpan类型, 该类型强调该块内存只读,也就是不存在Span 拥有的Fill,Clear等方法。
一线码农大佬写了文章讲述[使用span对字符串求和]的姿势,大家都说使用span能高效操作内存,我们对该用例BenchmarkDotNet压测。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Buffers;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace ConsoleApp3
{
public class Program
{
static void Main()
{
var summary = BenchmarkRunner.Run();
}
}
[MemoryDiagnoser,RankColumn]
public class MemoryBenchmarkerDemo
{
int NumberOfItems = 100000;
// 对字符串切割, 会产生字符串小对象
[Benchmark]
public void StringSplit()
{
for (int i = 0; i < NumberOfItems; i++)
{
var s = "97 3";
var arr = s.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
var num1 = int.Parse(arr[0]);
var num2 = int.Parse(arr[1]);
_ = num1 + num2;
}
}
// 对底层字符串切片
[Benchmark]
public void StringSlice()
{
for (int i = 0; i < NumberOfItems; i++)
{
var s = "97 3";
var position = s.IndexOf(' ');
ReadOnlySpanspan = s.AsSpan();
var num1 = int.Parse(span.Slice(0, position));
var num2 = int.Parse(span.Slice(position));
_= num1+ num2;
}
}
}
}
压测解读:
对字符串运行时切分,不会利用驻留池,于是case1会分配大量小对象;
case2对底层字符串切片,虽然会产生不同的透视对象Span, 但是实际引用了的原始内存块的偏移区间, 不存在分配新内存。
从C#7.2开始,ref可以作用在struct,指示该类型被分配在堆栈上,并且不能转义到托管堆。
Span,ReadonlySpan 包装了对于任意连续内存快的透视操作,但是只能被存储堆栈上,不适用于一些场景,例如异步调用,.NET Core 2.1为此新增了Memory[4] , ReadOnlyMemory, 可以被存储在托管堆上,这个暂时按下不表。
最后用一张图总结, 本文成文,感谢[ yi念之间 ]大佬参与讨论。
[1] Span: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Span.cs
[2] readonly strcut: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-struct
[3] ref struct: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct
[4] Memory: https://docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines
网页名称:站在前人的肩膀上重新透视C# Span数据结构
文章转载:http://www.mswzjz.cn/qtweb/news10/35410.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能