使用Span<T>优化代码(简单介绍)

作者 Kalpani Ranasinghe
发布于 2021年03月20日
浏览 48

作为一名程序员,应努力写出能提高整体应用性能的代码。在去年我刚上班时,四处寻找有没有高效处理字符串操作(如Substring()Split()等)的方法,C中的Span<T>类型给了我答案。所以,我想和大家分享一下它的一些基本细节。

什么是Span<T>

Span<T>是C#7.2中引入的一种新类型,并在.NET Core 2.1得到支持。

Span<T>能够表示任意内存的连续区域,无论该内存是与托管对象相关联,还是由本地代码通过互操作提供,或者是在堆栈上。而且它在这样做的同时,还能提供安全的访问,具有像数组那样的性能特性。

Span<T>有一个相关类型ReadOnlySpan<T>,它提供了内存数据的只读视图。ReadOnlySpan可以用来查看一个不可变类型(例如String)所占用的内存。你可以把 Span<T>看作是进入一些现有内存的窗口,不管它被分配到哪里。

Span<T>被定义为 ref 结构,这意味着它被限制为只能在堆栈上分配。这就减少了一些潜在的用例,比如将它作为一个字段存储在类中,或者在异步方法中使用它。ref结构设计的主要原因是确保在使用Span<T>时,我们不会造成额外的堆分配。这也是它支持编写高度优化代码的原因之一。

让我们看一些编码示例来理解Span<T>的行为。

想象一下,你有一个像下面这样的表达式,你需要取出等号右边的值。

var str = "gender=female";//需取出female

为此,你可以写一个简单的方法,比如这样。

public string GetValueUsingSubstring(string expression)
{
  var lastEqualSignIndex = expression.LastIndexOf('=', StringComparison.Ordinal);

  return lastEqualSignIndex == -1
    ? string.Empty
    : expression.Substring(lastEqualSignIndex + 1);
}

它的作用是,得到最后一个等号的索引,然后用它来得到代表值的子串。如果索引为-1,我们没有找到任何等号,所以该方法将返回一个空字符串作为我们的默认结果。如果我们确实找到了索引,那么该方法就会使用Substring方法提取值并返回。

让我们使用Span<T>来优化这段代码,而不是使用字符串操作。

public ReadOnlySpan<char> GetValueUsingSpan(ReadOnlySpan<char> expression)
{
  var lastEqualSignIndex = expression.LastIndexOf('=');

  return lastEqualSignIndex == -1
  ? ReadOnlySpan<char>.Empty
  : expression.Slice(lastEqualSignIndex + 1);
}

请注意,优化后的方法,参数 "expression "现在的类型是ReadOnlySpan<char>,返回类型现在也是ReadOnlySpan<char>

首先,和上面的代码一样,我们寻找等号的索引。同样,如果索引为-1,这意味着我们没有找到等号,该方法返回一个空的ReadOnlySpan<char>结果。如果我们确实找到了一个等号,我们现在可以使用Span的一个名为 Slicing的功能。

Slicing是一个强大的方法,我们可以将一个现有的Span切成一个较小的窗口。切片时,我们可以定义切片的起始位置的索引,也可以定义切片的结束位置的长度。如果省略了长度,则会给出一个从起始位置到Span结束的切片。

切片也是一种低成本的操作,因为我们没有复制任何东西。我们只是创建了一个新的Span,它代表了现有内存范围子集的一个窗口。

为了更好的理解,请查看下图。

在这里,我们创建了一个原始SpanSlice,以查看其中的 "female" 值,而无需分配任何额外的原始内存副本。我们从最后一个等值字符后的索引开始取值的一个片断。由于我们没有指定长度,这个切片将运行到现有Span的末端。一旦我们对原始Span 进行了分片,就会产生一个包含值的新Span,我们将其作为方法的结果返回。

所以正如你所看到的,我们可以在任何需要提高性能的地方使用Span<T>,并在需要的时候减少内存分配。虽然Span<T>听起来有点复杂,但它的用法却非常简单。

希望你能从本文中获得一些关于Span<T>的新知识。

标签云
asp.net core (9) google登录 (1) css (1) less calc (1) C# (5) C#进制转换 (1) Authentication (1) 注销 (1) 登录 (1) 验证 (1) scroll-view (1) 微信小程序 (4) 滚动到底部 (1) StackExchange.Redis (1) google (1) 百度 (1) nginx (2) 大文件 (2) 微信小程序c#解密 (1) 微信小程序获取手机号 (1) openid (1) session_key (1) CDN (1) URL鉴权 (1) 阿里云 (1) async (1) await (1) 禁止下拉上滑效果 (1) Index类型 (1) Range类型 (1) dontent publish (1) dotnet publish在线生成器 (1) System.DrawingCore.GDIPlus报错 (1) centos (1) 中文字体 (1) SqlBulkCopy (1) SqlSugar (1) JWT (5) 认证 (3) RSA JWT (1) 非对称加密 (1) 写信 (1) 见字如面 (1) 优化建议 (2) 正确操作字符串 (1) Java (1) JWT退出 (1) RefreshToken (1) .NET Core网站开发框架 (1) Moz (3) 墨子 (1) JSON.NET (1) Newtonsoft (1) System.Text.Json (1) 自定义后台路径 (1) .netcore (1) quartz (2) 作业调度框架 (1) 作业调度 (1) 定时任务 (1) exception (1) 异常处理 (1) HttpClient (1) IHttpClientFactory (1) RDM (1) Redis (1) Redis Desktop Manager (1) RedisDesktopManager (1) linux (1) mac (1) windows (1) Could not get any response (1) postman (1) leetcode (2) 力扣 (1) 回文字符串 (1) 面试刷题 (1) centos7 (1) php安装 (1) 网易云插件 (1) 马甲App (1) Discuz插件 (1) 网易云音乐 (1) Blazor (1) 五子棋 (1) c#解题 (1) 最长连续序列 (1) Swagger (5) 在线文档 (1) blob (1) mp4 (1) 视频 (1) big file (1) 上传 (1) s (1) Azure (1) Azure Key Vault (1) Configuration (1) 密钥保管库 (1) Dapper安装 (1) Dapper是什么 (1) Dapper连接Mysql (1) Dapper连接SqlServer (1) dapper (1) int (1) range (1) net6 (1) 数据结构 (1) Span<T> (1) 压缩css (1) ISession Extension (1) String.IsNumeric、扩展方法 (1) 身份证号码校验 (1) IsValidCnMobile (1) 手机号校验 (1) .NET 6 (1) 接口过期 (1)