4

I have a way that converts ulongs to bytes using binary shifts in a for statement but it's not very time efficient. Is there a way to cast a ulong array of size 64 directly into a byte array of size 512? This is a section of code that runs thousands of times and I need to save every millisecond so I can in turn save seconds.

Edit: Right now this is what I'm doing:

                for (int k = 0; k < ulongs.Length; k++) {
                    bytes[(k << 3)] = (byte)(ulongs[k] >> 56);
                    bytes[(k << 3) + 1] = (byte)(ulongs[k] >> 48);
                    bytes[(k << 3) + 2] = (byte)(ulongs[k] >> 40);
                    bytes[(k << 3) + 3] = (byte)(ulongs[k] >> 32);
                    bytes[(k << 3) + 4] = (byte)(ulongs[k] >> 24);
                    bytes[(k << 3) + 5] = (byte)(ulongs[k] >> 16);
                    bytes[(k << 3) + 6] = (byte)(ulongs[k] >> 8);
                    bytes[(k << 3) + 7] = (byte)(ulongs[k]);
                }
Ajay
  • 18,086
  • 12
  • 59
  • 105
Corey Ogburn
  • 24,072
  • 31
  • 113
  • 188

4 Answers4

4
unsafe 
{
    fixed (ulong* src = ulongs) 
    {
        Marshal.Copy(new IntPtr((void*)src), bytes, 0, 512);
    }
}

This seems to work. I'm not sure if fixed is required, but I lost an entire second during timed tests.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Corey Ogburn
  • 24,072
  • 31
  • 113
  • 188
  • Fixed is required because the fixed statement prevents the garbage collector from relocating a movable variable. And without fixed your code would not even compile. – Igor Korkhov Jan 25 '10 at 18:26
  • And your version outperforms mine, it's great that you reminded me of Marshal.Copy(), thank you! – Igor Korkhov Jan 25 '10 at 18:30
3

Probably the fastest way of doing it is to use unsafe and fixed constructions. Here's an example:

ulong[] ulongs = new ulong[64];
byte[] bytes = new byte[512];

unsafe
{
    fixed (ulong* src = ulongs)
    {
        byte* pb = (byte*)src;
        for (int i = 0; i < bytes.Count(); i++)
        {
             bytes[i] = *(pb + i);
        }
    }
}

Please do not forget to use /unsafe compiler switch when compiling the code above.

Edit: here's modified version of the first code fragment, which runs much faster on my machine:

unsafe
{
    fixed (ulong* src = ulongs)
    {
        fixed (byte *dst = bytes)
        {
            ulong* pl = (ulong*)dst;
            for (int i = 0; i < ulongs.Count(); i++)
            {
                *(pl + i) = *(src + i);
            }   
        }
    }
}
Igor Korkhov
  • 8,283
  • 1
  • 26
  • 31
  • I've tried this code, and when my program was usually running at 3.5 seconds, it jumped to 8.5 seconds. I had high hopes for this, but I'm looking for a way that doesn't include a for statement, just an unmanaged cast (those being the best words I can think of to call what I'm looking for) – Corey Ogburn Jan 25 '10 at 18:00
  • You can unroll for() loop by writing something like "byte[i] = *(pb + i); byte[i+1] = *(pb + i+1); byte[i+2] = *(pb + i+2); ...; byte[i+7] = *(pb + i +7);" inside the for() statement body – Igor Korkhov Jan 25 '10 at 18:11
0

You may be able to achieve this using a struct and explicit layout instructions using the StructLayout attribute to force variables to occupy the same space.

That way you could write the value using the ulong property and read it using the byte array property. This assumes, of course, that byte array truly takes up the space you expect and that it isn't using 32 bits to hold the 8 bits of information. I'll have to look that up in the specification, which I have not done yet.

Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
  • Nope, the byte[] has some other info, so you get an error when trying to load such a type because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field. – chris.w.mclean Jan 25 '10 at 17:02
  • If you know the size of that field, you can probably offset the ulong appropriately. – Jeff Yates Jan 25 '10 at 17:47
0

Use the System.Buffer helper class.

ulong[] buffer = null;
int byteLength = Buffer.ByteLength(buffer);
byte[] byteBuffer = new byte[byteLength];
Buffer.BlockCopy(buffer, 0, byteBuffer, 0, byteLength);
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280