4

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.

3
  • The size of the 2D array is (1,N). Assuming it has [1,2,3,4] in the first dimension and nothing else in the 2nd, the 1D array will also be the same [1,2,3,4]. But it will be a double[] array of length N instead of double[,] of size (1,N). Commented Apr 5, 2019 at 3:00
  • @vasily.sib Try it yourself and see why not ;) Commented Apr 5, 2019 at 3:03
  • yes, not a jagged array. Commented Apr 5, 2019 at 3:05

3 Answers 3

12

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

Benchmarks

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         │
└──────────────────┴──────────┴──────────────────┴──────────────┘

Results for byte array

┌── 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 % │
└────────────────────────────────────────────────────────────────────────────────────────┘

Results for double array

┌── 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 Code

[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

Sign up to request clarification or add additional context in comments.

2 Comments

I just want to know which tool do you use to build such good look tables?
@shingo i have box and table drawing library that does that fancy stuff with the benchmarker tool wrote, ill chuck it on github and nuget it when i get a chance to sanitise it a bit
3

I was thinking about jagged array at first, so this question was looking too easy:) How ever, with 2D arrays it's not so hard too:

using System.Linq;

var twoDArray = new double[1,5] { { 1, 2, 3, 4, 5 } };
var oneDArray = twoDArray.OfType<double>().ToArray();
// oneDArray == { 1, 2, 3, 4, 5 }

It's also working with any sized 2D arrays:

var twoDArray2 = new double[2,3] { { 1, 2, 3 }, { 4, 5, 6 } };
var oneDArray2 = twoDArray2.OfType<double>().ToArray();
// oneDArray2 == { 1, 2, 3, 4, 5, 6 }

Lyrical digression about performance:

LINQ is (much more) slower than direct memory manipulation, for sure. But you should keep in mind, that LINQ (and C# as well) is not about "execution time optimisation", but about "development convenience". So as for me, easy readable oneline LINQ expression is preferable then fast memory manipulating magic, even though it converts double[1, 65535] to double[65535] for 35.116 µs instead of 7.402 ms.

C# is slow by its managed nature, so if you need a blazing fast running algorithm - I advise you to switch to C++.

2 Comments

Upvote, i actually agree, that the one liner is convenient and easy to understand
I marked the other answer as the solution because technically it was the exact answer, and a great one, to my original question. However, I acknowledge the optimization vs convenience trade-off and I ended up using this answer in my C# app for now. Meanwhile, I am already working on migrating everything to C++ for an even faster-running algorithm.
0

We can convert 2d array in c# to one d array using simple logic below. there is no linq or another namespace required for it.

/* C# Code */
int m=2, n=3;
int[,] a= new int[2,3] {
    {  3,  7,  9 },
    {  6,  88,  9 },
};
int[] b=new int[6];;
int k = 0;

for (int i = 0; i < m; i++)
{
    for (int j = 0; j < n; j++)
    {
       Array1D[k++] = Array2D[i, j];
    }
 }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.