【问题标题】:C# Copy variables into buffer without creating garbage?C#将变量复制到缓冲区而不产生垃圾?
【发布时间】:2013-03-09 13:39:32
【问题描述】:

是否可以在 C# .Net(3.5 及更高版本)中将变量复制到 byte[] 缓冲区而不在进程中创建任何垃圾?

例如:

int variableToCopy = 9861;

byte[] buffer = new byte[1024];
byte[] bytes = BitConverter.GetBytes(variableToCopy);
Buffer.BlockCopy(bytes, 0, buffer, 0, 4);

float anotherVariableToCopy = 6743897.6377f;
bytes = BitConverter.GetBytes(anotherVariableToCopy);
Buffer.BlockCopy(bytes, 0, buffer, 4, sizeof(float));

...

创建变成垃圾的 byte[] bytes 中间对象(假设不再持有 ref)...

我想知道使用按位运算符是否可以将变量直接复制到缓冲区中而无需创建中间字节[]?

【问题讨论】:

  • 是的,这是可能的,但在我看来,您过早地进行了优化。您在方法调用中创建的垃圾,如小字节数组等,都是 Gen 0 对象,可能会在几毫秒甚至更短的时间内被收集。 CLR 中的临时对象分配和收集非常高效。
  • 什么是 Gen 0 对象?
  • (您担心 - 我不认为优化为时过早 - 我不确定我听起来如何 - 我正在编写一个将由手机使用的库并可能使用缓冲区 100每秒数千次 - GC 是此类资源受限设备的一个问题)。
  • 这是一个问题,但直到你测量它。如果你没有测量它,你不知道。即使是最优秀的工程师也常常对测量后真正的性能瓶颈感到惊讶。
  • Gen0 是 GC 用来跟踪最近创建的对象的“世代”。大多数工作负载都有分配、使用大量对象然后超出范围的模式。在执行 GC 而不是整个堆时查看这些受限对象集是有意义的。 .Net 做到了这一点,而 GC 分配和收集符合这种模式的对象的速度令人惊叹。见:en.wikipedia.org/wiki/…

标签: c# .net buffer


【解决方案1】:

使用指针是最好也是最快的方法: 您可以使用任意数量的变量来执行此操作,不会浪费内存,固定语句有一点开销但它太小了

        int v1 = 123;
        float v2 = 253F;
        byte[] buffer = new byte[1024];
        fixed (byte* pbuffer = buffer)
        {
            //v1 is stored on the first 4 bytes of the buffer:
            byte* scan = pbuffer;
            *(int*)(scan) = v1;
            scan += 4; //4 bytes per int

            //v2 is stored on the second 4 bytes of the buffer:
            *(float*)(scan) = v2;
            scan += 4; //4 bytes per float
        }

【讨论】:

  • 也许要提一下:照顾所有固定(固定)的对象。它们可能会导致堆碎片,最终可能会使用更多的内存。
【解决方案2】:

你为什么不能这样做:

byte[] buffer = BitConverter.GetBytes(variableToCopy);

请注意,这里的数组不是对原始 Int32 存储的间接访问,它在很大程度上是一个副本。

您可能担心您的示例中的bytes 相当于:

unsafe
{
    byte* bytes = (byte*) &variableToCopy;
}

.. 但我向你保证不是;它是源 Int32 中字节的逐字节副本。

编辑

根据您的编辑,我认为您想要这样的东西(需要不安全的上下文):

public unsafe static void CopyBytes(int value, byte[] destination, int offset)
{
    if (destination == null)
        throw new ArgumentNullException("destination");

    if (offset < 0 || (offset + sizeof(int) > destination.Length))
        throw new ArgumentOutOfRangeException("offset");

    fixed (byte* ptrToStart = destination)
    {
        *(int*)(ptrToStart + offset) = value;
    }
}

【讨论】:

  • 因为与我的示例不同,我的缓冲区需要保存许多变量 - 我会更新我的问题
猜你喜欢
  • 2016-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-30
  • 1970-01-01
  • 2021-11-28
  • 2010-09-27
  • 2018-07-04
相关资源
最近更新 更多