Mark Lakata
Mark Lakata

Reputation: 20838

Efficient way of copying an array of nullables to array of non-nullables of the same base type?

Is there an efficient way of copying an array of nullables (say byte?[]) to an array of non-nullables (say byte[]) assuming that the source array is guaranteed to not have any nullables (and if it does, it's ok to throw an exception)? Obviously, I can loop over the indices and copy each element individually.

This does not work. It compiles, but throws an ArrayTypeMismatchException at run-time.

 byte?[] sourceNullable = new byte?[]{1,2,3};
 byte[] destNonNullable = new byte[3];

 Array.Copy(sourceNullable,destNonNullable,3);

This will work but I am looking for something "better"

for(int i=0;i<3;i++) {
    destNonNullable[i] = sourceNullable[i] ?? 0;
}

I'm willing to accept the answer: what's wrong with the explicit loop? And why are you wasting time optimizing this? :)


Edit: I tried using the Linq style Cast<>(), but that turns out to be much slower. The time summary from my code below:

for loop = 585 ms

Linq Cast = 3626 ms

The input image file is a sparse array, filled with sections of nulls.

        uint rowsize = 16;
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (UInt32 address = start & 0xFFFFFFF0; address <= last; address += rowsize)
        {
            Int32 imageOffset = (Int32)(address - start);
            Int32 maxRowLen = (int)rowsize;
            if (maxRowLen + imageOffset > image.Length) maxRowLen = (image.Length - imageOffset);

            if (maxRowLen == 0) throw new Exception("this should not happen");

            int ptr = 0;
            while (ptr < maxRowLen)
            {
                while (ptr < maxRowLen && image[imageOffset + ptr] == null) ptr++;
                int startOffset = ptr;
                while (ptr < maxRowLen && image[imageOffset + ptr] != null) ptr++;
                int stopOffset = ptr;

                if (startOffset < maxRowLen)
                {
 #if false
                    int n = stopOffset - startOffset;
                    byte[] subdata = new byte[n];
                    for (int i = 0; i < n; i++)
                    {
                        subdata[i] = image[imageOffset + startOffset + i] ?? 0;
                    }
 #else
                    byte[] subdata = image.Skip(imageOffset + startOffset).Take(stopOffset - startOffset).Cast<byte>().ToArray();
 #endif
                    IntelHexRecord rec = new IntelHexRecord((uint)(address + startOffset), subdata);
                    records.Add(rec);
                }
            }
        }
        sw.Stop();
        Console.WriteLine("elapsed: {0} ms", sw.ElapsedMilliseconds);

Upvotes: 4

Views: 1711

Answers (1)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

You can use LINQ, like this:

byte[] destNonNullable = sourceNullable.Cast<byte>().ToArray();

However, this is not faster than what you are doing. If you need a faster way of copying a fixed number of bytes known at compile time, you can eliminate the loop overhead, which is not that big, but should work if you must squeeze the last CPU cycle out of this:

byte[] destNonNullable = new[] {
    sourceNullable[0].Value
,   sourceNullable[1].Value
,   sourceNullable[2].Value
};

You can also reduce the overhead by unwinding the loop. For example, if you know that the number of bytes that you would like to copy is divisible by four, you can do this:

Debug.Assert(N % 4 == 0); // Otherwise, the loop below wouldn't stop
for (int i = 0 ; i != N ; i += 4) {
    destNonNullable[i] = sourceNullable[i].Value;
    destNonNullable[i+1] = sourceNullable[i+1].Value;
    destNonNullable[i+2] = sourceNullable[i+2].Value;
    destNonNullable[i+3] = sourceNullable[i+3].Value;
}

Upvotes: 5

Related Questions