tags:

views:

4312

answers:

5

I have huge 3D arrays of numbers in my .NET application. I need to convert them to a 1D array to pass it to a COM library. Is there a way to convert the array without making a copy of all the data?

I can do the conversion like this, but then I use twice the ammount of memory which is an issue in my application:

  double[] result = new double[input.GetLength(0) * input.GetLength(1) * input.GetLength(2)];
  for (i = 0; i < input.GetLength(0); i++) 
    for (j = 0; j < input.GetLength(1); j++) 
      for (k = 0; k < input.GetLength(2); k++) 
        result[i * input.GetLength(1) * input.GetLength(2) + j * input.GetLength(2) + k)] = input[i,j,l];
  return result;
+6  A: 

Unfortunately, C# arrays aren't guaranteed to be in contiguous memory like they are in closer-to-the-metal languages like C. So, no. There's no way to convert double[,,] to double[] without an element-by-element copy.

Jekke
+7  A: 

I don't believe the way C# stores that data in memory would make it feasible the same way a simple cast in C would. Why not use a 1d array to begin with and perhaps make a class for the type so you can access it in your program as if it were a 3d array?

Loren Segal
+1  A: 

Without knowing details of your COM library, I'd look into creating a facade class in .Net and exposing it to COM, if necessary.
Your facade would take a double[,,] and have an indexer that will map from [] to [,,].

Edit: I agree about the points made in the comments, Lorens suggestion is better.

Robert Jeppesen
This won't work. COM will **need** to have the literal double[] array. If anything, you'd want to create a .NET class that made the double[] act like a double[,,].
Jonathan
I am not able to change the COM library.
Hallgrim
+1  A: 

As a workaround you could make a class which maintains the array in one dimensional form (maybe even in closer to bare metal form so you can pass it easily to the COM library?) and then overload operator[] on this class to make it usable as a multidimensional array in your C# code.

Tobi
+3  A: 

Consider abstracting access to the data with a Proxy (similar to iterators/smart-pointers in C++). Unfortunately, syntax isn't as clean as C++ as operator() not available to overload and operator[] is single-arg, but still close.

Of course, this extra level of abstraction adds complexity and work of its own, but it would allow you to make minimal changes to existing code that uses double[,,] objects, while allowing you to use a single double[] array for both interop and your in-C# computation.

class Matrix3
{
    // referece-to-element object
    public struct Matrix3Elem{
     private Matrix3Impl impl;
     private uint dim0, dim1, dim2;
     // other constructors
     Matrix3Elem(Matrix3Impl impl_, uint dim0_, uint dim1_, uint dim2_) {
      impl = impl_; dim0 = dim0_; dim1 = dim1_; dim2 = dim2_;
     }
     public double Value{
      get { return impl.GetAt(dim0,dim1,dim2); }
      set { impl.SetAt(dim0, dim1, dim2, value); }
     }
    }

    // implementation object
    internal class Matrix3Impl
    {
     private double[] data;
     uint dsize0, dsize1, dsize2; // dimension sizes
     // .. Resize() 
     public double GetAt(uint dim0, uint dim1, uint dim2) {
      // .. check bounds
      return data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ];
     }
     public void SetAt(uint dim0, uint dim1, uint dim2, double value) {
      // .. check bounds
      data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ] = value;
     }
    }

    private Matrix3Impl impl;

    public Matrix3Elem Elem(uint dim0, uint dim1, uint dim2){
     return new Matrix2Elem(dim0, dim1, dim2);
    }
    // .. Resize
    // .. GetLength0(), GetLength1(), GetLength1()
}

And then using this type to both read and write -- 'foo[1,2,3]' is now written as 'foo.Elem(1,2,3).Value', in both reading values and writing values, on left side of assignment and value expressions.

void normalize(Matrix3 m){

    double s = 0;
    for (i = 0; i < input.GetLength0; i++)     
     for (j = 0; j < input.GetLength(1); j++)       
      for (k = 0; k < input.GetLength(2); k++)
    {
     s += m.Elem(i,j,k).Value;
    }
    for (i = 0; i < input.GetLength0; i++)     
     for (j = 0; j < input.GetLength(1); j++)       
      for (k = 0; k < input.GetLength(2); k++)
    {
     m.Elem(i,j,k).Value /= s;
    }
}

Again, added development costs, but shares data, removing copying overhead and copying related developtment costs. It's a tradeoff.

Aaron