Этот вопрос, хотя и довольно старый, нуждается в некоторых критериев, как он просит не самый идиоматических способ, или способ, который может быть записан в наименьшее число линий, но самый быстрый способ. И глупо отвечать на этот вопрос без какого-либо фактического тестирования. Поэтому я сравнил четыре решения: memset vs. std :: fill vs. ZERO ответа AnT с решением, которое я сделал с использованием встроенных AVX.
Обратите внимание, что это решение не является общим, оно работает только с данными 32 или 64 бит. Прокомментируйте, если этот код делает что-то неправильное.
#include<immintrin.h>
#define intrin_ZERO(a,n){\
size_t x = 0;\
const size_t inc = 32/sizeof(*(a));/*size of 256 bit register over size of variable*/\
for (;x < n-inc;x+=inc)\
_mm256_storeu_ps((float *)((a)+x),_mm256_setzero_ps());\
if(4 == sizeof(*(a))){\
switch(n-x){\
case 3:\
(a)[x] = 0;x++;\
case 2:\
_mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
case 1:\
(a)[x] = 0;\
break;\
case 0:\
break;\
};\
}\
else if(8 == sizeof(*(a))){\
switch(n-x){\
case 7:\
(a)[x] = 0;x++;\
case 6:\
(a)[x] = 0;x++;\
case 5:\
(a)[x] = 0;x++;\
case 4:\
_mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
case 3:\
(a)[x] = 0;x++;\
case 2:\
((long long *)(a))[x] = 0;break;\
case 1:\
(a)[x] = 0;\
break;\
case 0:\
break;\
};\
}\
}
Я не буду утверждать, что это самый быстрый способ, так как я не эксперт по оптимизации на низком уровне. Скорее, это пример правильной реализации, зависящей от архитектуры, которая быстрее, чем memset.
Теперь, на результаты. Я рассчитал производительность для массивов длиной 100 и более длинный длинный, как статически, так и динамически распределенных, но за исключением msvc, который удалил мертвый код на статических массивах, результаты были чрезвычайно сопоставимы, поэтому я буду показывать только динамическую производительность массива. Временные метки составляют ms для 1 миллиона итераций, используя функцию часов с низкой точностью time.h.
лязг 3,8 (Использование лязг-сл фронтэнда, флаги оптимизации =/OX/арка: AVX/Oi/Ot)
int:
memset: 99
fill: 97
ZERO: 98
intrin_ZERO: 90
long long:
memset: 285
fill: 286
ZERO: 285
intrin_ZERO: 188
GCC 5.1.0 (флаги оптимизации: -O3 -march = родной - mtune = родной -mavx):
int:
memset: 268
fill: 268
ZERO: 268
intrin_ZERO: 91
long long:
memset: 402
fill: 399
ZERO: 400
intrin_ZERO: 185
MSVC 2015 (флаги оптимизации:/OX/арка: AVX/Oi/Ot):
int
memset: 196
fill: 613
ZERO: 221
intrin_ZERO: 95
long long:
memset: 273
fill: 559
ZERO: 376
intrin_ZERO: 188
Существует много интересно г здесь: llvm gilling gcc, типичная оптимизация пятен MSVC (это делает впечатляющее уничтожение мертвого кода на статических массивах, а затем имеет ужасную производительность для заполнения). Хотя моя реализация значительно быстрее, это может быть связано только с тем, что она распознает, что для очистки бит имеет гораздо меньше накладных расходов, чем любая другая операция настройки.
Выполнение Клана заслуживает большего внимания, поскольку оно значительно быстрее. Некоторое дополнительное тестирование показывает, что его memset на самом деле специализируется на ноль - ненулевые memsets для 400-байтового массива намного медленнее (~ 220 мс) и сравнимы с gcc. Однако ненулевое memsetting с массивом 800 байт не делает разницы в скорости, что, вероятно, в этом случае, их memset имеет худшую производительность, чем моя реализация - специализация предназначена только для небольших массивов, а cuttoff - около 800 байт. Также обратите внимание, что gcc 'fill' и 'ZERO' не оптимизируются для memset (смотря на сгенерированный код), gcc просто генерирует код с одинаковыми характеристиками производительности.
Заключение: memset на самом деле не оптимизирован для этой задачи, а люди будут притворяться, что это (в противном случае memc gcc и msvc и llvm будут иметь одинаковую производительность). Если производительность имеет значение, memset не должен быть окончательным решением, особенно для этих неудобных массивов среднего размера, поскольку он не специализируется на очистке бит, и он не оптимизирован вручную, лучше, чем компилятор может сделать сам по себе.
Нет C++ в поле зрения - удаляющая бирка. –
@BoPersson: ну, 'новый' * есть * C++ ... –
@Matteo - ну, да. Не сильно повлиял на ответы (пока только сейчас :-). –