views:

305

answers:

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 shave 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]);
                }
A: 

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
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
If you know the size of that field, you can probably offset the ulong appropriately.
Jeff Yates
+2  A: 

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
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
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
+3  A: 
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.

Corey Ogburn
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
And your version outperforms mine, it's great that you reminded me of Marshal.Copy(), thank you!
Igor Korkhov
A: 

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