C#中的快速数组复制

本文关键字:数组 复制 | 更新日期: 2023-09-27 17:59:44

我有一个C#类,它包含一个int[]数组(以及其他几个字段,但数组是最主要的)。代码通常会创建此类的副本,分析显示,复制此数组的Array.Copy()调用需要花费大量时间。我能做些什么让它更快?

数组的大小非常小并且是恒定的:有12个元素。所以理想情况下,我想要一个类似C风格的数组:一个位于类内部的内存块(而不是指针)。这在C#中可能吗?(如果需要,我可以使用不安全的代码。)

我已经试过了:

1) 使用UIn64和位移位代替数组。(每个元素的值也很小。)这确实使复制速度很快,但总体上会减慢程序的速度。

2) 为每个数组元素使用单独的字段:int element0、int element1、int element 2等。同样,当我必须访问给定索引处的元素时,这总体上会更慢。

C#中的快速数组复制

如果您真的关心速度,我会检查System.Buffer.BlockCopy

http://msdn.microsoft.com/en-us/library/system.buffer.blockcopy.aspx

简单示例:

  int[] a = new int[] {1,2,3,4,5,6,7,8};
  int[] b = new int[a.Length];
  int size = sizeof(int);
  int length = a.Length * size;               
  System.Buffer.BlockCopy(a, 0, b, 0, length);

在这里进行了精彩的讨论:Array.Copy与Buffer.BlockCopy

这篇文章很旧,但任何与OP处境相似的人都应该看看structs中的固定大小缓冲区。它们正是OP所要求的:一个大小不变的基元类型数组,直接存储在类中。

您可以创建一个结构来表示您的集合,该集合将包含固定大小的缓冲区。数据将直接存储在结构中,结构将直接存储到类中。你可以通过简单的作业进行复制。

它们附带了一些警告:

  • 它们只能与基元类型一起使用
  • 它们需要在结构中使用"unsafe"关键字
  • 在编译时必须知道大小

过去,您必须使用固定关键字和指针来访问它们,但最近针对性能编程对C#进行的更改使这变得不必要。现在,您可以像处理数组一样处理它们。

public unsafe struct MyIntContainer
{
    private fixed int myIntegers[12];
    public int this[int index]
    {
        get => this.myIntegers[index];
        set => this.myIntegers[index] = value;
    }
}

没有内置的绑定检查,因此最好将其包含在这样的属性中,将跳过绑定检查的任何功能封装在方法中。我在手机上,否则我会把它作为我的榜样。

您询问了有关托管阵列的问题。如果您满足于使用fixed/unsafe,这可能非常快。

struct是可赋值的,就像任何基元一样。由于缺少方法调用开销,几乎可以肯定比Buffer.BlockCopy()或任何其他方法都快:

public unsafe struct MyStruct //the actual struct used, contains all
{
    public int a;
    public unsafe fixed byte buffer[16];
    public ulong b;
    //etc.
}
public unsafe struct FixedSizeBufferWrapper //contains _only_ the buffer
{
    public unsafe fixed byte buffer[16];
}
unsafe 
{
    fixed (byte* bufferA = myStructA.buffer, bufferB = myStructB.buffer)
    {
        *((FixedSizeBufferWrapper*)bufferA) =
        *((FixedSizeBufferWrapper*)bufferB);
    }
}

我们将每个原始结构的fixed大小的byte缓冲区强制转换为包装器指针类型,并取消引用每个指针,这样我们就可以按值将一个指针分配给另一个指针;直接分配fixed缓冲区是不可能的,因此包装器基本上是零开销的(它只影响指针算术中使用的值)。那个包装只用于铸造。

我们必须进行强制转换,因为(至少在我的C#版本中)我们不能分配除基元类型(通常为byte[])之外的任何其他类型作为缓冲区,并且不允许在fixed(...)内部进行强制转换。

编辑:这似乎被翻译成了对引擎盖下Buffer.Memcpy()(在我的情况下是Buffer.memcpy4(),在Unity/Mono中)的调用来进行复制。