Предполагая, что у нас есть a T myarray[100]
с T = int, unsigned int, long long int или unsigned long long int, каков самый быстрый способ сбросить все его содержимое до нуля (не только для инициализации, но и для сброса содержимого несколько раз в моей программе) ? Может с memset?
Тот же вопрос для динамического массива вроде T *myarray = new T[100]
.
new
это C ++ ...memset
когда каким-то образом задействован C ++ ... :)for
цикл. Но, что удивительно, вы можете сделать намного хуже, если будете умны.Ответы:
memset
(from<string.h>
), вероятно, самый быстрый стандартный способ, поскольку обычно это процедура, написанная непосредственно на ассемблере и оптимизированная вручную.Кстати, в C ++ идиоматическим способом было бы использовать
std::fill
(from<algorithm>
):который может быть автоматически оптимизирован в
memset
; Я совершенно уверен, что он будет работать так же быстро, какmemset
дляint
s, но может работать немного хуже для меньших типов, если оптимизатор недостаточно умен. Тем не менее, если есть сомнения, профиль.источник
memset
что целое число будет установлено равным 0; не было никакого конкретного утверждения, которое представляло бы все биты-ноль0
. Техническое исправление добавило такую гарантию, которая включена в стандарт ISO 2011 года. Я считаю, что all-bit-zero является допустимым представлением0
для всех целочисленных типов во всех существующих реализациях C и C ++, поэтому комитет смог добавить это требование. (Нет аналогичной гарантии для типов с плавающей запятой или указателей.)0
. (С битами заполнения существует возможность, что все-биты-ноль могут быть представлением ловушки). Но в любом случае TC должен подтверждать и заменять дефектный текст, поэтому с 2004 года мы должны действовать так, как если бы C99 всегда содержал этот текст.int (*myarray)[N] = malloc(sizeof(*myarray));
.N
, но в подавляющем большинстве случаев, если вы использовали,malloc
вы знали только во время выполнения.Этот вопрос, хотя и довольно старый, требует некоторых тестов, поскольку он требует не самого идиоматического способа, или способа, который может быть записан с наименьшим количеством строк, но самым быстрым способом. И глупо отвечать на этот вопрос без реального тестирования. Поэтому я сравнил четыре решения: memset против std :: fill против ZERO ответа AnT и решения, которое я сделал с использованием встроенных функций AVX.
Обратите внимание, что это решение не является универсальным, оно работает только с 32- или 64-битными данными. Прокомментируйте, если этот код делает что-то неправильно.
Я не буду утверждать, что это самый быстрый метод, так как я не специалист по оптимизации низкого уровня. Скорее, это пример правильной архитектурно-зависимой реализации, которая быстрее, чем memset.
Теперь о результатах. Я рассчитал производительность для массивов размера 100 int и long long, как статически, так и динамически распределенных, но за исключением msvc, который устранял мертвый код на статических массивах, результаты были чрезвычайно сопоставимы, поэтому я покажу только производительность динамического массива. Разметка времени составляет миллисекунды для 1 миллиона итераций с использованием функции часов с низкой точностью time.h.
clang 3.8 (Используя интерфейс clang-cl, флаги оптимизации = / OX / arch: AVX / Oi / Ot)
gcc 5.1.0 (флаги оптимизации: -O3 -march = native -mtune = native -mavx):
msvc 2015 (флаги оптимизации: / OX / arch: AVX / Oi / Ot):
Здесь происходит много интересного: llvm убивает gcc, типичные точечные оптимизации MSVC (он производит впечатляющее устранение мертвого кода на статических массивах, а затем имеет ужасную производительность для заполнения). Хотя моя реализация значительно быстрее, это может быть только потому, что она признает, что очистка битов имеет гораздо меньше накладных расходов, чем любая другая операция настройки.
Реализация Clang заслуживает большего внимания, так как она значительно быстрее. Некоторое дополнительное тестирование показывает, что его memset на самом деле специализирован для нулевых - ненулевых memset для 400-байтового массива намного медленнее (~ 220 мс) и сопоставим с gcc. Однако ненулевое значение memset с 800-байтовым массивом не влияет на скорость, вероятно, поэтому в этом случае их memset имеет худшую производительность, чем моя реализация - специализация предназначена только для небольших массивов, а отсечение составляет около 800 байтов. Также обратите внимание, что gcc 'fill' и 'ZERO' не оптимизируются для memset (глядя на сгенерированный код), gcc просто генерирует код с идентичными характеристиками производительности.
Вывод: memset на самом деле не оптимизирован для этой задачи, как люди могли бы это представить (иначе gcc, msvc и llvm memset имели бы одинаковую производительность). Если производительность имеет значение, то memset не должен быть окончательным решением, особенно для этих неудобных массивов среднего размера, потому что он не специализируется на очистке битов и не оптимизирован вручную лучше, чем компилятор может сделать сам по себе.
источник
a
помещается в регистр. После этого он перебирает все 32-байтовые блоки, которые должны быть полностью перезаписаны с помощью арифметики указателей ((float *)((a)+x)
). Два встроенных функций (начиная с_mm256
) просто создать нулевой инициализированную регистр 32byte и хранить его к текущему указателю. Это первые 3 строки. Остальное просто обрабатывает все особые случаи, когда последний 32-байтовый блок не должен быть полностью перезаписан. Это быстрее за счет векторизации. - Надеюсь, это поможет.Из
memset()
:Вы можете использовать,
sizeof(myarray)
если размерmyarray
известен во время компиляции. В противном случае, если вы используете массив с динамическим размером, например, полученный черезmalloc
илиnew
, вам нужно будет отслеживать длину.источник
sizeof
всегда оценивается во время компиляции (и не может использоваться с VLA). В C99 это может быть выражение времени выполнения в случае VLA.c
и вc++
. Я прокомментировал ответ Алекса, в котором говорится: «Вы можете использовать sizeof (myarray), если размер myarray известен во время компиляции».Ты можешь использовать
memset
, но только потому, что наш выбор типов ограничен целыми типами.В общем случае в C имеет смысл реализовать макрос
Это даст вам функциональность, подобную C ++, которая позволит вам «сбросить до нуля» массив объектов любого типа, не прибегая к хакам вроде
memset
. По сути, это аналог C шаблона функции C ++, за исключением того, что вы должны явно указать аргумент типа.Кроме того, вы можете построить «шаблон» для нераспавшихся массивов.
В вашем примере это будет применяться как
Также стоит отметить, что специально для объектов скалярных типов можно реализовать не зависящий от типа макрос
и
превратить приведенный выше пример в
источник
;
послеwhile(0)
, чтобы можно было позвонитьZERO(a,n);
, +1 отличный ответdo{}while(0)
идиомы не требует;
определения макроса. Исправлена.Для статического объявления, я думаю, вы могли бы использовать:
Для динамического объявления я предлагаю такой же способ:
memset
источник
zero(myarray);
это все, что вам нужно в C ++.Просто добавьте это в заголовок:
источник
zero
также верна, например,T=char[10]
как это может быть в случае, когдаarr
аргумент является многомерным массивом, напримерchar arr[5][10]
.ARRAY_SIZE
макрос, который дает неправильный размер при использовании в многомерном массиве, возможно, лучшее имяARRAY_DIM<n>_SIZE
.Вот функция, которую я использую:
Вы можете назвать это так:
Выше приведен более способ использования C ++ 11, чем использование memset. Также вы получите ошибку времени компиляции, если используете динамический массив с указанием размера.
источник