netcore 内存限制_ASP.NET Core高性能Span<T>和Memory<T>

本文介绍了.NET Core中的Span<T>和Memory<T>,它们提供了对内存的安全和高效操作。Span<T>是栈上存储的类型,用于连续内存区域,适用于减少中间对象创建的高性能场景。Memory<T>则没有栈上的限制,可用于跨await和yield边界。两者都强调切片的概念,以提升性能,特别是在处理字符串和数组时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(给DotNet加星标,提升.Net技能)

转自:【秦时明月】cnblogs.com/humble/p/12382833.html

一、Span概述

原文:Provides a type- and memory-safe representation of a contiguous region of arbitrary memory.

中文的翻译不准确,这里给出比较厚道的翻译:提供类型T安全、连续的内存区域的表达方式.

bc98168b2cf2af0be44a54bb16140596.png

(图1:Span定义,不是全图)

这里出现高阶语法 readonly ref struct,下面是msdn给的语言规范(或者其核心意义),估计大家会看晕, 

Span 并且不能跨 await 和 yield 边界使用。此外,对两个方法的调用(Equals(Object) 和 GetHashCode)将引发一个 NotSupportedException。因为锁定在堆栈上,所以也不要试图让其成为做为静态成员。

db2a01fd5cfb2d11c0e6d5c0a25f91b3.png

我先给出最简单的解释:

Span是微软为了给.NET提供了一个高效的内存操纵元素,而定义的一个数据结构,为了高效的初衷,将Span自身锁定在堆栈上(内存连续,且处理高效)

注意:是Span自身!!!Span 实例通常用于保存数组或某个数组的一部分的元素。

二、Span可用来做哪些事

2.1、不得不提的 Slice

切片这种东西,在GO,Rust中太寻常了(PS:当然对于C++的表示不屑),对于C#而言,这是一个性能提升不可或缺的概念,

Span基本上就是这个概念的翻版.所以其中有诸多方法就是切片.

bbc900fcbdc42300c8d22f2889ad8a37.png

8a389f76a94ad29d47b96a9d458cf12e.png

3124c0fb19a43d67fd9995dc81989688.png

可见微软为Span提供了诸多类似于原来的String中的很多方法,具体查阅地址: Span的扩展方法:https://docs.microsoft.com/zh-cn/dotnet/api/system.span-1?view=netcore-3.1#extension-methods

2.2、切片是其本质,是对原有对象的投影(或部分投影)

之前我们要实现高效的操作,如字符串类的操作,数组类的操作,

这里应该尤其注意,不见得你使用Span就高效了,明白它的设计初衷:Slice! 特别是会不断产生新的碎片和构造新对象的场景.(由此可见对于String的操作产生了诸多

新碎片这样的场景是尤其好用的)

我们看看如下的场景,大家觉得哪个效率会更高

int[] array = new int[10000];
Span<int> arraySpan = array;

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
arraySpan[ctr] = arraySpan[ctr] * arraySpan[ctr]/3;
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed);

array = new int[10000];
stopwatch.Reset();
stopwatch.Start();
for (int ctr = 0; ctr < array.Length; ctr++)
array[ctr] = array[ctr] * array[ctr]/3;
Console.WriteLine(stopwatch.Elapsed);

结果按照我们的原则你就知道,不用Span效果会更好,下图是realse模式下发布的,整体上可知:不用Span会更快,所以切片是它的本质!大家看看GO的切片就知道了.

2e7330ed992b8aa20f27d6f538d4b83e.png

2.3、Span可以不仅投影常见对象还可以是从Marshal , stackalloc分配的而来的

var native = Marshal.AllocHGlobal(

三、Memory概述

和Span类似,它同样表示连续内存区域。区别是没有Span堆栈上的限制,没有 readonly ref struct 这样的申明了.

这意味着 Memory 可以放置在托管堆上,而 Span 不能。因此,Memory 结构与 Span 实例没有相同的限制。

具体而言:它可用作类中的字段。它可跨 await 和 yield 边界使用。除了 Memory之外,还可以使用 System.ReadOnlyMemory 来表示不可变或只读内存。

87edc02b3d8cc1cf812ce7b3ca04f325.png

这里有园友从C++源码的角度进行分析,这里提取下面两段,供大家参阅(链接地址),

Span 与 Memory 的区别:

1、Memory 保存 原有的对象地址、子内容的开始地址 与 子内容的长度,大致情况下图:

e718c78584c9b33678128607c6e521eb.png

如上文所说,Span被微软锁定在堆栈上,

2、Span 保存子内容的开始地址与长度,不保存原始对象的地址,大致如下图:

98b1eef9df18acae8e385de047866952.png

如果这就是真实情况,可见Span脱离不了堆栈的环境的,不然计算不了真实的切片地址的.

四、使用上的预测和建议

1、类似于string这样的操作会Span大有用处(因为会产生很多新的中间数据产生开销)

2、无论span还是memory设计初衷就是Slice,使用场景是那些会不断产生新的开销、新的对象的场景.

3、其实所有的性能提升根本让CPU运行的指令更少了,减少了不必要的开销

推荐阅读  点击标题可跳转ASP.NET Core高性能 性能的闲聊ASP.NET Core的启动过程直接使用汇编编写.NET Standard库

看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

c7de1dc5c1f9262ad23f5c33630e49ae.png

好文章,我在看❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值