programing tip

C에서 2D 배열을 0으로 만드는 가장 빠른 방법은 무엇입니까?

itbloger 2020. 9. 13. 10:12
반응형

C에서 2D 배열을 0으로 만드는 가장 빠른 방법은 무엇입니까?


C에서 큰 2d 배열을 반복적으로 제로화하고 싶습니다. 이것이 제가 지금하는 일입니다.

// Array of size n * m, where n may not equal m
for(j = 0; j < n; j++)
{
    for(i = 0; i < m; i++)
    {  
        array[i][j] = 0;
    }
}

memset을 사용해 보았습니다.

memset(array, 0, sizeof(array))

그러나 이것은 1D 배열에서만 작동합니다. 2D 배열의 내용을 인쇄하면 첫 번째 행은 0이지만 임의의 큰 숫자가로드되어 충돌합니다.


memset(array, 0, sizeof(array[0][0]) * m * n);

어디 mn2 차원 배열의 폭과 높이 (귀하의 예제에서, 당신이 그렇게, 사각형 2 차원 배열을 가지고 있습니다 m == n).


경우 array진정으로 배열입니다, 당신은 함께 "그것을 밖으로 제로"할 수 있습니다 :

memset(array, 0, sizeof array);

그러나 알아야 할 두 가지 사항이 있습니다.

  • 이것은 array실제로 "2 차원 배열"인 경우에만 작동합니다 . 즉, T array[M][N];일부 유형에 대해 선언 되었습니다 T.
  • array선언 된 범위에서만 작동합니다 . 당신이 함수에 전달하면, 그 이름은 array 포인터로 붕괴 , 그리고 sizeof당신에게 배열의 크기를 제공하지 않습니다.

실험을 해보자 :

#include <stdio.h>

void f(int (*arr)[5])
{
    printf("f:    sizeof arr:       %zu\n", sizeof arr);
    printf("f:    sizeof arr[0]:    %zu\n", sizeof arr[0]);
    printf("f:    sizeof arr[0][0]: %zu\n", sizeof arr[0][0]);
}

int main(void)
{
    int arr[10][5];
    printf("main: sizeof arr:       %zu\n", sizeof arr);
    printf("main: sizeof arr[0]:    %zu\n", sizeof arr[0]);
    printf("main: sizeof arr[0][0]: %zu\n\n", sizeof arr[0][0]);
    f(arr);
    return 0;
}

내 컴퓨터에서 위의 내용이 인쇄됩니다.

main: sizeof arr:       200
main: sizeof arr[0]:    20
main: sizeof arr[0][0]: 4

f:    sizeof arr:       8
f:    sizeof arr[0]:    20
f:    sizeof arr[0][0]: 4

arr은 배열 이지만 에 전달 될 때 첫 번째 요소에 대한 포인터로 축소 f()되므로 인쇄 된 크기 f()가 "잘못된"것입니다. 또한 f()의 크기 는 "array [5] of "인 arr[0]배열의 크기입니다 . "감쇠"는 첫 번째 수준에서만 발생하기 때문에 의 크기가 아니므 로 올바른 크기의 배열에 대한 포인터를 사용하는 것으로 선언해야합니다 .arr[0]intint *f()

그래서 제가 말했듯이, 당신이 원래하던 일은 위의 두 가지 조건이 충족 되어야만 작동 할 것입니다. 그렇지 않은 경우 다른 사람이 말한대로해야합니다.

memset(array, 0, m*n*sizeof array[0][0]);

마지막으로 게시 memset()for루프는 엄격한 의미에서 동일하지 않습니다. 포인터 및 부동 소수점 값과 같은 특정 유형에 대해 "모든 비트 0"이 0이 아닌 컴파일러가있을 수 있습니다. 나는 당신이 그것에 대해 걱정할 필요가 있는지 의심합니다.


글쎄요, 가장 빠른 방법은 전혀하지 않는 것입니다.

내가 아는 이상하게 들리는데, 여기에 의사 코드가 있습니다.

int array [][];
bool array_is_empty;


void ClearArray ()
{
   array_is_empty = true;
}

int ReadValue (int x, int y)
{
   return array_is_empty ? 0 : array [x][y];
}

void SetValue (int x, int y, int value)
{
   if (array_is_empty)
   {
      memset (array, 0, number of byte the array uses);
      array_is_empty = false;
   }
   array [x][y] = value;
}

Actually, it's still clearing the array, but only when something is being written to the array. This isn't a big advantage here. However, if the 2D array was implemented using, say, a quad tree (not a dynamic one mind), or a collection of rows of data, then you can localise the effect of the boolean flag, but you'd need more flags. In the quad tree just set the empty flag for the root node, in the array of rows just set the flag for each row.

Which leads to the question "why do you want to repeatedly zero a large 2d array"? What is the array used for? Is there a way to change the code so that the array doesn't need zeroing?

For example, if you had:

clear array
for each set of data
  for each element in data set
    array += element 

that is, use it for an accumulation buffer, then changing it like this would improve the performance no end:

 for set 0 and set 1
   for each element in each set
     array = element1 + element2

 for remaining data sets
   for each element in data set
     array += element 

This doesn't require the array to be cleared but still works. And that will be far faster than clearing the array. Like I said, the fastest way is to not do it in the first place.


If you are really, really obsessed with speed (and not so much with portability) I think the absolute fastest way to do this would be to use SIMD vector intrinsics. e.g. on Intel CPUs, you could use these SSE2 instructions:

__m128i _mm_setzero_si128 ();                   // Create a quadword with a value of 0.
void _mm_storeu_si128 (__m128i *p, __m128i a);  // Write a quadword to the specified address.

Each store instruction will set four 32-bit ints to zero in one hit.

p must be 16-byte aligned, but this restriction is also good for speed because it will help the cache. The other restriction is that p must point to an allocation size that is a multiple of 16-bytes, but this is cool too because it allows us to unroll the loop easily.

Have this in a loop, and unroll the loop a few times, and you will have a crazy fast initialiser:

// Assumes int is 32-bits.
const int mr = roundUpToNearestMultiple(m, 4);      // This isn't the optimal modification of m and n, but done this way here for clarity.    
const int nr = roundUpToNearestMultiple(n, 4);    

int i = 0;
int array[mr][nr] __attribute__ ((aligned (16)));   // GCC directive.
__m128i* px = (__m128i*)array;
const int incr = s >> 2;                            // Unroll it 4 times.
const __m128i zero128 = _mm_setzero_si128();

for(i = 0; i < s; i += incr)
{
    _mm_storeu_si128(px++, zero128);
    _mm_storeu_si128(px++, zero128);
    _mm_storeu_si128(px++, zero128);
    _mm_storeu_si128(px++, zero128);
}

There is also a variant of _mm_storeu that bypasses the cache (i.e. zeroing the array won't pollute the cache) which could give you some secondary performance benefits in some circumstances.

See here for SSE2 reference: http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx


If you initialize the array with malloc, use calloc instead; it will zero your array for free. (Same perf obviously as memset, just less code for you.)


int array[N][M] = {0};

...at least in GCC 4.8.


How was your 2D array declared?

If it something like:

int arr[20][30];

You can zero it by doing:

memset(arr, sizeof(int)*20*30);

Use calloc instead of malloc . calloc will initiate all fields to 0.

int *a = (int *)calloc(n,size of(int)) ;

//all cells of a have been initialized to 0


I think that the fastest way to do it by hand is following code. You can compare it's speed to memset function, but it shouldn't be slower.

(change type of ptr and ptr1 pointers if your array type is different then int)


#define SIZE_X 100
#define SIZE_Y 100

int *ptr, *ptr1;
ptr = &array[0][0];
ptr1 = ptr + SIZE_X*SIZE_Y*sizeof(array[0][0]);


while(ptr < ptr1)
{
    *ptr++ = 0;
}


memset(array, 0, sizeof(int [n][n]));

You can try this

int array[20,30] = {{0}};

This happens because sizeof(array) gives you the allocation size of the object pointed to by array. (array is just a pointer to the first row of your multidimensional array). However, you allocated j arrays of size i. Consequently, you need to multiply the size of one row, which is returned by sizeof(array) with the number of rows you allocated, e.g.:

bzero(array, sizeof(array) * j);

Also note that sizeof(array) will only work for statically allocated arrays. For a dynamically allocated array you would write

size_t arrayByteSize = sizeof(int) * i * j; 
int *array = malloc(array2dByteSite);
bzero(array, arrayByteSize);

참고URL : https://stackoverflow.com/questions/2516096/fastest-way-to-zero-out-a-2d-array-in-c

반응형