关于 Span 的一切:探索新的 .NET 明星:5. .NET 运行时的处理

.5. NET 运行时会怎么样?

.NET 运行时提供的安全保证之一是确保访问数组的下标不会越界,实践中被称为边界检查,考虑如下方法:

[MethodImpl(MethodImplOptions.NoInlining)]
static int Return4th(int[] data) => data[3];

在我撰写本文的 x64 机器上,生成的汇编代码如下所示:

sub      rsp, 40
       cmp      dword ptr [rcx+8], 3
       jbe      SHORT G_M22714_IG04
       mov      eax, dword ptr [rcx+28]
       add      rsp, 40
       ret
G_M22714_IG04:
       call     CORINFO_HELP_RNGCHKFAIL
       int3

cmp 指令比较下标 3 与数组的长度,如果下标 3 越界,随后的 jbe 指令跳转到检查失败代码 ( 抛出异常 )。JIT 需要生成代码来确保此类访问不会导致下标越界,但是这不意味着每次独立的数组访问都需要边界检查。考入下面的 Sum 方法:

static int Sum(int[] data)
{
  int sum = 0;
  for (int i = 0; i < data.Length; i++) sum += data[i];
  return sum;
}

JIT 需要生成确保访问 data[i] 的代码不会下标越界,但是因为可以告诉 JIT,从循环结构 i 永远不会越界 ( 循环从头到尾遍历每个元素 ),JIT 可以优化掉对数组访问的边界检查。这样,生成的汇编代码如下所示:

G_M33811_IG03:
       movsxd   r9, edx
       add      eax, dword ptr [rcx+4*r9+16]
       inc      edx
       cmp      r8d, edx
       jg       SHORT G_M33811_IG03

cmp 指令还在循环中,但是只是简单地比较下标 i 和数组的长度 ( 它保存在 r8d 寄存器中 ),没有了其它的边界检查。

运行时使用类似对 Span ( 包括 Span<T> 和 ReadOnlySpan<T> ) 的优化。对比上一示例的如下代码,这里仅仅变更了参数类型:

static int Sum(Span<int> data)
{
  int sum = 0;
  for (int i = 0; i < data.Length; i++) sum += data[i];
  return sum;
}

上述代码生成的汇编代码几乎相同。

G_M33812_IG03:
       movsxd   r9, r8d
       add      ecx, dword ptr [rax+4*r9]
       inc      r8d
       cmp      r8d, edx
       jl       SHORT G_M33812_IG03

汇编代码非常类似,因为省去了边界检查。但是因为 JIT 识别到 Span 原生的索引器,意味着 JIT 对索引器生成特定的代码,而不是转换实际代码到汇编中。

所有这些说明了,对于运行时可以使用 Span 对数组相同类型的优化,使得 Span 成为访问数据的高效机制。更深入内容请参见:bit.ly/2zywvyI

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值