13

Is it possible in C# .Net (3.5 and above) to copy a variable into a byte[] buffer without creating any garbage in the process?

For instance:

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

...

creates the byte[] bytes intermediary object which becomes garbage (presuming a ref is no longer held to it)...

I wonder if using bitwise operators the variable can be copied directly into the buffer without creating the intermediary byte[]?

markmnl
  • 11,116
  • 8
  • 73
  • 109
  • Yes, it is possible, but it sounds to me like you are prematurely optimizing. The garbage you create in a method call, like small byte arrays and the like, are all Gen 0 objects and will be collected likely in a couple milliseconds or, probably, even less. Ephemeral object allocation and collection in the CLR is extremely efficient. – codekaizen Mar 09 '13 at 05:44
  • Whare are Gen 0 objects? – markmnl Mar 09 '13 at 05:51
  • (To your concern - I do not think the optimization is premature - I am not sure how I sounded like it was - I am writing a library that will be used by phones and using the buffer 100's possibly thousands of times a secound - and the GC is a concern on such resource constrained devices). – markmnl Mar 09 '13 at 05:53
  • 2
    It is a concern, but not until you measure it. If you haven't measured it, you don't know. Even the best engineers are often surprised by what the real perf bottlenecks are after measuring. – codekaizen Mar 09 '13 at 06:42
  • Gen0 is the "generation" which the GC uses to track the most recently created objects. Most workloads have patterns where numerous objects are allocated, used, and then go out of scope. It makes sense to look at this restricted set of objects when doing a GC instead of the entire heap. .Net does this and the speed with which the GC can allocate and collect objects that fit this pattern is breathtaking. See: http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)#Generational_GC_.28ephemeral_GC.29 – codekaizen Mar 09 '13 at 06:45
  • http://c2.com/cgi/wiki?PrematureOptimization – codekaizen Mar 09 '13 at 06:47
  • Sorry question - why garbage collection? byte is value type, not reference type. I see it's faster but not because GC collection avoided but you skipped calls to BitConverter.GetBytes and Buffer.BlockCopy – Swab.Jat May 13 '14 at 06:46
  • Becuase byte[] is a reference type - like all arrays it is malloced on the the heap. – markmnl May 13 '14 at 06:59

2 Answers2

8

Use pointers is the best and the fastest way: You can do this with any number of variables, there is no wasted memory, the fixed statement has a little overhead but it's too small

        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
        }
Rafael
  • 2,642
  • 2
  • 24
  • 30
  • 2
    Maybe something to mention: take care of all the pinned (fixed) objects. They may cause heap fragmentation which may end up using more memory in the end. – Caramiriel Feb 24 '14 at 07:29
4

Why can't you just do:

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

Note that the array here is not an indirection into the storage for the original Int32, it is very much a copy.

You are perhaps worried that bytes in your example is equivalent to:

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

.. but I assure you that it is not; it is a byte by byte copy of the bytes in the source Int32.

EDIT:

Based on your edit, I think you want something like this (requires unsafe context):

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;
    }
}
Ani
  • 111,048
  • 26
  • 262
  • 307