Best/Fastest Way To Change/Access Elements of a Matrix - c#

I'm quite new to C# and I'm having difficult with our arrays, arrays of arrays, jagged arrays, matrixes and stuff. It's quite different from the C++ , since I can't get a reference (unless I use unsafe code) to a row of a matrix, using pointers and stuff.
Anyway, here's the problem: I have a struct/class called "Image" that cointains 1024 columns and 768 lines. For each line/column theres a 'pixel' struct/class that contains 3 bytes. I'd like to get/set pixels in random places of the matrix as fast as possible.
Let's pretend I have a matrix with 25 pixels. That is 5 rows and 5 columns, something like this:
A B C D E
F G H I J
K L M N O
P Q R S T
U V X W Y
And I need to compare M to H and R. Then M to L and N. Then I need to 'sum' G+H+I+L+M+N+Q+R+S.
How can I do that?
Possibilities:
1) Create something like pixel[5][5] (that's a jagged array, right?), which will be slow whenever I try to compare elements on different columns, right?
2) Create something like pixel[25] , which won't be as easy to code/ready because I'll need to do some (simple) math each and everything I want to access a element
3) Create something like pixe[5,5] (that's a multi-dimensional array, right?)... But I don't know how that will be translated to actual memory... If it's going to be a single block of memory, like the pixe[25], or what...
Since I intend to do this operations ('random' sums/comparison of elements that are in different rows/columns) tens of thousands of times per image. And I have 1000+ imagens. Code optimization is a must... Sadly I'm not sure which structure / classe I should use.
TL;DR: Whats the FASTEST and whats the EASIEST (coding wise) way of getting/setting elements in random positions of a (fixed size) matrix?
edit: I do not want to compare C++ to C#. I'm just saying I AM NEW TO C# and I'd like to find the best way to accomplish this, using C#. Please don't tell me to go back to C++.

I have worked on pixel based image processing in C#. I found that pattern #2 in your list is fastest. For speed, you must forget about accessing pixels via some kind of nice abstract interface. The pixel processing routines must explicitly deal with the width and height of the image. This makes for crappy code in general, but unless Microsoft improves the C# compiler, we are stuck with this approach.
If your for loops start at index 0 of an array, and end at array.Length - 1, the compiler will optimize out the array index bounds testing. This is nice, but typically you have to use more than one pixel at a time while processing.

I just finished testing, heres the result:
SD Array Test1: 00:00:00.9388379
SD Array Test2: 00:00:00.4117926
MD Array Test1: 00:00:01.4977765
MD Array Test2: 00:00:00.8950093
Jagged Array Test1: 00:00:03.6850013
Jagged Array Test2: 00:00:00.5036041
Conclusion: Single dimensional array is the way to go... Sadly we lose in readability.
And heres the code:
int[] myArray = new int[10000 * 10000];
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
myArray[(i*10000)+j] = i+j;
}
}
Stopwatch sw = new Stopwatch();
int sum = 0;
sw.Start();
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
sum += myArray[(j * 10000) + i];
}
}
sw.Stop();
Console.WriteLine("SD Array Test1: " + sw.Elapsed.ToString());
sum=0;
sw.Restart();
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
sum += myArray[(i * 10000) + j];
}
}
sw.Stop();
Console.WriteLine("SD Array Test2: " + sw.Elapsed.ToString());
myArray = null;
int[,] MDA = new int[10000, 10000];
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
MDA[i, j] = i + j;
}
}
sum = 0;
sw.Restart();
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
sum += MDA[j, i];
}
}
sw.Stop();
Console.WriteLine("MD Array Test1: " + sw.Elapsed.ToString());
sw.Restart();
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
sum += MDA[i, j];
}
}
sw.Stop();
Console.WriteLine("MD Array Test2: " + sw.Elapsed.ToString());
MDA = null;
int[][] JA = new int[10000][];
for (int i = 0; i < 10000; i++)
{
JA[i] = new int[10000];
}
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
JA[i][j] = i + j;
}
}
sum = 0;
sw.Restart();
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
sum += JA[j][i];
}
}
sw.Stop();
Console.WriteLine("Jagged Array Test1: " + sw.Elapsed.ToString());
sw.Restart();
for (int i = 0; i < 10000; i++)
{
for (int j = 0; j < 10000; j++)
{
sum += JA[i][j];
}
}
sw.Stop();
Console.WriteLine("Jagged Array Test2: " + sw.Elapsed.ToString());
MDA = null;
Console.ReadKey();

Related

Why writing by column is slow in two dimensional array in C#

I have two-dimensional array when I am adding values by column it write very slowly (less than 300x):
class Program
{
static void Main(string[] args)
{
TwoDimArrayPerfomrance.GetByColumns();
TwoDimArrayPerfomrance.GetByRows();
}
}
class TwoDimArrayPerfomrance
{
public static void GetByRows()
{
int maxLength = 20000;
int[,] a = new int[maxLength, maxLength];
DateTime dt = DateTime.Now;
Console.WriteLine("The current time is: " + dt.ToString());
//fill value
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
a[i, j] = i + j;
}
}
DateTime end = DateTime.Now;
Console.WriteLine("Total: " + end.Subtract(dt).TotalSeconds);
}
public static void GetByColumns()
{
int maxLength = 20000;
int[,] a = new int[maxLength, maxLength];
DateTime dt = DateTime.Now;
Console.WriteLine("The current time is: " + dt.ToString());
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
a[j, i] = j + i;
}
}
DateTime end = DateTime.Now;
Console.WriteLine("Total: " + end.Subtract(dt).TotalSeconds);
}
}
The Column vice taking around 4.2 seconds
while Row wise taking 1.53
It is the "cache proximity" problem mentioned in the first comment. There are memory caches that any data must go through to be accessed by the CPU. Those caches store blocks of memory, so if you are first accessing memory N and then memory N+1 then cache is not changed. But if you first access memory N and then memory N+M (where M is big enough) then new memory block must be added to the cache. When you add new block to the cache some existing block must be removed. If you then have to access this removed block then you have inefficiency in the code.
I concur fully with what #Dialecticus wrote... I'll just add that there are bad ways to write a microbenchark, and there are worse ways. There are many things to do when microbenchmarking. Remembering to run in Release mode without the debugger attached, remembering that there is a GC, and that it is better if it runs when you want it to run, and not casually when you are benchmarking, remembering that sometimes the code is compiled only after it is executed at least once, so at least a round of full warmup is a good idea... and so on... There is even a full library about benchmarking (https://benchmarkdotnet.org/articles/overview.html) that is used by Microscot .NET Core teams to check that there are no speed regressions on the code they write.
class Program
{
static void Main(string[] args)
{
if (Debugger.IsAttached)
{
Console.WriteLine("Warning, debugger attached!");
}
#if DEBUG
Console.WriteLine("Warning, Debug version!");
#endif
Console.WriteLine($"Running at {(Environment.Is64BitProcess ? 64 : 32)}bits");
Console.WriteLine(RuntimeInformation.FrameworkDescription);
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Console.WriteLine();
const int MaxLength = 10000;
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Round {i + 1}:");
TwoDimArrayPerfomrance.GetByRows(MaxLength);
GC.Collect();
GC.WaitForPendingFinalizers();
TwoDimArrayPerfomrance.GetByColumns(MaxLength);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine();
}
}
}
class TwoDimArrayPerfomrance
{
public static void GetByRows(int maxLength)
{
int[,] a = new int[maxLength, maxLength];
Stopwatch sw = Stopwatch.StartNew();
//fill value
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
a[i, j] = i + j;
}
}
sw.Stop();
Console.WriteLine($"By Rows, size {maxLength} * {maxLength}, {sw.ElapsedMilliseconds / 1000.0:0.00} seconds");
// So that the assignment isn't optimized out, we do some fake operation on the array
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
if (a[i, j] == int.MaxValue)
{
throw new Exception();
}
}
}
}
public static void GetByColumns(int maxLength)
{
int[,] a = new int[maxLength, maxLength];
Stopwatch sw = Stopwatch.StartNew();
//fill value
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
a[j, i] = i + j;
}
}
sw.Stop();
Console.WriteLine($"By Columns, size {maxLength} * {maxLength}, {sw.ElapsedMilliseconds / 1000.0:0.00} seconds");
// So that the assignment isn't optimized out, we do some fake operation on the array
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
if (a[i, j] == int.MaxValue)
{
throw new Exception();
}
}
}
}
}
Ah... and multi-dimensional arrays of the type FooType[,] went the way of the dodo with .NET 3.5, when LINQ came out and it didn't support them. You should use jagged arrays FooType[][].
If you try to map your two dimensional array to a one dimensional one, it might be a bit easier to see what is going on.
The mapping gives
var a = int[maxLength * maxLength];
Now the lookup calculation is up to you.
for (int i = 0; i < maxLength; i++)
{
for (int j = 0; j < maxLength; j++)
{
//var rowBased = j + i * MaxLength;
var colBased = i + j * MaxLength;
//a[rowBased] = i + j;
a[colBased] = i + j;
}
}
So observe the following
On column based lookup the number of multiplications is 20.000 * 20.000 multiplications, because j changes for every loop
On row based lookup the i * MaxLength is compiler optimised and only happens 20.000 times.
Now that a is a one dimensional array it's also easier to see how the memory is being accessed. On row based index the memory is access sequentially, where as column based access is almost random and depending on the size of the array the overhead will vary as you have seen it.
Looking a bit on what BenchmarkDotNet produces
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET Core SDK=5.0.101
Method
MaxLength
Mean
Error
StdDev
GetByRows
100
23.60 us
0.081 us
0.076 us
GetByColumns
100
23.74 us
0.357 us
0.334 us
GetByRows
1000
2,333.20 us
13.150 us
12.301 us
GetByColumns
1000
2,784.43 us
10.027 us
8.889 us
GetByRows
10000
238,599.37 us
1,592.838 us
1,412.009 us
GetByColumns
10000
516,771.56 us
4,272.849 us
3,787.770 us
GetByRows
50000
5,903,087.26 us
13,822.525 us
12,253.308 us
GetByColumns
50000
19,623,369.45 us
92,325.407 us
86,361.243 us
You will see that while MaxLength is reasonable small, the differences are almost negligible (100x100) and (1000x1000), because I expect that the CPU can keep the allocated two dimensional array in the fast access memory cache and the differences are only related to the number of multiplications.
When the matrix becomes larger, then the CPU can no longer keep all allocated memory in its internal cache and we will start to see cache-misses and fetching memory from the external memory storage instead, which is always going to be a lot slower.
That overhead just increases as the size of the matrix grows.

No. of rows 1 less than expected in a jagged array

While printing this jagged array I am getting the no of rows to be 1 less than expected. It should start with 0th index to (h-1)th index creating a total of h rows. What am I doing wrong?
h is the no. of rows.
int h = int.Parse(Console.ReadLine());
int[][] arr = new int[h][];
for(int i = 0; i < h; ++i)
{
arr[i] = new int[i+1];
}
for(int i = 0; i < h; i++)
{
Console.WriteLine();
for(int j = 0; j < i; j++)
{
Console.Write(arr[i][j] + " ");
}
}
That's because your inner for-loop has the condition j < i. If i is 0 in the first pass, the inner for-loop will not be passed.
Try it with
for(int j = 0; j < arr[i].Length; j++)
{
Console.Write(arr[i][j] + " ");
}
The arrays have a growing list of elements, starting with 1 so if you want to scan all the items:
for(int i = 0; i < h; i++)
{
Console.WriteLine();
for(int j = 0; j < (i + 1); j++)
{
Console.Write(arr[i][j] + " ");
}
}
Shouldn't it be:
new int[i];
Instead of:
new int[i+1]
Or is it h - 1? Just change that index of the array.
Or you need j < arr[i].Length

C#: Out of memory Exception

I have written a console application
Int64 sum = 0;
int T = Convert.ToInt32(Console.ReadLine());
Int64[] input = new Int64[T];
for (int i = 0; i < T; i++)
{
input[i] = Convert.ToInt32(Console.ReadLine());
}
for (int i = 0; i < T; i++)
{
int[,] Matrix = new int[input[i], input[i]];
sum = 0;
for (int j = 0; j < input[i]; j++)
{
for (int k = 0; k < input[i]; k++)
{
Matrix[j, k] = Math.Abs(j - k);
sum += Matrix[j, k];
}
}
Console.WriteLine(sum);
}
When I gave input as
2
1
999999
It gave Out of memory exception. Can you please help.
Look at what you are allocating:
input[] is allocated as 2 elements (16 bytes) - no worries
But then you enter values: 1 and 999999 and in the first iteration of the loop attempt to allocate
Matrix[1,1] = 4 bytes - again no worries,
but the second time round you try to allocate
Matrix[999999, 999999]
which is 4 * 10e12 bytes and certainly beyond the capacity of your computer even with swap space on the disk.
I suspect that this is not what you really want to allocate (you'd never be able to fill or manipulate that many elements anyway...)
If you are merely trying to do the calculations as per your original code, there is not need to allocate or use the array, as you only ever store one value and immediately use that value and then never again.
Int64 sum = 0;
int T = Convert.ToInt32(Console.ReadLine());
Int64[] input = new Int64[T];
for (int i = 0; i < T; i++)
input[i] = Convert.ToInt32(Console.ReadLine());
for (int i = 0; i < T; i++)
{
// int[,] Matrix = new int[input[i], input[i]];
sum = 0;
for (int j = 0; j < input[i]; j++)
for (int k = 0; k < input[i]; k++)
{
//Matrix[j, k] = Math.Abs(j - k);
//sum += Matrix[j, k];
sum += Math.Abs(j - k);
}
Console.WriteLine(sum);
}
But now beware - a trillion sums is going to take forever to calculate - it won't bomb out, but you might like to take a vacation, get married and have kids before you can expect a result.
Of course instead of doing the full squared set of calculations, you can calculate the sum thus:
for (int i = 0; i < T; i++)
{
sum = 0;
for (int j = 1, term = 0; j < input[i]; j++)
{
term += j;
sum += term * 2;
}
Console.WriteLine(sum);
}
So now the calculation is O(n) instead of O(n^2)
And if you need to know what the value in Matrix[x,y] would have been, you can calculate it by the simple expression Math.Abs(x - y) thus there is no need to store that value.

convert 3 dimensional array to a 2 dimensional array

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?
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.

filling multidimensional array with unique numbers in C#

I'm trying to write a code that will fill array with unique numbers.
I could write the code separately for 1, 2 and 3 dimensional arrays but number of for cycles grow to "infinity".
this is the code for 2D array:
static void fillArray(int[,] array)
{
Random rand = new Random();
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
array[i, j] = rand.Next(1, 100);
for (int k = 0; k < j; k++)
if (array[i, k] == array[i, j])
j--;
}
}
print_info(array);
}
Is it possible to do something like this for n-dimensional arrays?
My approach is to start with a 1-d array of unique numbers, which you can shuffle, and then slot into appropriate places in your real array.
Here is the main function:
private static void Initialize(Array array)
{
var rank = array.Rank;
var dimensionLengths = new List<int>();
var totalSize = 1;
int[] arrayIndices = new int[rank];
for (var dimension = 0; dimension < rank; dimension++)
{
var upperBound = array.GetLength(dimension);
dimensionLengths.Add(upperBound);
totalSize *= upperBound;
}
var singleArray = new int[totalSize];
for (int i = 0; i < totalSize; i++) singleArray[i] = i;
singleArray = Shuffle(singleArray);
for (var i = 0; i < singleArray.Length; i++)
{
var remainingIndex = i;
for (var dimension = array.Rank - 1; dimension >= 0; dimension--)
{
arrayIndices[dimension] = remainingIndex%dimensionLengths[dimension];
remainingIndex /= dimensionLengths[dimension];
}
// Now, set the appropriate cell in your real array:
array.SetValue(singleArray[i], arrayIndices);
}
}
The key in this example is the array.SetValue(value, params int[] indices) function. By building up the correct list of indices, you can use this function to set an arbitrary cell in your array.
Here is the Shuffle function:
private static int[] Shuffle(int[] singleArray)
{
var random = new Random();
for (int i = singleArray.Length; i > 1; i--)
{
// Pick random element to swap.
int j = random.Next(i); // 0 <= j <= i-1
// Swap.
int tmp = singleArray[j];
singleArray[j] = singleArray[i - 1];
singleArray[i - 1] = tmp;
}
return singleArray;
}
And finally a demonstration of it in use:
var array1 = new int[2,3,5];
Initialize(array1);
var array2 = new int[2,2,3,4];
Initialize(array2);
My strategy assigns sequential numbers to the original 1-d array to ensure uniqueness, but you can adopt a different strategy for this as you see fit.
You can use Rank property to get the total number of dimentions in your array
To insert use SetValue method
In the first two for loops you are analysing the array properly (i and j go from the start to the end of the corresponding dimension). The problem comes in the most internal part where you introduce a "correction" which actually provokes an endless loop for j.
First iteration:
- First loop: i = 0;
- Second loop: j = 0;
- Third loop: j = -1
Second iteration
- First loop: i = 0;
- Second loop: j = 0;
- Third loop: j = -1
. etc., etc.
(I start my analysis in the moment when the internal loop is used for the first time. Also bear in mind that the exact behaviour cannot be predicted as far as random numbers are involved. But the idea is that you are making the j counter back over and over by following an arbitrary rule).
What you want to accomplish exactly? What is this last correction (the one provoking the endless loop) meant to do?
If the only thing you intend to do is checking the previously stored values, you have to rely on a different variable (j2, for example) which will not affect any of the loops above:
int j2 = j;
for (int k = 0; k < j2; k++)
if (array[i, k] == array[i, j2])
j2--;

Categories