在.Net 7中如何简化GetSubArray源码

前言

在Runtime源码更新中,有一个简化GetSubArray源码的更新,对GetSubArray还是有些印象的,在C#中切片功能就是调用GetSubArray函数实现的,如果不知道C#切片如何使用可以看这里: C#中的切片功能 

可以先看看上面这篇文章,因为里面有早期的GetSubArray源码,和下面的源码对比一下,发现现在的代码比原先的代码是简化过的,虽然改动并不大,但容易阅读,易于理解.

GetSubArray源码

public static T[] GetSubArray<T>(T[] array, Range range)
{
    if (array == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
    }

    //range记录开始位置和结束位置,根据数组的长度,计算出子数组起始位置和长度
    (int offset, int length) = range.GetOffsetAndLength(array.Length); 

    if (length == 0)
    {
        return Array.Empty<T>();  //如果为0,返回泛型长度为0的数组
    }

    T[] dest = new T[length]; //减少判断,根据长度分配泛型的数组

    // Due to array variance, it's possible that the incoming array is
    // actually of type U[], where U:T; or that an int[] <-> uint[] or
    // similar cast has occurred. In any case, since it's always legal
    // to reinterpret U as T in this scenario (but not necessarily the
    // other way around), we can use Buffer.Memmove here.
    //根据起始位置和长度,从数组从拷贝数据到子数组中
    Buffer.Memmove(
        ref MemoryMarshal.GetArrayDataReference(dest),
        ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), offset),
        (uint)length);

    return dest;
}

惯例还是用BenchmarkDotNet测试一下:

namespace net6perf
{
    [DisassemblyDiagnoser(printSource: true, maxDepth: 3)]
    public class GetSubArrayTest
    {
        public int[] array = new int[] { 1, 2, 3 };

        [Params(1024, 2048)]
        public int Count { get; set; }

        [Benchmark]
        public void Test()
        {
            int sum = 0;
            for (int i = 0; i < Count; i++)
            {
                sum += array[^1];
            }
        }
    }
}

C#中切片所需要的GetSubArray源码进行简化实现

通过基准测试,发现源码简化,没有带来性能提升,和.Net 6对比,性能相差不大.

秋风 2022-04-07