如何在不产生垃圾的情况下将变量复制到缓冲区中?使用C#。

13

在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中介对象,该对象将成为垃圾(假设不再保留对它的引用)...
我想知道是否可以使用位运算符直接将变量复制到缓冲区中,而不必创建中介byte[]。

2
这是一个问题,但只有在你测量它之后才会引起关注。如果你没有测量过,你就不知道。即使最好的工程师在测量后也经常会对真正的性能瓶颈感到惊讶。 - codekaizen
Gen0是GC用来跟踪最近创建的对象的"代"。大多数工作负载都有这样的模式:分配大量对象,使用这些对象,然后这些对象超出范围。进行GC时,查看此受限对象集合而不是整个堆是有意义的。.Net就是这样做的,GC可以分配和回收符合此模式的对象的速度令人惊叹。请参见:http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)#Generational_GC_.28ephemeral_GC.29 - codekaizen
http://c2.com/cgi/wiki?PrematureOptimization - codekaizen
抱歉,我有一个问题 - 为什么需要垃圾回收?byte是值类型,而不是引用类型。我知道这样做更快,但并不是因为避免了GC回收,而是因为你跳过了对BitConverter.GetBytes和Buffer.BlockCopy的调用。 - Swab.Jat
因为byte[]是一个引用类型 - 像所有的数组一样,它在堆上分配内存。 - markmnl
显示剩余3条评论
2个回答

8

使用指针是最好和最快的方法: 您可以对任意数量的变量执行此操作,没有浪费的内存,fixed语句有一些开销,但它很小。

        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
也许需要提醒一下:要注意所有被固定的对象。它们可能会导致堆碎片化,最终可能会使用更多的内存。 - Caramiriel

4
为什么不能直接这样做:

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;
    }
}

因为与我的示例不同,我的缓冲区需要保存许多变量 - 我会更新我的问题。 - markmnl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接