C#如何安全、高效地玩转任何种类的内存之Span的本质攻略
什么是Span
Span<T>
是 .NET Core 中新增的一种类型,它是一种类似于指针的结构体,表示一块连续的内存区域,其内容不一定是类型T的连续区域,可以是其他原语类型(如byte、int、long等)的连续区间。Span<T>
可以让我们高效地访问和读写内存在不进行垃圾回收的情况下,同时提高代码的可读性和安全性。
为什么要使用Span
在.NET Core中,Span<T>
可以取代一部分App端耗费运行性能的,无法直接内联为CPU指令的Array类型(如byte[]、char[]等)。在某些场景下,使用Span比使用数组更快、更节省内存、更安全且易于维护。大多数情况下,Span可以完全替代传统Array类型。Span能够提供更快的数据访问速度,而且由于它并不在堆上分配内存,不会引发GC和堆内碎片。
Span的核心API
Span支持各种常见的API,比如基于范围(“range-based”)的访问、数组初始化、复制、遍历等等。以下是Span类型的几个重要API方法:
Slice
Slice方法可以创建一个新的Span,其所占据的连续内存段为当前原始Span所指定的一部分。从而可以通过该方法在Span中提供对子范围的非常高效的访问。
Span<byte> original = stackalloc byte[10];
original[0] = 1;
original[9] = 10;
Span<byte> slice = original.Slice(2, 6);
Console.WriteLine(slice[0]); // output is 3
Pin
针对临时的内存分配,Span还提供了一种手段——Pin,用于将Span关联起来的内存固定在内存中,防止GC在处理对象的过程中重复移动这个对象所提供的内存。如果你不经意间将 Span 的内存地址传递给其他代码,GC 稍后可能会移动该内存,导致应用程序崩溃。因此,通过固定内存而使其不可移动,即可避免该问题。
unsafe
{
fixed (int* p = &source[0])
fixed (int* q = &dest[0])
{
var src = new Span<int>(p, source.Length);
var dst = new Span<int>(q, dest.Length);
Span<int> slice = src.Slice(1, 2);
MemoryExtensions.CopyTo(slice, dst);
}
}
TryRead, TryWrite
TryRead和TryWrite方法能够更加高效地处理Span的访问。TryRead可以通过返回success值来判断是否成功读取,而不是抛出异常。TryWrite可以通过返回success值来判断是否成功写入,同时返回写入字节数。
Span<byte> buffer = new byte[4] { 1, 2, 3, 4 };
Span<int> casted = MemoryMarshal.Cast<byte, int>(buffer);
int val;
if(casted.TryRead(out val))
{
Console.WriteLine(val);
}
Span的示例
数组快速排序
下面展示了使用Span实现的静态快速排序算法,其中排序的数组是int类型的。
public static void QuickSort(Span<int> span)
{
if (span.Length <= 1)
{
return;
}
int pivotIndex = span.Length / 2;
int pivotValue = span[pivotIndex];
int left = 0;
int right = span.Length - 1;
while (left <= right)
{
while (span[left] < pivotValue)
{
left++;
}
while (span[right] > pivotValue)
{
right--;
}
if (left <= right)
{
int temp = span[left];
span[left] = span[right];
span[right] = temp;
left++;
right--;
}
}
QuickSort(span.Slice(0, left));
QuickSort(span.Slice(left));
}
比较两个Span
下面用举例的方式来展示比较两个Span的方法。
public static bool SequenceEqual(Span<byte> span1, Span<byte> span2)
{
if (span1.Length != span2.Length)
{
return false;
}
for (int i = 0; i < span1.Length; i++)
{
if (span1[i] != span2[i])
{
return false;
}
}
return true;
}
总结
Span是一个非常有用的数据类型,使用它有助于提高应用程序的性能和可读性。使用Span可以避免访问托管内存时的内存拷贝,提高程序的效率。Span API有助于简化代码,从而提高类型安全性。通过实践,Span也可以成为您的.NET Core开发工具箱中的一部分。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#如何安全、高效地玩转任何种类的内存之Span的本质 - Python技术站