Reputation: 20838
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
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