Этот вопрос, хотя и довольно старый, требует некоторых критериев, поскольку он требует не самого идиоматического способа или способа, который может быть записан в наименьшем количестве строк, но способа самый быстрый . И глупо отвечать на этот вопрос без какого-либо реального тестирования. Поэтому я сравнил четыре решения: memset против std :: fill и 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.
Теперь перейдем к результатам. Я рассчитал производительность для int размером 100 и для длинных длинных массивов, как статически, так и динамически распределенных, но за исключением msvc, который исключил мертвый код для статических массивов, результаты были чрезвычайно сопоставимы, поэтому я покажу только производительность динамического массива. Временные метки равны мс на 1 миллион итераций с использованием функции часов time.h с низкой точностью.
clang 3.8 (при использовании внешнего интерфейса clang-cl флаги оптимизации = / OX / arch: 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 = native -mtune = native -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 / arch: 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 killing gcc, типичные точечные оптимизации MSVC (он производит внушительное устранение мертвого кода на статических массивах, а затем имеет ужасную производительность для заполнения). Хотя моя реализация значительно быстрее, это может быть только потому, что она распознает, что очистка битов имеет гораздо меньше накладных расходов, чем любая другая операция установки.
Реализация Clang заслуживает большего внимания, поскольку она значительно быстрее. Некоторое дополнительное тестирование показывает, что его memset на самом деле специализирован для нуля - ненулевые memset для массива 400 байтов намного медленнее (~ 220 мс) и сравнимы с gcc. Тем не менее, ненулевое значение memset с массивом в 800 байтов не имеет разницы в скорости, поэтому, вероятно, в этом случае их memset имеет более низкую производительность, чем моя реализация - специализация предназначена только для небольших массивов, а обрезание составляет около 800 байтов. Также обратите внимание, что gcc 'fill' и 'ZERO' не оптимизируются под memset (глядя на сгенерированный код), gcc просто генерирует код с идентичными характеристиками производительности.
Вывод: memset на самом деле не оптимизирован для этой задачи, так как люди будут притворяться, что это так (иначе у gcc и msvc и memset llvm будет одинаковая производительность) Если производительность имеет значение, то memset не должен быть окончательным решением, особенно для этих неуклюжих массивов среднего размера, потому что он не специализирован для очистки битов, и он не оптимизирован вручную лучше, чем компилятор может сделать сам по себе.