очистка небольшого целочисленного массива: memset против цикла for - PullRequest
56 голосов
/ 16 июля 2009

Есть два способа обнуления целочисленного / плавающего массива:

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;

Ответы [ 4 ]

45 голосов
/ 16 июля 2009

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

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

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

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

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

21 голосов
/ 16 июля 2009

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

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

в

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

Ничего не лучше ...

8 голосов
/ 16 июля 2009

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

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

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

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

Это обходит расчеты размера и устраняет проблему обмена параметрами длины и значения.

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

1 голос
/ 10 июля 2016

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

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

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

...