In C, is there a difference in time and space between an m×n 2-dimensional array vs a 1-dimensional array of length m×n (for large values of m and n)? Will accessing elements be faster with a 1-dimensional array?
In C, 2-dimensional arrays are just a neat indexing scheme for 1-dimensional arrays. Just like with a 1D array, 2D arrays allocate a single block of contiguous memory, and the A[row][col] notation is similar to saying A[row*NCOLS+col].
Usually if you were to implement your own multidimensional arrays using single dimensional arrays, you'd write an indexing function:
int getIndex(int row, int col) { return row*NCOLS+col; }
Assuming your compiler inlines this function, the performance here would be precisely the same as if you used the built in 'indexing function' of 2D arrays.
To illustrate:
#define NROWS 10
#define NCOLS 20
This:
int main(int argc, char *argv[]) {
int myArr[NROWS*NCOLS];
for (int i=0; i<NROWS; ++i) {
for (int j=0; j<NCOLS; ++j) {
myArr[getIndex(i,j)] = i+j;
}
}
return 0;
}
Should perform the same as this:
int main(int argc, char *argv[]) {
int myArr[NROWS][NCOLS];
for (int i=0; i<NROWS; ++i) {
for (int j=0; j<NCOLS; ++j) {
myArr[i][j] = i+j;
}
}
return 0;
}
Though as AraK pointed out, if you are jumping around rows a lot, and the rows are very large, you could hit a lot of page faults... in that case the custom indexing function (with rows and cols switched around) could help, but so could simply changing which of the dimensions in a 2-dimensional array you treat as the rows and which you treat as the columns.
Actually, if you use the so-called two-dimensional array in C, the compiler will do the mapping into one-dimensional array for you. If you use one-dimensional array and you like to treat it as a two-dimensional one, then you have to write the mapping yourself.
The only thing that you have to take care of is you should access the array row-wise, because the C compiler will store your two-dimensional array row after row. If you access a 'big' two-dimensional array column-wise then page-faults are likely to happen. Even if you are programming in language that supports only one-dimensional arrays, you could easily write the mapping into any number of dimensions.
Take a look at this Wikipedia article if you want to do the mapping row-wise. Your mapping could be column-wise, like FORTRAN matrices for example.
Robert is correct. Indexing expressions are compiled to pointer arithmetic expressions and so there's no difference.
What can have an impact is access order however, and so you may want to implement things yourself so you can control the access order. For example column first vs row first forms.
On modern processors, accessing large arrays at various strides can have unexpected performance differences. Sequential access is always fastest, and other strides can be up to 30 times slower due to cache interactions. Multi dimensional arrays where the inner dimensions are a power of two often have poor performance because of they way they interact with the cache associativity. To understand these issues there's no real substitute for doing measurement.
I don't think there's any difference. Internally, c treats a two dimensional array like several one-dimensional arrays in sequence.
However, as with all things performance, your mileage may vary. There may be some kind of subtle pointer arithmetic difference. Run timed tests on both scenarios. Whichever one runs faster wins.
As said by other, the difference really is how you access your items: what matters if how your items are layout in the memory, which is linear, at least on common architectures. So all you have really is 1d array, the 2d, etc... is "just" a convenience, and a reasonable compiler should optimize the indexing - but in practice, once you have more than a few variables, compilers often fail on arch like x86 because of the register starvation.
Now, it depends on your application, but I think you should think with a 1d layout by default, especially, if you need to handle multiple dimensions. The first problem with multi-dimensional arrays in C is that you cannot dynamically allocate them - if you allocate on a per-row basis, you will have awful performances because you don't have a contiguous piece of memory. See the FFTW doc for details about this.
Note that you can always describe your single piece of memory with convenient array indexing on top of it (you allocate one big nxm memory block, and then create an array of n pointer to each row).
I'm only guessing, but I would say that a 1d array is faster than a 2d array. However, it wouldn't be measurably faster. Kind of like $1,000,000.01 is more than $1,000,000.
I'd use whatever is easier to code with.
Related
I'm currently writing a project in C, and I need to be able to fill a 2D array with information already stored in another 2D array. In a separate C file, I have this array:
int levelOne[][4] =
{{5,88,128,0},
{153,65,0,0},
{0,144,160,20}}; //First Array
int levelTwo[][4] =
{{5,88,128,0},
{153,65,0,0},
{0,144,160,20}}; //Second Array
And in my main file, I have this variable which I'd like to fill with the information from both of these arrays at different points in my code. (This isn't exactly what I'm doing, but it's the general gist):
#include "arrayFile.c"
void main()
{
int arrayContainer[][4] = levelOne;
while (true)
{
func(arrayContainer);
if(foo)
{
arrayContainer = levelTwo;//Switches to the other array if the conditional is met.
}
}
}
I know this method doesn't work - you can't overwrite items in arrays after they're instantiated. But is there any way to do something like this? I know I'll most likely need to use pointers to do this instead of completely overwriting the array, however there's not a lot of information on the internet about pointers with multidimensional arrays. In this situation, what's best practice?
Also, I don't know exactly how many arrays of 4 there will be, so I wouldn't be able to use a standard 3D array and just switch between indexes, unless there's a way to make a 3D jagged array that I don't know about.
Given the definitions you show, such as they are, all you need is memcpy(arrayContainer, levelTwo, sizeof LevelTwo);.
You should ensure that arrayContainer has sufficient memory to contain the copied data and that LevelTwo, since it is used as the operand of sizeof, is a designator for the actual array, not a pointer. If it is not, replace sizeof LevelTwo with the size of the array.
If you do not need the actual memory filled with data but simply need a way to refer to the contents of the different arrays, make arrayContainer a pointer instead of an array, as with int (*arrayContainer)[4];. Then you can use arrayContainer = levelOne; or arrayContainer = levelTwo; to change which data it points to.
Also, I don't know exactly how many arrays of 4 there will be, so I wouldn't be able to use a standard 3D array and just switch between indexes, unless there's a way to make a 3D jagged array that I don't know about.
It is entirely possible to have a pointer to dynamically allocated memory which is filled with pointers to arrays of four int, and those pointers can be changed at will.
Just stumbled accross this recent question:
How can I have a dynamically allocated 2D array in C?
I just wondered: When I create a 2D array with a simple malloc and manage the 2D-like access myself like so:
int row=100;
int col=100;
char* buffer = malloc(sizeof(char)*row*col);
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
buffer[i*col+j]=128;
}
}
would this be (significantly) faster then when creating a 'conventional' 2D Array because in the former I achieve buffer optimization through sequential access? Or am I thinking wrong?
int row=100;
int col=100;
char buffer[row][col];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
buffer[i][j]=128;
}
}
Thanks for your explanation.
Leaving the (small) overhead for dynamic memory allocation away, there is no difference if you access a particular element in a memory area by [row][column] or * (row * rowsize + column). It's basically just a difference in notation.
So your question is more like "is it better to have arrays defined "row first" than "column first?".
And the answer is: only you will know since you are the one to define the access pattern to the memory area depending on your application's needs.
I wouldn't think too much about this unless you deal with very large arrays (where one dimension is larger than what fits into your caches).
Note 1:
In the first code snippet, the array is allocated on the process's heap, while on the second snippet you allocate the buffer on the stack. If you want to use larger sized arrays, you might get a ... stackoverflow :)
Note 2:
My explain is focusing on the case where you want to allocate dynamically a 2D array, using Type** (int** in your case).
When you deal with a 2D array, it's faster to allocate it as a 1D array and use smart indexing to access it as a 2D array. This is because:
1D array fills a contiguous space in memory (lower fragmentation). This enables better caching of the array, decreasing the access latencies.
When you allocate a 2D array, you have another level of indirection, meaning that you need to get the address of the row first and then access the element. When you use a 1D array, you access directly the element.
When the array is allocated in 1D fashion, it's easily to align it to the cache line size. This will make it easier for the compiler to optimize transactions and avoid having to make 2 reads for an element that falls on a cache line boundary.
Dealing with 1D array, should help the compiler generate better vectorized code.
I am new to use thrust library. I have my CUDA C code which uses global 2D arrays. I am initializing it using kernel function in my code.
I have to know whether is it possible to use thrust::device_vector or thrust::fill to initialize and fill 2D arrays.
For example:
// initialize 1D array with ten numbers in a device_vector
thrust::device_vector<int> D(10);
Is it possible to give..
thrust::device_vector<int> D[5][10];
and if its possible how will I use thrust::fill function.
My aim is to optimize the code using thrust library.
In STL and thrust, a vector is a container of data elements, adhering to a strict linear sequence, therefore it is basically 1-D in nature. In thrust, these data elements can be ordinary types, and even structs and objects, but they cannot be other vectors (unlike STL).
You can create an array of vectors, but thrust operations on them will generally need to be done one-by-one on each vector within the array.
Regarding syntax, you cannot do this:
thrust::device_vector D[5][10];
You can do something like this:
thrust::device_vector<int> D[5][10];
However this will create a 2-D array of vectors, which is not what you want, I don't think.
In many cases, 2-D arrays can be "flattened" to be handled as if they were 1-dimensional, and without knowing more about your situation, this is what I would recommend investigating. If you can treat your 2-D array as if it were 1-D perhaps using pointer indexing, then you can fill the whole thing with a single thrust::fill call, for example.
I would also recommend becoming familiar with the thrust quick start guide.
Here is a worked example showing a 2D array on the host with rudimentary flattening:
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#define H 5
#define W 10
__global__ void kernel(int *data, int row, int col) {
printf("Element (%d, %d) = %d\n", row, col, data[(row*W)+col]);
}
int main(void)
{
int h[H][W];
thrust::device_vector<int> d(H*W);
thrust::copy(&(h[0][0]), &(h[H-1][W-1]), d.begin());
thrust::sequence(d.begin(), d.end());
kernel<<<1,1>>>(thrust::raw_pointer_cast(d.data()), 2, 3);
cudaDeviceSynchronize();
return 0;
}
I had a language-agnostic discussion with someone in the C++ chat and he said that arrays of arrays and multidimensional arrays are two things.
But from what I learned, a multidimensional array is nothing more than an array of other arrays that all have the same size. In particular he is saying
Well, they kind of are in C, where you simulate multiple dimensions with nested arrays
but that’s only because C doesn’t actually support multiple dimension arrays
Can someone please explain what the canonical computer-science definition of "multiple dimension arrays" is and why C (or the abstract definition "array of arrays") does not fit that definition?
Take .NET arrays which illustrate this nicely:
C# has a distinction between jagged arrays which are defined in a nested fashion:
int[][] jagged = new int[3][];
Each nested array can have a different length:
jagged[0] = new int[3];
jagged[1] = new int[4];
(And note that one of the nested arrays isn’t initialised at all, i.e. null.)
By contrast, a multidimensional array is defined as follows:
int[,] multidim = new int[3, 4];
Here, it doesn’t make sense to talk of nested arrays, and indeed trying to access multidim[0] would be a compile-time error – you need to access it providing all dimensions, i.e. multidim[0, 1].
Their types are different too, as the declarations above reveal.
Furthermore, their handling is totally different. For instance, you can iterate over the above jagged array with an object of type int[]:
foreach (int[] x in jagged) …
but iterating over a multidimensional array is done with items of type int:
foreach (int x in multidim) …
Conceptually, a jagged array is an array of arrays (… of arrays of arrays … ad infinitum) of T while a multidimensional array is an array of T with a set access pattern (i.e. the index is a tuple).
I would expect multidimensional arrays to offer operations such as "Give me the number of dimensions" or "Give me a certain column" or "Give me a certain sub-view". C arrays don't offer these operations.
From Wikipedia:
Multi-dimensional arrays
The number of indices needed to specify an element is called the dimension, dimensionality, or rank of the array type. (This nomenclature conflicts with the concept of dimension in linear algebra,[5] where it is the number of elements. Thus, an array of numbers with 5 rows and 4 columns, hence 20 elements, is said to have dimension 2 in computing contexts, but represents a matrix with dimension 4-by-5 or 20 in mathematics. Also, the computer science meaning of "rank" is similar to its meaning in tensor algebra but not to the linear algebra concept of rank of a matrix.)
Many languages support only one-dimensional arrays. In those languages, a multi-dimensional array is typically represented by an Iliffe vector, a one-dimensional array of references to arrays of one dimension less. A two-dimensional array, in particular, would be implemented as a vector of pointers to its rows. Thus an element in row i and column j of an array A would be accessed by double indexing (A[i][j] in typical notation). This way of emulating multi-dimensional arrays allows the creation of ragged or jagged arrays, where each row may have a different size — or, in general, where the valid range of each index depends on the values of all preceding indices.
This representation for multi-dimensional arrays is quite prevalent in C and C++ software. However, C and C++ will use a linear indexing formula for multi-dimensional arrays that are declared as such, e.g. by int A[10][20] or int A[m][n], instead of the traditional int **A.[6]:p.81
For an example of a language supporting multidimensional arrays, see here.
C does not have multidimensional arrays but C have arrays of arrays.
In the Standard, the wording multidimensional array is used but C multidimensional arrays are in reality arrays of arrays. From Kernighan & Ritchie:
"In C, a two-dimensional array is really a one-dimensional array, each of whose elements is an array."
Some languages support multidimensional arrays as first class types. The "Expert C Programming" book shows the example of Ada which supports both arrays of arrays and multidimensional arrays.
I get his point. He actually differ them from implementation point of view, but both are actually valid to be said multidimensional arrays.
The "array of array" kind uses linear indexing as it's actually implemented as one dimensional array, despite at language level it's referenced via multiple index. Ex, in C:
int a[5][5];
would actually have the same structure as:
int a[25];
The compiler would translate an access such as:
a[i][j]
to:
a[i * jDimensionWidth + j]
where jDimensionWidth = 5 in above example. And it's even possible to access it like:
int* b = (int*) a;
printf("%d\n",b[12] == a[2][2]); // should output 1
The "multidimensional array" kind is implemented through Iliffe vector as he said, where the indexing can't be linearized because the address is not linear as well since the vector is typically implemented as heap object. This kind of multidimensional array doesn't fit the equation (for 2-dimensional array):
addr(a[i + 1]) = addr(a[i]) + (a[i].width * sizeof(a[i][j].datatype))
which is fulfilled by the "array of array" kind.
given the following function signature:
void readFileData(FILE* fp, double inputMatrix[][], int parameters[])
this doesn't compile.
and the corrected one:
void readFileData(FILE* fp, double inputMatrix[][NUM], int parameters[])
my question is, why does the compiler demands that number of columns will be defined when handling a 2D array in C? Is there a way to pass a 2D array to a function with an unknown dimensions?
thank you
Built-in multi-deminsional arrays in C (and in C++) are implemented using the "index-translation" approach. That means that 2D (3D, 4D etc.) array is laid out in memory as an ordinary 1D array of sufficient size, and the access to the elements of such array is implemented through recalculating the multi-dimensional indices onto a corresponding 1D index. For example, if you define a 2D array of size M x N
double inputMatrix[M][N]
in reality, under the hood the compiler creates an array of size M * N
double inputMatrix_[M * N];
Every time you access the element of your array
inputMatrix[i][j]
the compiler translates it into
inputMatrix_[i * N + j]
As you can see, in order to perform the translation the compiler has to know N, but doesn't really need to know M. This translation formula can easily be generalized for arrays with any number of dimensions. It will involve all sizes of the multi-dimensional array except the first one. This is why every time you declare an array, you are required to specify all sizes except the first one.
As the array in C is purely memory without any meta information about dimensions, the compiler need to know how to apply the row and column index when addressing an element of your matrix.
inputMatrix[i][j] is internally translated to something equivalent to *(inputMatrix + i * NUM + j)
and here you see that NUM is needed.
C doesn't have any specific support for multidimensional arrays. A two-dimensional array such as double inputMatrix[N][M] is just an array of length N whose elements are arrays of length M of doubles.
There are circumstances where you can leave off the number of elements in an array type. This results in an incomplete type — a type whose storage requirements are not known. So you can declare double vector[], which is an array of unspecified size of doubles. However, you can't put objects of incomplete types in an array, because the compiler needs to know the element size when you access elements.
For example, you can write double inputMatrix[][M], which declares an array of unspecified length whose elements are arrays of length M of doubles. The compiler then knows that the address of inputMatrix[i] is i*sizeof(double[M]) bytes beyond the address of inputMatrix[0] (and therefore the address of inputMatrix[i][j] is i*sizeof(double[M])+j*sizeof(double) bytes). Note that it needs to know the value of M; this is why you can't leave off M in the declaration of inputMatrix.
A theoretical consequence of how arrays are laid out is that inputMatrix[i][j] denotes the same address as inputMatrix + M * i + j.¹
A practical consequence of this layout is that for efficient code, you should arrange your arrays so that the dimension that varies most often comes last. For example, if you have a pair of nested loops, you will make better use of the cache with for (i=0; i<N; i++) for (j=0; j<M; j++) ... than with loops nested the other way round. If you need to switch between row access and column access mid-program, it can be beneficial to transpose the matrix (which is better done block by block rather than in columns or in lines).
C89 references: §3.5.4.2 (array types), §3.3.2.1 (array subscript expressions)
C99 references: §6.7.5.2 (array types), §6.5.2.1-3 (array subscript expressions).
¹ Proving that this expression is well-defined is left as an exercise for the reader. Whether inputMatrix[0][M] is a valid way of accessing inputMatrix[1][0] is not so clear, though it would be extremely hard for an implementation to make a difference.
This is because in memory, this is just a contiguous area, a single-dimension array if you will. And to get the real offset of inputMatrix[x][y] the compiler has to calculate (x * elementsPerColumn) + y. So it needs to know elementsPerColumn and that in turn means you need to tell it.
No, there's not. The situation's pretty simple really: what the function receives is really just a single, linear block of memory. Telling it the number of columns tells it how to translate something like block[x][y] into a linear address in the block (i.e., it needs to do something like address = row * column_count + column).
Other people have explained why, but the way to pass a 2D array with unknown dimensions is to pass a pointer. The compiler demotes array parameters to pointers anyway. Just make sure it's clear what you expect in your API docs.