Этот вопрос, хотя и довольно старый, требует некоторых тестов, так как он запрашивает не самый идиоматический способ или способ, который можно записать с наименьшим количеством строк, но самый самый быстрый способ. И глупо отвечать на этот вопрос без реального тестирования. Итак, я сравнил четыре решения: 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.
Теперь о результатах. Я рассчитал производительность для массивов размера 100 int и long long, как статически, так и динамически распределенных, но за исключением 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 убивает gcc, типичные точечные оптимизации MSVC (он производит впечатляющее удаление мертвого кода на статических массивах, а затем имеет ужасную производительность для заполнения). Хотя моя реализация значительно быстрее, это может быть только потому, что она признает, что очистка битов имеет гораздо меньше накладных расходов, чем любая другая операция настройки.
Реализация Clang заслуживает большего внимания, поскольку она значительно быстрее. Некоторое дополнительное тестирование показывает, что его memset на самом деле специализирован для нулевых - ненулевых memset для 400-байтового массива намного медленнее (~ 220 мс) и сопоставим с gcc. Однако ненулевое значение memset с 800-байтовым массивом не влияет на скорость, вероятно, поэтому в этом случае их memset имеет худшую производительность, чем моя реализация - специализация предназначена только для небольших массивов, а отсечение составляет около 800 байтов. Также обратите внимание, что gcc 'fill' и 'ZERO' не оптимизируются для memset (глядя на сгенерированный код), gcc просто генерирует код с идентичными характеристиками производительности.
Заключение: memset на самом деле не оптимизирован для этой задачи, как люди могли бы это представить (иначе gcc, msvc и llvm memset имели бы одинаковую производительность). Если производительность имеет значение, тогда memset не должен быть окончательным решением, особенно для этих неудобных массивов среднего размера, потому что он не специализируется на очистке битов и не оптимизирован вручную лучше, чем компилятор может сделать сам по себе.
person
Benjamin
schedule
21.09.2015
new
это C ++ ... - person Matteo Italia   schedule 06.02.2012memset
, когда каким-то образом задействован C ++ ... :) - person Matteo Italia   schedule 06.02.2012for
. Но, что удивительно, вы можете сделать намного хуже, если будете умны. - person David Schwartz   schedule 21.09.2015