I have a multidimensional double[,]
array, whose size is [1,N] (Assume N is known). What's the fastest way to convert this to a 1-D double[]
array of length N?
I'm new to C# and I use it to interact with Matlab functions. The Matlab function I use returns a 1-D row vector. In C# it is perceived as object[,]
and I can only cast it to double[,]
. However, I need it to be of type double[]
as an input to another function. Is there a fast way to convert this 2-D double array to a 1-D array with the same elements and the same order?
I need the conversion to be as fast as possible since I am working on a real-time application.
The fastest way is one of the direct memory copy methods, like Buffer.BlockCopy
, or Marshal.Copy
.
Example
var ary1 = new int[3,3];
ary1[0, 0] = 0;
ary1[0, 1] = 1;
ary1[0, 2] = 2;
ary1[1, 0] = 3;
ary1[1, 1] = 4;
ary1[1, 2] = 5;
ary1[2, 0] = 6;
ary1[2, 1] = 7;
ary1[2, 2] = 8;
var ary2 = new int[9];
Buffer.BlockCopy(ary1, 0, ary2, 0, 9 * sizeof(int));
Console.WriteLine(string.Join(",", ary2));
Output
0,1,2,3,4,5,6,7,8
You could also use memcpy
, however you will incur the initial overhead from the PInvoke
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);
The benchmarks were run 100 times, garbage collected after each run, and validated against each other for accuracy, the results are collated from the top 75% of the fasted runs, the scale is the dimension length of a multidimensional array ie double[scale,scale]
┌──────────────────┬────────────────────────────────────────────┐
│ Test Mode │ Release (64Bit) │
│ Test Framework │ .NET Framework 4.7.1 (CLR 4.0.30319.42000) │
╞══════════════════╪════════════════════════════════════════════╡
│ Operating System │ Microsoft Windows 10 Pro │
│ Version │ 10.0.17763 │
├──────────────────┼────────────────────────────────────────────┤
│ CPU System │ Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz │
│ CPU Description │ Intel64 Family 6 Model 158 Stepping 9 │
├──────────────────┼──────────┬──────────────────┬──────────────┤
│ Cores (Threads) │ 4 (8) │ Architecture │ x64 │
│ Clock Speed │ 3600 MHz │ Bus Speed │ 100 MHz │
│ L2Cache │ 1 MB │ L3Cache │ 8 MB │
└──────────────────┴──────────┴──────────────────┴──────────────┘
┌── Standard input ──────────────────────────────────────────────────────────────────────┐
│ Value │ Average │ Fastest │ Cycles │ Garbage │ Test │ Gain │
├── Scale 256 ────────────────────────────────────────────────────────────── 0.628 sec ──┤
│ MarshalCopy │ 6.450 µs │ 4.300 µs │ 26.698 K │ 0.000 B │ Pass │ 24.21 % │
│ memcpy │ 7.992 µs │ 4.700 µs │ 32.758 K │ 0.000 B │ Pass │ 6.09 % │
│ BlockCopy │ 8.511 µs │ 4.600 µs │ 37.053 K │ 0.000 B │ Base │ 0.00 % │
│ ElemCopy Unsafe │ 26.124 µs │ 24.400 µs │ 97.794 K │ 0.000 B │ Pass │ -206.96 % │
│ ElemCopy │ 75.426 µs │ 72.300 µs │ 273.201 K │ 0.000 B │ Pass │ -786.27 % │
│ Linq │ 7.619 ms │ 7.078 ms │ 27.103 M │ 0.000 B │ Pass │ -89,429.16 % │
├── Scale 512 ────────────────────────────────────────────────────────────── 1.826 sec ──┤
│ MarshalCopy │ 17.939 µs │ 17.300 µs │ 68.142 K │ 0.000 B │ Pass │ 1.33 % │
│ BlockCopy │ 18.182 µs │ 17.300 µs │ 69.770 K │ 0.000 B │ Base │ 0.00 % │
│ memcpy │ 25.897 µs │ 19.200 µs │ 97.357 K │ 0.000 B │ Pass │ -42.44 % │
│ ElemCopy Unsafe │ 128.776 µs │ 102.400 µs │ 471.381 K │ 0.000 B │ Pass │ -608.28 % │
│ ElemCopy │ 293.237 µs │ 285.400 µs │ 1.055 M │ 0.000 B │ Pass │ -1,512.82 % │
│ Linq │ 31.057 ms │ 29.750 ms │ 110.869 M │ 0.000 B │ Pass │ -170,714.99 % │
├── Scale 1,024 ──────────────────────────────────────────────────────────── 6.579 sec ──┤
│ memcpy │ 268.747 µs │ 255.600 µs │ 972.409 K │ 0.000 B │ Pass │ 12.28 % │
│ BlockCopy │ 306.371 µs │ 291.500 µs │ 1.104 M │ 0.000 B │ Base │ 0.00 % │
│ MarshalCopy │ 307.868 µs │ 293.100 µs │ 1.111 M │ 0.000 B │ Pass │ -0.49 % │
│ ElemCopy Unsafe │ 583.684 µs │ 561.100 µs │ 2.103 M │ 0.000 B │ Pass │ -90.52 % │
│ ElemCopy │ 1.325 ms │ 1.305 ms │ 4.768 M │ 0.000 B │ Pass │ -332.50 % │
│ Linq │ 122.561 ms │ 120.700 ms │ 439.940 M │ 0.000 B │ Pass │ -39,904.01 % │
├── Scale 2,048 ─────────────────────────────────────────────────────────── 26.084 sec ──┤
│ memcpy │ 1.179 ms │ 1.129 ms │ 4.230 M │ 0.000 B │ Pass │ 17.50 % │
│ MarshalCopy │ 1.397 ms │ 1.346 ms │ 5.029 M │ 0.000 B │ Pass │ 2.25 % │
│ BlockCopy │ 1.429 ms │ 1.360 ms │ 5.135 M │ 0.000 B │ Base │ 0.00 % │
│ ElemCopy Unsafe │ 2.441 ms │ 2.312 ms │ 8.757 M │ 0.000 B │ Pass │ -70.88 % │
│ ElemCopy │ 5.466 ms │ 5.264 ms │ 19.587 M │ 0.000 B │ Pass │ -282.61 % │
│ Linq │ 497.788 ms │ 489.885 ms │ 1.786 B │ 0.000 B │ Pass │ -34,743.98 % │
├── Scale 4,096 ─────────────────────────────────────────────────────────── 42.833 sec ──┤
│ memcpy │ 5.218 ms │ 4.889 ms │ 18.633 M │ 0.000 B │ Pass │ 15.45 % │
│ BlockCopy │ 6.172 ms │ 5.887 ms │ 22.141 M │ 0.000 B │ Base │ 0.00 % │
│ MarshalCopy │ 6.255 ms │ 5.871 ms │ 22.350 M │ 0.000 B │ Pass │ -1.35 % │
│ ElemCopy Unsafe │ 9.972 ms │ 9.535 ms │ 35.716 M │ 0.000 B │ Pass │ -61.57 % │
│ ElemCopy │ 22.149 ms │ 21.741 ms │ 79.508 M │ 0.000 B │ Pass │ -258.87 % │
│ Linq │ 1.969 s │ 1.948 s │ 7.067 B │ 0.000 B │ Pass │ -31,796.88 % │
└────────────────────────────────────────────────────────────────────────────────────────┘
┌── Standard input ─────────────────────────────────────────────────────────────────────┐
│ Value │ Average │ Fastest │ Cycles │ Garbage │ Test │ Gain │
├── Scale 256 ───────────────────────────────────────────────────────────── 0.688 sec ──┤
│ BlockCopy │ 35.116 µs │ 32.000 µs │ 131.112 K │ 0.000 B │ Base │ 0.00 % │
│ MarshalCopy │ 43.879 µs │ 34.900 µs │ 162.031 K │ 0.000 B │ Pass │ -24.96 % │
│ ElemCopy Unsafe │ 44.182 µs │ 39.500 µs │ 162.891 K │ 0.000 B │ Pass │ -25.82 % │
│ memcpy │ 60.113 µs │ 58.200 µs │ 219.950 K │ 0.000 B │ Pass │ -71.19 % │
│ ElemCopy │ 94.418 µs │ 86.100 µs │ 356.173 K │ 0.000 B │ Pass │ -168.88 % │
│ Linq │ 7.402 ms │ 7.123 ms │ 26.638 M │ 0.000 B │ Pass │ -20,979.54 % │
├── Scale 512 ───────────────────────────────────────────────────────────── 2.237 sec ──┤
│ memcpy │ 612.603 µs │ 552.000 µs │ 2.199 M │ 0.000 B │ Pass │ 10.20 % │
│ ElemCopy Unsafe │ 641.013 µs │ 586.200 µs │ 2.312 M │ 0.000 B │ Pass │ 6.03 % │
│ MarshalCopy │ 675.192 µs │ 621.200 µs │ 2.434 M │ 0.000 B │ Pass │ 1.02 % │
│ BlockCopy │ 682.161 µs │ 622.700 µs │ 2.458 M │ 0.000 B │ Base │ 0.00 % │
│ ElemCopy │ 745.692 µs │ 708.800 µs │ 2.687 M │ 0.000 B │ Pass │ -9.31 % │
│ Linq │ 33.579 ms │ 31.039 ms │ 119.974 M │ 0.000 B │ Pass │ -4,822.46 % │
├── Scale 1,024 ─────────────────────────────────────────────────────────── 7.830 sec ──┤
│ memcpy │ 2.708 ms │ 2.488 ms │ 9.712 M │ 0.000 B │ Pass │ 20.63 % │
│ ElemCopy Unsafe │ 3.156 ms │ 2.789 ms │ 11.324 M │ 0.000 B │ Pass │ 7.51 % │
│ MarshalCopy │ 3.208 ms │ 2.979 ms │ 11.508 M │ 0.000 B │ Pass │ 5.97 % │
│ ElemCopy │ 3.342 ms │ 3.091 ms │ 12.021 M │ 0.000 B │ Pass │ 2.05 % │
│ BlockCopy │ 3.412 ms │ 2.959 ms │ 12.234 M │ 0.000 B │ Base │ 0.00 % │
│ Linq │ 125.854 ms │ 122.872 ms │ 451.735 M │ 0.000 B │ Pass │ -3,588.76 % │
├── Scale 2,048 ────────────────────────────────────────────────────────── 29.876 sec ──┤
│ memcpy │ 10.989 ms │ 10.288 ms │ 39.509 M │ 0.000 B │ Pass │ 15.14 % │
│ ElemCopy Unsafe │ 12.075 ms │ 11.418 ms │ 43.436 M │ 0.000 B │ Pass │ 6.76 % │
│ BlockCopy │ 12.950 ms │ 12.462 ms │ 46.578 M │ 0.000 B │ Base │ 0.00 % │
│ MarshalCopy │ 13.032 ms │ 12.427 ms │ 46.876 M │ 0.000 B │ Pass │ -0.64 % │
│ ElemCopy │ 13.469 ms │ 12.689 ms │ 48.471 M │ 0.000 B │ Pass │ -4.01 % │
│ Linq │ 502.897 ms │ 497.335 ms │ 1.805 B │ 0.000 B │ Pass │ -3,783.35 % │
├── Scale 4,096 ────────────────────────────────────────────────────────── 58.669 sec ──┤
│ memcpy │ 45.901 ms │ 44.148 ms │ 164.750 M │ 0.000 B │ Pass │ 15.80 % │
│ ElemCopy Unsafe │ 51.889 ms │ 50.497 ms │ 186.137 M │ 0.000 B │ Pass │ 4.82 % │
│ MarshalCopy │ 53.237 ms │ 51.847 ms │ 191.248 M │ 0.000 B │ Pass │ 2.34 % │
│ BlockCopy │ 54.514 ms │ 52.417 ms │ 195.778 M │ 0.000 B │ Base │ 0.00 % │
│ ElemCopy │ 56.551 ms │ 54.674 ms │ 203.163 M │ 0.000 B │ Pass │ -3.74 % │
│ Linq │ 2.004 s │ 1.976 s │ 7.192 B │ 0.000 B │ Pass │ -3,575.84 % │
└───────────────────────────────────────────────────────────────────────────────────────┘
[Test("BlockCopy", "", true)]
public double[] Test1(double[,] input, int scale)
{
var width = input.GetLength(0);
var height = input.GetLength(1);
var size = width * height;
var result = new double[size];
Buffer.BlockCopy(input, 0, result, 0, size * sizeof(double));
return result;
}
[Test("MarshalCopy", "", false)]
public unsafe double[] Test2(double[,] input, int scale)
{
var width = input.GetLength(0);
var height = input.GetLength(1);
var size = width * height;
var result = new double[size];
fixed (double* pInput = input)
Marshal.Copy((IntPtr)pInput, result, 0, size );
return result;
}
[Test("ElemCopy", "", false)]
public double[] Test3(double[,] input, int scale)
{
var width = input.GetLength(0);
var height = input.GetLength(1);
var size = width * height;
var result = new double[size];
for (var i = 0; i < width; i++)
for (var j = 0; j < height; j++)
result[i * height + j] = input[i,j];
return result;
}
[Test("ElemCopy Unsafe", "", false)]
unsafe public double[] Test4(double[,] input, int scale)
{
var width = input.GetLength(0);
var height = input.GetLength(1);
var size = width * height;
var result = new double[size];
fixed (double* pInput = input, pResult = result)
for (var i = 0; i < width; i++)
for (var j = 0; j < height; j++)
*(pResult + i * height + j) = *(pInput + i * height + j);
return result;
}
[Test("memcpy", "", false)]
unsafe public double[] Test5(double[,] input, int scale)
{
var width = input.GetLength(0);
var height = input.GetLength(1);
var size = width * height;
var result = new double[size];
fixed (double* pInput = input, pResult = result)
memcpy((IntPtr)pResult, (IntPtr)pInput, (UIntPtr)(size * sizeof(double)));
return result;
}
[Test("Linq", "", false)]
unsafe public double[] Test6(double[,] input, int scale)
{
return input.OfType<double>().ToArray();
}
Note : You should probably run these tests yourself on your own spec pcs, framework and such and should only be used as a guide
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With