Подтвердить что ты не робот

Очистка небольшого целочисленного массива: memset vs. for loop

Есть два способа обнулить массив integer/float:

memset(array, 0, sizeof(int)*arraysize);

или

for (int i=0; i <arraysize; ++i)
    array[i]=0;

очевидно, memset выполняется быстрее при больших arraysize. Однако в какой момент накладные расходы memset действительно больше, чем накладные расходы цикла for? Например, для массива размером 5 - что было бы лучше? Первая, вторая или, возможно, даже нерасширенная версия:

array[0] = 0;
array[1] = 0;
array[2] = 0;
array[3] = 0;
array[4] = 0;
4b9b3361

Ответ 1

По всей вероятности, memset() будет встроен вашим компилятором (большинство компиляторов рассматривают его как "внутреннее", что в основном означает его встроенный, за исключением, возможно, самых низких оптимизаций или если явно не отключено).

Например, вот некоторые примечания к выпуску из GCC 4.3:

Генерация кода движения блока (memcpy) и набор блоков (memset) была переписана. GCC теперь может выбрать лучший алгоритм (цикл, развернутый цикл, инструкции с префиксом rep или вызов библиотеки) в зависимости от размера блок копируется, а процессор оптимизирован для. Новый вариант -minline-stringops-dynamically имеет был добавлен. С этой опцией строка операции неизвестного размера расширены так, что небольшие блоки копируется по встроенному коду, а для используются большие блоки для вызова библиотеки. Это приводит к более быстрому коду, чем -minline-all-stringops, когда реализация библиотеки используя подсказки иерархии кеша. эвристический выбор конкретного алгоритм может быть перезаписан -mstringop-strategy. Недавно также memset значений, отличных от 0, встраиваемый.

Возможно, компилятор может сделать что-то подобное с альтернативными примерами, которые вы дали, но я бы поставил на него меньше шансов.

И он grep -возможный и более сразу очевидный с первого взгляда, что намерение загружается (не то, что цикл особенно сложно проверить).

Ответ 2

Как уже отмечал Майкл, gcc и, я думаю, большинство других компиляторов оптимизируют это уже очень хорошо. Например, gcc превращает это

char arr[5];
memset(arr, 0, sizeof arr);

в

movl  $0x0, <arr+0x0>
movb  $0x0, <arr+0x4>

Это не лучше, чем это...

Ответ 3

Невозможно ответить на вопрос без измерения. Он будет полностью зависеть от реализаций библиотеки компилятора, процессора и среды выполнения.

memset() может быть бит "запаха кода", поскольку он может быть подвержен переполнениям буфера, инверсии параметров и имеет неудачную способность только очищать "байт-мудрый". Однако это безопасная ставка, что она будет "самой быстрой" во всех, кроме крайних случаях.

Я склонен использовать макрос, чтобы обернуть это, чтобы избежать некоторых проблем:

#define CLEAR(s) memset(&(s), 0, sizeof(s))

Это уменьшает размеры вычислений и устраняет проблему замены параметров длины и vlaue.

Короче говоря, используйте memset() "под капотом". Напишите то, что вы намерены, и пусть компилятор беспокоится об оптимизации. Большинство из них невероятно хороши в этом.

Ответ 4

Учитывая этот код как таковой, уже сказано. Но если вы считаете это в своей программе, о которой я ничего не знаю, что-то еще можно сделать. Например, если этот код должен выполняться через некоторое время, чтобы очистить массив, вы можете запустить поток, который постоянно выделяет новый массив нулевых элементов, назначенных глобальной переменной, которую ваш код, когда требуется, чтобы очищаемый массив был очищен, просто указывает на.

Это третий вариант. Конечно, если вы планируете запустить свой код на процессоре с по меньшей мере двумя ядрами, и это имеет смысл. Кроме того, код должен запускаться более одного раза, чтобы увидеть преимущества. Для однократного запуска вы можете объявить массив, заполненный нулями, а затем указать его, когда это необходимо.

Надеюсь, это поможет кому-то