Самый быстрый способ обнулить 2d-массив в C?
Я хочу повторно обнулить большой 2d массив в C. Это то, что я делаю в данный момент:
// 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-массива, первая строка равна нулю, но затем я получил нагрузку случайных больших чисел, и она падает.
12 ответов:
memset(array, 0, sizeof(array[0][0]) * m * n);
здесь
m
иn
- это ширина и высота двумерного массива (в вашем примере у вас есть квадратный двумерный массив, поэтомуm == n
).
если
array
действительно массив, то вы можете "обнулить его" с:memset(array, 0, sizeof array);
но есть два момента, которые вы должны знать:
- это работает только если
array
действительно является "двухмерным массивом", т. е. был объявлен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()
размерarr[0]
- это размер массиваarr[0]
, который является "массивом [5]int
". Это не размерint *
, потому что "загнивающий" только происходит на первом уровне, и именно поэтому нам нужно объявитьf()
как взять указатель на массив правильного размера.memset(array, 0, m*n*sizeof array[0][0]);
наконец,
memset()
иfor
цикл, который вы опубликовали, не эквивалентен в строгом смысле. Там могут быть (и были) компиляторы, где "все биты ноль" не делает равны нулю для некоторых типов, таких как указатели и значения с плавающей точкой. Я сомневаюсь, что вам нужно беспокоиться об этом.
ну, самый быстрый способ сделать это-не делать этого вообще.
звучит странно, я знаю, вот некоторые псевдокод:
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 массив"? Для чего используется массив? Есть ли способ изменить код так, чтобы массив не нуждался в обнулении?
например, если у вас есть:
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 вы можете использовать следующие инструкции 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.
каждая инструкция магазина установит четыре 32-битных входа в ноль за один удар.
p должен быть выровнен по 16 байтам, но это ограничение также хорошо для скорости, потому что это поможет Кешу. Другое ограничение заключается в том, что п должен указывать на размер выделения, кратный 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
это обходит кэш (т. е. обнуление массива не будет загрязнять кэш), что может дать вам некоторые дополнительные преимущества производительности в некоторых обстоятельствах.см. здесь Ссылка SSE2:http://msdn.microsoft.com/en-us/library/kcwz153a (v = vs. 80). aspx
Если вы инициализируете массив с
malloc
используйтеcalloc
вместо этого; он обнулит Ваш массив бесплатно. (Тот же perf очевидно, что и memset, просто меньше кода для вас.)
как был объявлен ваш 2D массив?
Если это что-то вроде:
int arr[20][30];
Вы можете нул его:
memset(arr, sizeof(int)*20*30);
используйте calloc вместо malloc . calloc инициирует все поля до 0.
инт *а = (инт *)с памятью(Н размер(международный)) ;
//все ячейки a были инициализированы до 0
Я думаю, что самый быстрый способ сделать это вручную следующим кодом. Вы можете сравнить его скорость с функцией memset, но она не должна быть медленнее.
(измените тип указателей ptr и ptr1, если ваш тип массива отличается от 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]);
это происходит потому, что sizeof (array) дает вам размер выделения объекта, на который указывает массив. (массив - это просто указатель на первую строку вашего многомерного массива). Тем не менее, вы выделили j массивы в размере Я. Следовательно, вам нужно умножить размер одной строки, которая возвращается sizeof (массив) с количеством выделенных строк, например:
bzero(array, sizeof(array) * j);
Также обратите внимание, что sizeof(массив) только работа для статически выделенных массивов. Для динамически выделенного массива вы бы написали
size_t arrayByteSize = sizeof(int) * i * j; int *array = malloc(array2dByteSite); bzero(array, arrayByteSize);