0

I have this byte array:

byte[,,] somedata = new byte[100,150,3];
~somedata is now populated by an external input.

I want to use the values in just this:

byte[,] useThisDataOnly = new byte[100,150];

Presently, I use this:

foreach (int x=0,x< 100; x++)
{
    foreach (int y=0,y< 150; y++)
    {
       useThisDataOnly [x,y] = somedata[x,y,0];
    }
}

Is there a quicker way to do this at all?

Also, I am going to flatten this useThisDataOnly (as I heard it is quicker to manipulate with).

Any suggestions?

Andrew Simpson
  • 6,883
  • 11
  • 79
  • 179
  • what do you mean by quicker, more performant? at these array sizes it won't matter unless you do it very often. – thumbmunkeys Sep 09 '14 at 10:55
  • @thumbmunkeys Hi, thanks for your time and comment, Yes, I have been given a requirement that I will be receiving many of these arrays every second (groan - I have no control over that). I guess I am asking 2 questions here. My main focus is to convert 3dim to 2dim without using foreach in low memory usage and as quickly as I can :) – Andrew Simpson Sep 09 '14 at 10:57
  • hi @AlexFarber a good link (+1 from me) but the example shows [,] to []. What I am ideallyy after is [,,] to []? – Andrew Simpson Sep 09 '14 at 11:13
  • 1
    Might I suggest rather than pre-optimising the memory, try the quick and dirty loops first and **measure** the performance. Then you'll know **if** it is a problem. If so have a look at parallelism; see Task Parallel Library – Meirion Hughes Sep 09 '14 at 11:31
  • @MeirionHughes Hi, sorry I missed your comment. I have tried the 'quick and dirty loops' 1st and on my Quad PC it is not a problem. When I run the app in a lower spec PC and use jetBrains to measure performance it does seem to have a degrading affect – Andrew Simpson Sep 09 '14 at 11:53
  • 1
    You could always have a C++/CLI library (allows you to do true unmanaged code and feed it back to normal C# apps) to do the intensive aspects. But my guess is if you can just break up your work into 4/8 parts and have parallel tasks, you'll get the performance you need. – Meirion Hughes Sep 09 '14 at 12:21
  • Hi, I shall have to have alook how to do that and then test it. Thanks for your insights :) – Andrew Simpson Sep 09 '14 at 12:25

1 Answers1

1

The difficulty you have with optimising this is that the bytes that you want to leave out are in the most "rapidly moving" index.

What I mean by that is: If you assign consecutive numbers to each array element like so:

byte[,,] source = new byte[100, 150, 3];

for (int i = 0, n = 0; i < 100; ++i)
    for (int j = 0; j < 150; ++j)
        for (int k = 0; k < 3; ++k, ++n)
            source[i, j, k] = unchecked ((byte)n);

If you printed out the contents in flattened order, you'd get 0..255 repeating.

Then you exclude the final dimension:

for (int i = 0; i < 100; ++i)
    for (int j = 0; j < 150; ++j)
        dest[i, j] = source[i, j, 0];

If you print the result out in flattened order, you'll get:

0, 3, 6, 9, 12, 15, ...

So you can see that in order to transform the input to the output you need to take every third byte, and unfortunately there is no quick way to do that.

However, if you were able to ask for the array to be created with the index to be omitted first:

byte[,,] source = new byte[3, 100, 150];

then you would be able to use Buffer.BlockCopy().

I guess this isn't an option - if not, the hand-coded loop is probably the quickest you'll be able to do it without resorting to unsafe code.

Unsafe code

Here's how you can do it using unsafe code and pointers. This might not even make it faster, so before choosing to do it like this you need to (a) be sure that doing the simple way is really too slow and (b) do some careful timings of a release build using Stopwatch to ensure that it really is worth using unsafe code.

Also be aware of the drawbacks of using unsafe code! Unsafe code requires elevated permissions and can cause your program to crash with low-level C++-style wild pointer errors!

using System;
using System.Diagnostics;

namespace Demo
{
    internal static class Program
    {
        static void Main()
        {
            byte[,,] source = new byte[100, 150, 3];

            for (int i = 0, n = 0; i < 100; ++i)
                for (int j = 0; j < 150; ++j)
                    for (int k = 0; k < 3; ++k, ++n)
                        source[i, j, k] = unchecked ((byte)n);

            byte[,] dest1 = new byte[100, 150];
            byte[,] dest2 = new byte[100, 150];

            for (int i = 0; i < 100; ++i)
                for (int j = 0; j < 150; ++j)
                    dest1[i, j] = source[i, j, 0];

            unsafe
            {
                fixed (byte* sp = source)
                fixed (byte* dp = dest2)
                {
                    byte* q = dp;
                    byte* p = sp;

                    for (int i = 0; i < 100*150; ++i)
                    {
                        *q++ = *p;
                        p += 3;
                    }
                }
            }

            for (int i = 0; i < 100; ++i)
                for (int j = 0; j < 150; ++j)
                    Trace.Assert(dest1[i, j] == dest2[i, j], "Arrays should be identical");
        }
    }
}

I personally don't think that it will be too slow using the simple, safe loop anyway, but now at least you have some code to try out.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Hi, thanks for being so comprehensive. I understand what you are saying and it makes sense to me. Unfortunately, I have no control over the input. But I am curious though in regards to the unsafe code statement. Are you implying there is an approach using unsafe techniques and if so are you advising against it? Thanks for your time.. – Andrew Simpson Sep 09 '14 at 11:49
  • 1
    @AndrewSimpson Yes, you can use unsafe code and pointers, but this means that your assembly (or anything that uses it) must be marked as "unsafe", which can have security implications. Nevertheless, I'll post sample code so you can see. It might make it a bit faster - but quite possibly it won't! – Matthew Watson Sep 09 '14 at 11:53
  • that is so good of you. thank you very much for all your time! – Andrew Simpson Sep 09 '14 at 11:57
  • Hi, thank you v much. I will try this out and test as you suggested. Thanks – Andrew Simpson Sep 09 '14 at 13:22