[c] 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;
}

실제로, 여전히 배열을 지우고 있지만 배열에 무언가가 기록 될 때만 가능합니다. 이것은 여기서 큰 이점이 아닙니다. 그러나 2D 배열이 쿼드 트리 (동적 한 마음이 아님) 또는 데이터 행 모음을 사용하여 구현 된 경우 부울 플래그의 효과를 지역화 할 수 있지만 더 많은 플래그가 필요합니다. 쿼드 트리에서는 루트 노드에 대해 빈 플래그를 설정하고 행 배열에서 각 행에 대한 플래그를 설정합니다.

“대형 2D 배열을 반복적으로 0으로 설정하려는 이유”라는 질문으로 이어지는 것은 무엇입니까? 어레이의 용도는 무엇입니까? 배열을 0으로 만들 필요가 없도록 코드를 변경하는 방법이 있습니까?

예를 들어 다음과 같은 경우 :

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

즉, 누적 버퍼에 사용하고 다음과 같이 변경하면 성능이 향상됩니다.

 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 

이것은 배열을 지울 필요가 없지만 여전히 작동합니다. 그리고 그것은 어레이를 지우는 것보다 훨씬 빠를 것입니다. 내가 말했듯이 가장 빠른 방법은 처음부터하지 않는 것입니다.


답변

당신이 정말로 속도에 집착한다면 (이동성에 ​​그다지 그다지 중요하지 않음) 이것을 수행하는 가장 빠른 방법은 SIMD 벡터 내장을 사용하는 것입니다. 예를 들어 Intel CPU에서는 다음 SSE2 명령어를 사용할 수 있습니다.

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

각 저장 명령은 한 번의 히트에서 4 개의 32 비트 정수를 0으로 설정합니다.

p는 16 바이트로 정렬되어야하지만이 제한은 캐시에 도움이되기 때문에 속도에도 좋습니다. 다른 제한은 p가 16 바이트의 배수 인 할당 크기를 가리켜 야한다는 것입니다.하지만 루프를 쉽게 풀 수 있기 때문에 이것 역시 멋집니다.

이것을 루프에 넣고 루프를 몇 번 풀면 엄청나게 빠른 이니셜 라이저가 생깁니다.

// 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);
}

_mm_storeu캐시를 우회 하는 변형도 있습니다 (즉, 어레이를 0으로 설정해도 캐시가 오염되지 않음). 일부 상황에서 2 차 성능 이점을 제공 할 수 있습니다.

SSE2 참조는 여기를 참조하십시오 : http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx


답변

로 배열을 초기화하는 malloc경우 calloc대신 사용하십시오. 무료로 어레이를 제로화합니다. (분명히 memset과 동일한 성능이며 코드가 적습니다.)


답변

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

… 적어도 GCC 4.8에서는.


답변

2D 배열은 어떻게 선언 되었습니까?

다음과 같은 경우 :

int arr[20][30];

다음을 수행하여 제로화 할 수 있습니다.

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