2

So at the end of each iteration that I'm doing, I want to make my array be equal to my new array (which I have called array_new). I want every element of array to take the same value as is in array_new but I'm interested in getting my code as quick as possible and so copying everything across element-by-element as this current code does isn't an option:

for(i=0;i<N_a;i++) {
  for(j=0;j<N_b;j++) {
    array[i][j] = array_new[i][j];
  }
}

This takes quite a long time because my values of N_a and N_b are very large. Is there a way to simply change what each of them point to so that I can start my next iteration more quickly? I've tried doing stuff like

 double *temp = *array;
 *array = *array_new;
 *array_new = temp;

in order to try and avoid a slow element-by-element copying procedure but it doesn't seem to work for me. Effectively what I'm trying to make happen is for every element of array point to the corresponding element in array_new but I can't work out how to make the pointers do that.

Any help would be much appreciated!

thay2302
  • 31
  • 1
  • 4
  • 2
    Please edit your post to indicate (via tags) what language you're using. A short but complete example of what you're trying to do would help, too... – Jon Skeet Nov 11 '15 at 21:23
  • Edited now, is that more clear? – thay2302 Nov 11 '15 at 21:28
  • 2
    `memcpy` if the copy is really needed. If it is just to swap two arrays each time, work with a pointer which will be updated with the currently used array address (double-buffering). – Eugene Sh. Nov 11 '15 at 21:32
  • *"avoid a slow element-by-element copying procedure"* You want to copy a block of data from one place to another. There is no getting around it - it's just a matter of how fast you can make that copy. – Jonathon Reinhart Nov 11 '15 at 21:32
  • 1
    Note that your title indicates "swapping" arrays, but your actual question only implies "copying" one array to another. I think you need to explain what it is you're *actually* trying to do, and see if anyone can make a better suggestion. – Jonathon Reinhart Nov 11 '15 at 21:34
  • Maybe keep track of parts that have not changed since last time. – 001 Nov 11 '15 at 21:34
  • I can't help but notice that you call this "swap" rather than "copy". For what reason *exactly* do you need this operation? –  Nov 11 '15 at 21:34
  • Sorry for the confusion, I only need to copy the array. In my loop I calculate the values of different elements of my array and then I need to make the elements of "array" the same as the elements of "array_new" so that the procedure can repeat for my new time step. – thay2302 Nov 11 '15 at 21:38
  • Why can't you just write the results into the new array in the same loop? – Eugene Sh. Nov 11 '15 at 21:39
  • 1
    Because in order to calculate the element array_new[i][j], I use the values array[i][j], array[i-1][j], array[i+1][j], array[i][j-1] and array [i][j+1]. So if I overwrite the element in array[i][j] straight away, I would end up using the wrong values when I came to calculate array_new[i][j+1]. – thay2302 Nov 11 '15 at 21:42
  • @thay2302 That makes sense. However, I think you can still avoid the copy step if you don't need `array` after calculating `array_new`. If so, you could flip the roles of `array` and `array_new` for the following iteration. –  Nov 11 '15 at 21:45
  • Yeah I did think about this but it gives me problems later in my code when I use "array". If nothing else then this should cut the time it takes to copy the elements over in half as I would only need to copy every other one from "array_new" to "array". Thank you for your help. – thay2302 Nov 11 '15 at 21:55
  • You can't change what an array points to, since an array is not a pointer. *If you always access the arrays through pointers,* you can swap the pointers. – user253751 Nov 11 '15 at 22:47

4 Answers4

8

Since the memory size of your array is fixed, you can simply copy the memory block from one pointer to the other. It doesn't get any faster than that.

In c/c++ you could use memcpy if that is the language you are using. Every language has something equivalent.

Edit: since you confirmed use of c I can get more detailed:

memcpy(array_new,array,sizeof(VARIABLE_TYPE_of_ARRAY_ELEMENT)*N_a*N_b);
SunKnight0
  • 3,331
  • 1
  • 10
  • 8
  • what do you mean with "Since the memory size of your array is fixed"? – terence hill Nov 11 '15 at 21:42
  • 1
    'known' was probably a better word. As opposed to passing the array pointer to a function that does not 'know' the actual size of the array. – SunKnight0 Nov 11 '15 at 21:43
  • I bet the optimizer will recognize the loops as a block copy, and use `memcpy`if that is faster. – Bo Persson Nov 11 '15 at 21:54
  • Well hopefullly the size of the array is known, otherwise you could not copy the array at all! I guess you mean "known at compile time", and also "declared as an array with a fixed size". – yankee Nov 11 '15 at 22:08
  • @Martin James That's what I'm trying to do but I can't get it to work. Can you be more specific? – thay2302 Nov 11 '15 at 22:12
1

Your pointer-swap code is just a little bit off: you are dereferencing your pointers where you shouldn't. After all, the point of that code is to avoid copying data by just swapping two pointers. Here are the correct versions (depending on whether you use a true 2D array or an array of pointers to arrays):

//array is declared as
double (*array)[N_b];

double (*temp)[N_b] = array;
array = array_new;
array_new = temp;

or

//array is declared as
double** array;

double** temp = array;
array = array_new;
array_new = temp;

This is all you need, and it's definitely the fastest possible way to exchange contents of two buffers. Much faster than memcpy()...

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • Both of these give me a segmentation fault? – thay2302 Nov 11 '15 at 21:53
  • @thay2302 Then there's something wrong with your arrays. How did you declare your arrays, how are you allocating them, and how are you destroying them? – cmaster - reinstate monica Nov 11 '15 at 21:54
  • Arrays were declared as "double **array, **array_new", they were allocated using "allocate2d(&array,N_a,N_b)" and "allocate2d(&array_new,N_a,N_b)" and are being destroyed using "free2d(&array,N_a)" and "free2d(&array_new,N_a)". – thay2302 Nov 11 '15 at 22:00
  • Can you add the full failing code in your question? As I said, the pointer swap is generally sound the way I posted it, so there must be something else wrong with your code if you get a segfault with that pointer swap. Unfortunately, I'll be away from keyboard for the next 20 hours, but I'll certainly take a look tomorrow. – cmaster - reinstate monica Nov 11 '15 at 22:06
  • I can't really share the full code as it is for an assignment that I am doing. I've tried to be reasonably unspecific with my questions but I can't realistically post the entire code on here. – thay2302 Nov 11 '15 at 22:10
  • @thay2302 Well, then you want to look for other pointers to your arrays: If you have two pointers `array` and `otherPtr` pointing to the same array, and you swap `array` with `array_new`, `otherPtr` will afterwards point to the same array as `array_new`. Producing such aliased pointers is common in C programming, but it must be avoided when doing stuff like pointer-swapping. For instance, if you freed `array_new` after the swap (does not sound too unlikely to me), `otherPtr` will still be pointing to the `free()`'d memory region, so that dereferencing `otherPtr` may segfault your program. – cmaster - reinstate monica Nov 12 '15 at 19:27
0

Two answer this, you first need to understand how the array is represented in memory. E.g. see this question: How are multi-dimensional arrays formatted in memory?

So first we need to know if you have a static array or not. If it is a static array then the task is particularly simple, because the data is laid out contiguously in memory. This means that if you have a 2x2 static array, with the content {{9, 8}, {7, 6}} your memory could look like this:

Address  0  1  2  3  4  5  6  7  8
Content  ?  ?  9  8  7  6  ?  ?  ?

In this case your variable which is declared like this:

int[2][2] myArray;

is actually a pointer to address "2" and you can easily copy the whole thing. with memcpy:

int [2][2] newArray;
memcpy(&newArray, &myArray, sizeof(int)*2*2);

Notice that this copies starting from wherever "myArray" points (in my example that is 2) as many bytes as 2*2*sizeof(int). So 4 times the size of int. (For simplicity my example assume a size of one byte for an int, but of course on most systems it is 4 byte).

If you have a dynamic array, then it is a different story. In this case your memory for your array declared like this:

int** myArray;

may well look like this:

Address  0  1  2  3  4  5  6  7  8
Content  9  8  0  6  ?  ?  7  6  ?

Note that the pointer myArray still points to the Address "2". However at the address "2" you don't find the first value, but instead another pointer which points to "0". And here you find the values of the first "row" which are 9 and 8. Next to the address "2" in number "3" you find the pointer to your second "row" which starts at position 6. As you can see, you can still find all your data, but you cannot copy them in a single go. To copy the whole array you will at least need the outer row:

int SIZE_X = 2;
int SIZE_Y = 2;
int** newArray = malloc(sizeof(int*)*SIZE_X);
for(i = 0; i < SIZE_X; ++i) {
  newArray[i] = malloc(sizeof(int)*SIZE_Y);
  memcpy(newArray[i], myArray, SIZE_Y*sizeof(int));
}

This should be faster then using two loops, as memcpy can use more efficient ways to copy than a copy loop.

Community
  • 1
  • 1
yankee
  • 38,872
  • 15
  • 103
  • 162
  • Shouldn't line #3 be like: `int** newArray = (int**)malloc(sizeof(int*) * SIZE_X);`? If not, what does this version mean? And when should I use it? – Rad Jun 16 '16 at 12:25
  • @Rad: You added a cast which has no effect (at least I believe it works without, it is too long ago that I checked) and you used `sizeof(int*)` instead of `sizeof(int)`. All systems I am aware of implement `int*` as a plain `int`, so that would be the same. However I agree that it is better practice to write `int*` so I changed my answer. – yankee Jun 16 '16 at 16:12
  • Thanks for the info. My point was `sizeof(int*)`, not the casting. Just to make sure I understand it correctly, what about this one: `sizeof(char*)`. The way I understand `sizeof(type*)` returns the size of address on the machine (might be 4 or 8), but `sizeof(type)` returns the size of that specific type (e.g if it's `char` it returns 1). Is it correct? – Rad Jun 16 '16 at 17:57
  • @Rad: Yes, that is correct. However an address is usually an `int` and thus size of address (for any type) is usually the same as an `int`. That means of course that `sizeof(int) == sizeof(int*)` but it also means `sizeof(char) != sizeof(char*)` (depending on the system) – yankee Jun 17 '16 at 07:19
0

If you just want to swap the pointers, not physically copying the data, and to still be able to access the arrays using indexes, here is an example of how it can be done:

#define N_a 2
#define N_b 3

typedef struct arr 
{ 
    int val[N_a][N_b]; 
} arr;

arr array     = {{{11,12,13},{14,15,16}}};
arr array_new = {{{21,22,23},{24,25,26}}};

int main()
{
    arr *p_array;
    arr *p_array_new;

    p_array     = &array;
    p_array_new = &array_new;
    printf("%d %d\n", p_array->val[1][2], p_array_new->val[1][2]);
    // output: 16 26

    p_array     = &array_new;
    p_array_new = &array;
    printf("%d %d\n", p_array->val[1][2], p_array_new->val[1][2]);
    // output: 26 16
}
nnn
  • 3,980
  • 1
  • 13
  • 17
  • Sorry, I'm struggling to follow this. So what is the typedef struct bit doing? What about if I've already got all the elements of my two arrays "array" and "array_new"? – thay2302 Nov 11 '15 at 22:16
  • It's just encapsulating the array type definition / dimensions, to help with indexing. If you already have the array data, just initialize the pointers to it: `arr *p_array = your_array_address;` – nnn Nov 11 '15 at 22:26
  • So if I have the values already then I should just use "*p_array = &array", "*p_array_new = &array_new", "p_array = &array_new" and "p_array_new = &array"? – thay2302 Nov 11 '15 at 22:33
  • It's giving me errors saying "initialization from incompatible pointer type" and "assignment from incompatible pointer type" – thay2302 Nov 11 '15 at 22:36
  • I edited my answer to separate the pointer declaration and initialization. To avoid the warning / error, use a cast. For your array, you may not need to use `&array`, but just `array` (especially if dynamically allocated). Use `p_array = (arr*)array;` – nnn Nov 11 '15 at 22:43
  • What do you mean use a cast? – thay2302 Nov 11 '15 at 22:45