Есть ли какие-либо asm инструкции, которые могут ускорить вычисление минуты / макс. из вектора удваивается/целым числа на архитектуре Core i7?
Обновление:
Я не ожидал такие богатые ответы, спасибо. Таким образом, я вижу, что макс. / минута возможно обойтись без ветвления. У меня есть дополнительный вопрос:
Существует ли эффективный способ получить индекс самого большого дважды в массиве?
SSE4 имеет PMAXSD
или PMAXUD
для 32-битных подписанных/неподписанных целых чисел, что может быть полезно.
SSE2 имеет MAXPD
и MAXSD
, которые сравнивают между собой и попарно пары двойников, поэтому вы следуете за n/2-1 MAXPD с одним MAXSD, чтобы получить максимум вектора n, с обычным чередованием нагрузок и операций.
Существуют МИН-эквиваленты вышеперечисленного.
Для двойного случая в ассемблере, скорее всего, не будет лучше, чем полуприличный компилятор C++ в режиме SSE:
peregrino:$ g++ -O3 src/min_max.cpp -o bin/min_max
peregrino:$ g++ -O3 -msse4 -mfpmath=sse src/min_max.cpp -o bin/min_max_sse
peregrino:$ time bin/min_max
0,40
real 0m0.874s
user 0m0.796s
sys 0m0.004s
peregrino:$ time bin/min_max_sse
0,40
real 0m0.457s
user 0m0.404s
sys 0m0.000s
где min_max вычисляет min и max массива из 500 удваивается 100,000 раз, используя наивный цикл:
bool min_max ( double array[], size_t len, double& min, double& max )
{
double min_value = array [ 0 ];
double max_value = array [ 0 ];
for ( size_t index = 1; index < len; ++index ) {
if ( array [ index ] < min_value ) min_value = array [ index ];
if ( array [ index ] > max_value ) max_value = array [ index ];
}
min = min_value;
max = max_value;
}
В ответ на вторую часть, традиционная оптимизация для удаления ветвления от максимальной операции заключается в сравнении значений, получении флага в виде одного бита ( давая 0 или 1 ), вычитании одного ( давая 0 или 0xffff_ffff ) и 'и' его с xor из двух возможных результатов, так что вы получаете эквивалент ( a > best ? ( current_index ^ best_index ) : 0 ) ^ best_index )
. Я сомневаюсь, что есть простой SSE способ сделать это, просто потому что SSE имеет тенденцию работать с упакованными значениями, а не с помеченными значениями; есть некоторые горизонтальные операции с индексами, так что вы можете попробовать найти max, затем вычесть его из всех элементов в исходном векторе, затем собрать знаковый бит, и нулевой знак будет соответствовать индексу max, но это, вероятно, не было бы улучшением, если бы вы не использовали шорты или байты.
MAXPS и MINPS из SSE работают с упакованными одноточными номерами с плавающей точкой. PMAXSW, PMINSW, PMAXUB и PMINUB работают с упакованными 8-битными словами, как подписанными, так и неподписанными. Обратите внимание, что они сравнивают два входных SSE регистра или адресные места по элементам и сохраняют результат в SSE регистре или памяти.
SSE2 версии MAXPS и MINPS должны работать с плавающими числами двойной точности.
Какие компилятор и флаги оптимизации вы используете? gcc 4.0 и лучше бы автоматически векторизовать операции, если ваша цель их поддерживает, более ранним версиям может понадобиться специальный флаг.
если Вы используете библиотеку Intel IPP, то можете использовать векторные статистические функции для вычисления векторного min/max (среди прочего)
.В ответ на Ваш второй вопрос: на большинстве платформ существуют библиотеки, которые уже содержат оптимизированные реализации именно этой операции (и большинства других простых векторных операций). Используйте их.
vDSP_maxviD( )
и cblas_idamax( )
. frameworkcblas_idamax( )
cblas_idamax( )
, которые в зависимости от их происхождения могут быть хорошо настроены, а могут и не быть; Пользователи, которым небезразлична производительность, как правило, имеют хорошую реализацию (или их можно убедить установить)