Fastest way to iterate 2d array in C# - c#

I have a class that wraps 2d float[][] array into 1d float[] array:
class Wrapper
{
int CountX;
int CountY;
float[] Values;
}
for example something like this
{1, 2, 3, 4}
{5, 6, 7, 8}
would be wrapped into
var wr = new Wrapper
{
Values = new float[8]{1,2,3,4,5,6,7,8},
CountX = 4,
CountY = 2
};
And I want to find the fastest way to get its row or column.
Currently I'm using these methods
class Wrapper
{
int CountX;
int CountY;
float[] Values;
public float[] GetRow(int row)
{
var res = new float[CountX];
for(int i = 0; i < CountX; i++)
{
res[i] = Values[CountX*row + i];
}
return res;
}
public float[] GetColumn(int column)
{
var res = new float[CountY];
for (int i = 0; i < CountY; i++)
{
res[i] = Values[column + CountX*i];
}
return res;
}
}
With this usage:
var wr = new Wrapper
{
Values = new float[8]{1,2,3,4,5,6,7,8},
CountX = 4,
CountY = 2
};
//{1, 2, 3, 4}
//{5, 6, 7, 8}
wr.GetRow(1) // 5 6 7 8
wr.GetColumn(3) // 4 8
And what I am trying to accomplish is increasing performance. I'm pretty sure there is a way to do it faster using unsafe code, but I don't really know how to use pointers in C#

The fastest way to do this would usually be to not allocate or copy anything. Switching to unsafe is not going to help much with the real cost here, which is the allocation and copy; at best you can avoid some bounds checks.
Assuming you keep a 1D backing array, on the minor axis (by which I mean: contiguous data), it should be trivially possible to get a Span<float> of the relevant chunk of data: nothing more than that i.e. new ReadOnlySpan<float>(Values, CountX*row, CountX); on the major axis, maybe return something that is simply a flyweight readonly struct with an indexer into the data?
However, honestly I wonder if you should just use a float[,] and use regular x/y indexing.
Example; note that choosing which dimension to use as the inner one is important, as the direct Span<T> access will be faster than the indirect Row<T> access:
using System.Runtime.InteropServices;
var obj = new ArrayWrapper<float>(2, 3);
obj[1, 2] = 4;
Write(obj);
var row = obj.GetColumn(1);
for (int i = 0; i < row.Length; i++)
row[i] = i;
Write(obj);
var col = obj.GetRow(1);
for (int i = 0; i < col.Length; i++)
col[i] = i + 10;
Write(obj);
col = obj.GetRow(2);
for (int i = 0; i < col.Length; i++)
col[i] = i + 20;
Write(obj);
static void Write(ArrayWrapper<float> arr)
{
for (int y = 0; y < arr.Height; y++)
{
for (int x = 0; x < arr.Width; x++)
{
Console.Write(arr[x, y]);
Console.Write('\t');
}
Console.WriteLine();
}
Console.WriteLine();
}
readonly struct ArrayWrapper<T>
{
private readonly T[,] _array;
public int Width => _array.GetLength(0);
public int Height => _array.GetLength(1);
public ArrayWrapper(int width, int height) => _array = new T[width, height];
public ref T this[int x, int y] => ref _array[x, y];
public readonly Span<T> GetColumn(int x)
=> MemoryMarshal.CreateSpan(ref _array[x, 0], Height);
public readonly Row<T> GetRow(int y) => new(_array, y);
}
readonly struct Row<T>
{
private readonly T[,] _array;
private readonly int _y;
public Row(T[,] array, int y)
{
_array = array;
_y = y;
}
public bool IsEmpty => Length == 0;
public int Length => _array.GetLength(0); // Width
public ref T this[int x] => ref _array[x, _y];
}

For rows you should return a ReadonlySpan<T> since that would be a zero-copy operation. This is assuming your storage is row-major.
For columns you will need to copy elements. It can be useful to take the destination as a parameter. That way it might be possible to avoid repeated allocations. You can also update the index directly in the loop, I would expect that to help a little bit, but I have not done any profiling.
public void CopyColumn(int column, Span<float> res)
{
for (int i = column; i < Values.Length; i += CountX)
{
res[i] = Values[i];
}
}
If you want an array as a result you can add a helper method:
public float[] GetColumn(int column){
var res = new float[CountY];
CopyColumn(column, res);
return res;
}

Related

Passing in a portion of a multidimensional array as argument in C#

Need to convert some c++ code into c#. In this case I need to pass in the second dimension of a multidimensional array into the function dot(...).
This is the original c++ declaration /* and definition */, followed by the global static const array.
double dot( const int* g, const double x, const double y ) /*{ return g[0]*x + g[1]*y; }*/;
static const int grad3[ 12 ][ 3 ] = {...};
In c# it could be this:
public class TestClass
{
float dot( ref int[] g, float x, float y ) { return g[0] * x + g[1] * y; }
public static readonly int[,] grad3 = new int[12, 3]{...};
}
Here is an example to see how it should be accessed:
public class TestClass
{
...
void test()
{
int gi0 = 0;
double d1 = dot( grad3[ gi0 ], x0, y0, z0 );
}
}
You can pass a portion of your array without using ref.
My suggested example is not a multi-dimensional array but is a jagged array which is an array of arrays.
public class TestClass
{
public static float Dot(int[] g, float x, float y)
{
return g[0] * x + g[1] * y;
}
// Note that the second index is empty
public static readonly int[][] grad3 = new int[12][] {...};
}
You can pass your multi dimensional array (jagged array) like this:
var value = TestClass.grad3;
TestClass.Dot(value[0], x0, y0, z0);
Also, initialization syntax is like this:
public static readonly int[][] TwoDimArray = new int[3][]
{
new[] {1, 2, 3},
new[] {4, 5, 6},
new[] {8, 9, 10}
};
Comparison between jagged arrays and multi-dimensional arrays:
public static double Sum(double[,] d) {
double sum = 0;
int l1 = d.GetLength(0);
int l2 = d.GetLength(1);
for (int i = 0; i < l1; ++i)
for (int j = 0; j < l2; ++j)
sum += d[i, j];
return sum;
}
// This is intuitive and clear for me
public static double Sum(double[][] d) {
double sum = 0;
for (int i = 0; i < d.Length; ++i)
for (int j = 0; j < d[i].Length; ++j)
sum += d[i][j];
return sum;
}

How to copy C# 3D jagged array

I need to (initially) copy a C# 3D jagged array, foos, to another 3D array (and eventually add x, y, z dimensions). I thought I could use the same syntax/logic to copy foos as was used to build foos as I've done below (where row = 2, col = 3, z = 4):
private static void copyFooArray(Foo[][][] foos, ref Foo[][][] newArray)
{
for (int row = 0; row < foos.Length; row++)
{
newArray[row] = new Foo[foos[row].Length][];
for (int col = 0; col < foos[row].Length; col++)
{
newArray[row][col] = new Foo[foos[row][col].Length];
for (int z= 0; z< foos[row][col].Length; z++)
{
newArray[row][col][z] = new Foo();
newArray[row][col][z].member = foos[row][col][z].member;
}
}
}
Console.Read();
}
but I'm getting Index was outside the bounds of the array. on this line:
newArray[row] = new Foo[foos[row].Length][];
Why?
Foo Class:
public class Foo
{
public string member;
}
Thanks.
It doesn't look like your referenced array is being initialized properly. In order to set the value, your newArray must be initialized as the same size of your original.
For this to work you'd need to pass in something like this:
Foo[][][] firstFoo = new Foo[10][][];
Foo[][][] fooToCopy = new Foo[firstFoo.Length][][];
copyFooArray(firstFoo, ref fooToCopy);
Also, the ref keyword is unnecessary since arrays are passed by reference in c# anyways.
In addition to the fix presented in the accepted answer, here's a faster way to do that:
public static int[][][] Copy(int[][][] source)
{
int[][][] dest = new int[source.Length][][];
for (int x = 0; x < source.Length; x++)
{
int[][] s = new int[source[x].Length][];
for (int y = 0; y < source[x].Length; y++)
{
int[] n = new int[source[x][y].Length];
int length = source[x][y].Length * sizeof(int);
Buffer.BlockCopy(source[x][y], 0, n, 0, length);
s[y] = n;
}
dest[x] = s;
}
return dest;
}

Getting from a dictionary with int[] arrays as value [duplicate]

This question already has answers here:
An integer array as a key for Dictionary
(3 answers)
Closed 7 years ago.
I have the following piece of code
public Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x=0; x <= 10; x++) {
for (int y=0; y <= 10; y++) {
int[] cords = new int[]{x,y};
worldMap.Add(cords, "empty");
}
}
How do I get values from this dictionary?
Create an IEqualityComparer and define your dictionary using that. you can find sample code in below SO question:
An integer array as a key for Dictionary
You can iterate over the entire dictionary.
foreach (var map in worldMap)
{
Console.WriteLine(String.Join(", ", map.Key) + " - " + map.Value);
}
Or look it up by reference
Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x = 0; x <= 10; x++)
{
for (int y = 0; y <= 10; y++)
{
int[] cords = new int[] { x, y };
worldMap.Add(cords, "empty");
Console.WriteLine(worldMap[cords]);
}
}
When you use an array or class, the dictionary uses the reference to the object in the background. This makes some things impossible. For instance if you new another list with the same values, the dictionary will throw an exception saying the key is not found.
Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x = 0; x <= 10; x++)
{
for (int y = 0; y <= 10; y++)
{
int[] cords = new int[] { x, y };
worldMap.Add(cords, "empty");
//This will cause an exception
Console.WriteLine(worldMap[new int[] { x, y }]);
}
}
Consider using System.Drawing.Point instead of an int array. In general, Dictionaries will only match if it's the exact same object that went into it. Even another object with the same value won't work.
But some data types such as Point are different, and implement all the hashing and equality check features necessary to be used a keys in a dictionary.
After you populate the worldMap you can get your keys like this (You have to add using System.Linq):
List<int[]> coords = worldMap.Keys.ToList();
Then you can get any value from the coords List like this (Make sure you use a value within the bounds of your List):
worldMap[coords[0]]
Code Sample:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
Dictionary<int[], string> worldMap = new Dictionary<int[], string>();
for (int x = 0; x <= 10; x++)
{
for (int y = 0; y <= 10; y++)
{
int[] cords = new int[] { x, y };
worldMap.Add(cords, String.Format("empty{0}_{1}", x, y));
}
}
// This should have 121 coordinates from (0, 0) to (10, 10)
List<int[]> coords = worldMap.Keys.ToList();
Console.WriteLine(worldMap[coords[21]]);
}
}
Demo
The use of an int[] for your word map coordinates is a poor choice for a number of reasons, not least of which I could send through zero, one, three, or more values when it seems like you specifically are using only two.
You would be far better off creating you own read-only class to hold your coordinates instead.
Try this class:
public sealed class WorldMapCoord : IEquatable<WorldMapCoord>
{
private readonly int _X;
private readonly int _Y;
public int X { get { return _X; } }
public int Y { get { return _Y; } }
public WorldMapCoord(int X, int Y)
{
_X = X;
_Y = Y;
}
public override bool Equals(object obj)
{
if (obj is WorldMapCoord)
return Equals((WorldMapCoord)obj);
return false;
}
public bool Equals(WorldMapCoord obj)
{
if (obj == null) return false;
if (!EqualityComparer<int>.Default.Equals(_X, obj._X)) return false;
if (!EqualityComparer<int>.Default.Equals(_Y, obj._Y)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int>.Default.GetHashCode(_X);
hash ^= EqualityComparer<int>.Default.GetHashCode(_Y);
return hash;
}
public override string ToString()
{
return String.Format("{{ X = {0}, Y = {1} }}", _X, _Y);
}
}
Now your code would look like this:
public Dictionary<WorldMapCoord, string> worldMap = new Dictionary<WorldMapCoord, string>();
for (int x=0; x <= 10; x++)
{
for (int y=0; y <= 10; y++)
{
worldMap.Add(new WorldMapCoord(x, y), "empty");
}
}
Since the class is read-only, and it implements GetHashCode & Equals you can use this code to retrieve values from the dictionary:
for (int x=0; x <= 10; x++)
{
for (int y=0; y <= 10; y++)
{
string value = worldMap[new WorldMapCoord(x, y)];
}
}

Combining two single dimensional arrays in a 2D array in c#

I am having two 1D arrays. I want to convert these 2 arrays as single 2D array.
My code is:
public Static void Main()
{
int[] arrayRow;
int[] arrayCol;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
int[,] myArray = new int[row,column];
myArray[i,j] = arrayRow[i]; // not possible -- your suggestions
}
}
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
Console.Write(myArray[i,j]);
}
}
}
I need to save arrayRow[] and arrayCol[] in myArray[,].
For example,
if we have arrayRow[]={1,2,3} and arrayCol[]={4,5,6} then myArray[,]={(1,4),(2,5),(3,6)}
Note: arrayRow and arrayCol may have different lengths. In such cases the element that have no pair should be stored in the new single dimensional array result[].
Your arrayRow[] and arrayCol[] will be just two lines of a two-dimensional array (if you didn't mean jagged one).
So the code to unite two arrays into one is just:
public static T[,] Union<T>(T[] first, T[] second) //where T : struct
{
T[,] result = new T[2, Math.Max(first.Length, second.Length)];
int firstArrayLength = first.Length * Marshal.SizeOf(typeof(T));
Buffer.BlockCopy(first, 0, result, 0, firstArrayLength);
Buffer.BlockCopy(second, 0, result, firstArrayLength, second.Length * Marshal.SizeOf(typeof(T)));
return result;
}
As it have been mentinoned, BlockCopy is cooler than for cycle.
If you do mean that you need a jagged array (like int[][]), that the solutiona will be way more simplier:
public static T[][] UnionJagged<T>(T[] first, T[] second)
{
return new T[2][] { first, second };
}
Which transforms into even simplier if we add multiple-array-as-parameters functionality:
public static T[][] UnionJagged<T>(params T[][] arrays)
{
return arrays;
}
static void Main()
{
int[] a = new int[] { 10, 2, 3 };
int[] b = new int[] { -1, 2, -3 };
int[] c = new int[] { 1, -2, 3 };
int[][] jaggedThing = UnionJagged(a, b, c);
}
Didn't tryed this, and I'm just guessing what you want to acomplish, but here it is:
int[] arrayRow;
int[] arrayCol;
int[,] myArray = new int[Math.Max(arrayRow.Length, arrayCol.Length), 2];
for (int i = 0; i < arrayRow.Length; i++)
myArray[i, 0] = arrayRow[i];
for (int i = 0; i < arrayCol.Length; i++)
myArray[i, 1] = arrayCol[i];
More performant / another way:
public static void ConvertFlatArrayToMatrix(int[] array, int[,] matrix, int dimension) {
for(int i = 0; i < array.Length; i++) {
int r = Mathf.FloorToInt(i / dimension);
int c = i % dimension;
matrix[c,r] = array[i];
}
}
This will just push the result into a 2d array you pass in.
Keep in mind, I'm not checking length or protecting against anything here, this is just the concept and the bare minimum to get the job done.

Generic initialization of multidimensional array

I have a multidimensional array, which I want to initialize in a simple and fast way:
double[,,] arr = new double[4,5,6];
// doesn't work by design
foreach(double d in arr)
d = ... ; // my initialization value
This obviously doesn't works. But I would like to have a generic function for setting all array values to a choosen default. With own classes, I could write a special constructor, but with value types I've no real idea. With C++, I could access all items in a linear way with one for loop, but in C# I think I've to use as much for loops as I have dimensions. I've no better solution at the moment ( or I'm using unsafe code and pointer arithmetics, which would probably work.).
Is there a more elegant way for doing this ?
Not quite sure if it's what you want, but the following extension method will allow you to initialise every value in an array, regardless of the number of dimensions.
public static class ArrayExtensions
{
public static void Set<T>(this Array array, T defaultValue)
{
int[] indicies = new int[array.Rank];
SetDimension<T>(array, indicies, 0, defaultValue);
}
private static void SetDimension<T>(Array array, int[] indicies, int dimension, T defaultValue)
{
for (int i = 0; i <= array.GetUpperBound(dimension); i++)
{
indicies[dimension] = i;
if (dimension < array.Rank - 1)
SetDimension<T>(array, indicies, dimension + 1, defaultValue);
else
array.SetValue(defaultValue, indicies);
}
}
}
Use like so:
int[, ,] test1 = new int[3, 4, 5];
test1.Set(1);
int[,] test2 = new int[3, 4];
test2.Set(1);
int[] test3 = new int[3];
test3.Set(1);
I would strongly recommend using a 1D array, and map the values sequentially. You will need to convert from indeces i, j, k, ... into the proper array index, which is done with the Que() function below, part of a generic array class SeqArray<T>.
// Test code first
class Program
{
static void Main(string[] args)
{
/* 3 pages, of a 4x2 matrix
*
* |16 17|
* | 8 9|19|
* | 0 1|11|21|
* | 2 3|13|23|
* | 4 5|15|
* | 6 7|
*
* shown above are the sequential indeces for a rank 3 array
*/
SeqArray<double> arr = new SeqArray<double>(3, 4, 2);
// Initialize values to squential index "num"
int num = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 2; k++)
{
arr[i, j, k] = num++;
}
}
}
// Check that the array values correspond to the index sequence
num = 0;
for (int i = 0; i < 3 * 4 * 2; i++)
{
Trace.Assert(arr.InnerArray[i] == num++);
}
// Initialize with value=π
arr = new SeqArray<double>(Math.PI, 4, 5, 6);
}
}
public class SeqArray<T>
{
T[] values;
int[] lengths;
public SeqArray(params int[] lengths)
{
this.lengths = lengths;
int N = 1;
for (int i = 0; i < lengths.Length; i++)
{
N *= lengths[i];
}
values = new T[N];
}
public SeqArray(T value, params int[] lengths) : this(lengths)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = value;
}
}
public int[] Lengths { get { return lengths; } }
public int Size { get { return values.Length; } }
internal T[] InnerArray { get { return values; } }
public int Que(params int[] indeces)
{
// Check if indeces are omited like arr[4] instead of arr[4,0,0]
if (indeces.Length < lengths.Length)
{
// Make a new index array padded with zeros
int[] temp = new int[lengths.Length];
indeces.CopyTo(temp, 0);
indeces = temp;
}
// Count the elements for indeces
int k = 0;
for (int i = 0; i < indeces.Length; i++)
{
k = lengths[i] * k + indeces[i];
}
return k;
}
public T this[params int[] indeces]
{
get { return values[Que(indeces)]; }
set { values[Que(indeces)] = value; }
}
}
Here is a non-recursive version alternative to that posted by Andy Holt above:
public static void SetAll<T>(this Array array, T value)
{
var sizes = new int[array.Rank];
sizes[array.Rank - 1] = 1;
for (var d = array.Rank - 2; d >= 0; d--)
{
sizes[d] = array.GetLength(d + 1)*sizes[d + 1];
}
for (var i = 0; i < array.Length; i++)
{
var remainder = i;
var index = new int[array.Rank];
for (var d = 0; d < array.Rank && remainder > 0; d++)
{
index[d] = remainder / sizes[d];
remainder -= index[d]*sizes[d];
}
array.SetValue(value, index);
}
}

Categories