В продолжение темы использования SSE для вычисления скалярного произведения. Наибольшей производительности мне удалось достичь при использовании SSE в скалярном режиме, как это не парадоксально. Т.е. когда огромные SSE регистры используются для хранения и вычисления с всего-навсего с одним числом.
Для оценки времени выполнения я использовал, в отличии от Ивана (см. комментарии к предыдущему сообщению), другой метод. С помощь пары команд процессора RDTSC/RDTSCP я получаю количество тактов процессора, потребовавшихся на исполнение функции вычисления скалярного произведения. Для того, что бы избавится от случайностей, функция вычисляется 100 раз. Результаты сортируются, 10 наибольших и наименьших значений отбрасываются, а среднее считается по оставшимся 80 измерениям.
Первая команда делает засечку непосредственно в момент вызова, а вот вторая, если я правильно понял описание, ожидает завершения всех предшествующих ей команд и только после этого делает засечку времени.
Вот полученные результаты:
| AMD FX-4350 4.2 GHz (очень редко) | Intel Core i3 3227u 1.9 GHz | AMD FX-4350 4.2 GHz (обычно) | |
| 4 элементные векторы | |||
| SSE | 143 | 184 | 408 |
| SSE3 | 157 | 184 | 408 |
| SSE4 | 161 | 178 | 417 |
| Pure Pascal | 167 | 182 | 464 |
| FPU | 149 | 181 | 415 |
| Scalar SSE | 145 | 182 | 399 |
| Векторы произвольной длины | |||
| Pure Pascal | 216 | 154 | 628 |
| FPU | 164 | 143 | 432 |
| Scalar SSE | 151 | 165 | 408 |
Сначала про SSE. Анализ показывает, что скорость вычисления векторного произведения для чисел с плавающей точкой одинарной точности не зависит от выбранного способа решения задачи!
На AMD различия более заметны, но, тем не менее, практически не выходят за рамки погрешности метода.
С чем это связано, мне трудно сказать. Возможно, я сгенерировал не самый лучший код для SSE. Возможно, дело в самой задачи. Как заметил Иван в комментарии к прошлому посту, SSE по идее могут выполнять две инструкции за такт: сложение и умножение. Но в случае векторного произведения эти две операции жестко связаны: не выполнив умножение, нельзя посчитать сложение. Поэтому и не достигается максимальная производительность. Использование команды DPPS (входит в набор SSE4) не приводит к заметному улучшению производительности, причем на обоих платформах (на АМД даже медленнее чуть-чуть).
А теперь замечание о производительности платформ. Интел стабильно работает быстрее, если смотреть не абсолютную производительность, а количество тактов. Но все не так просто. Иногда, очень редко, АМД выдает производительность в тактах выше, чем Интел. Даже не могу предположить, с чем это связано. Возможно, в Интел более продвинутый конвейер, который не очищается даже в достаточно сложных случаях.
Переход от векторов фиксированного размера к векторам произвольной длины мало сказывается на скорости работы. Для сравнения векторы произвольной длины также брались длиной в 4 числа. В случае Интел был получен парадоксальный результат, который я также не могу пока объяснить: универсальный алгоритм работает быстрее, чем специализированный, хотя в специализированном вообще нет циклов.
В случае же АМД все предсказуемо. Но замедление не так значительно, так как целочисленные команды, команды FPU и команды SSE могут выполнятся параллельно и независимо друг от друга. Если, конечно, они не взаимозависимы по данным.
Пока что на основе приведенного исследования можно сделать следующий вывод: для чисел с плавающей точкой использование команд SSE на текущий момент не очень оправдано. По крайней мере при расчете векторного произведения.
Но не факт, что ситуация не изменится в будущем. Появление более мощных процессоров может сделать использование инструкций SSE более быстрым при решении этой задачи.
Что-то здесь не так :) Неужели SSE и новые AVX не нужны...
ОтветитьУдалитьЯ тут нашёл интересную статью: http://sci.tuomastonteri.fi/programming/sse и в ней рекомендуют положить данные так чтобы за раз обрабатывать 4 вектора (если они из float)
Можете попробовать изменить тест на: рассчитать скалярные произведения для 4-х пар векторов. При этом данные положить так: x1, x2, y1, y2, z1, z2, w1, w2: float[число векторов]. Понятно что одной инструкцией SSE4 тут произведение не по считаешь, но зато может быть простые умножить, сложить из SSE ускорят процесс.
Возможно, и так. Как-нибудь попробую, как будет время. Другое дело, что обеспечить универсальность для такого подхода сложно. Есть сомнение, что при переходе от такой конструкции к векторам произвольной длины удастся сохранить полученные преимущества в производительности. Также усложниться и структура самой программы, потому что параллельно умножать четыре вектора, как то сложно, особенно если их число не кратно четырем.
УдалитьНа самом деле, SSE может и давать существенное преимущество при выполнении более простых операций над регулярными структурами данных.
И что самое печальное. В SSE4 есть команда DPPS, которая одна честно выполняет скалярное произведение 4-элементных векторов. По идее, она должна быть максимально оптимизирована и выполняться максимально быстро. Но, тем не менее, даже она не дает особых преимуществ перед оптимизированным чисто скалярным кодом.
УдалитьВозможно, тут важно выравнивание данных. Которое для SSE оптимально по границам 16 байт. Однако в реальности достаточно сложно обеспечить выравнивание всех данных по таким границам, особенно при обработке больших массивов информации. Но даже такое выравнивание не может быть больше порядка 10% выигрыша к производительности, на мой взгляд.
Решил еще поковырять свой тест.
ОтветитьУдалитьТестирую вычисление 10M скалярных произведений из float[4]:
1. 100 уникальных пар векторов (все должно быть в кэше), 10K проходов
2. 10M уникальных пар векторов (в кэш явно не влезает), 1 проход.
Каждый тест запускаю также 100 раз и также отбрасываю 10 наиболее медленных и быстрых запусков, далее считаю среднее время в миллисекундах.
Алгоритмы:
1. LOOP_DOT: clang раскручивает цикл, использует xmmX регистры, но обрабатывает 1 float за раз
2. SSE_DOT: mulps, movhlps, addps, movshdup, addss
3. SSE_DOT4: данные расположены так что сначала идут все x, далее y и т.д., считаем 4 скалярных произведения за раз используя 4 mulps и 3 addps
4. SSE3_DOT: mulps, 2 haddps
5. SSE4_DOT: dpps
6. SSE4_DOT4: считаем 4 произведения за раз, 4 dpps (но с разными масками), плюс 3 addps
Процессоры & компилятор:
1. i7-4870HQ @2.50GHz (Q3'14), Apple LLVM version 7.0.2 (clang-700.1.81)
2. Xeon-W3565 @3.2GHz (Q4'09), Ubuntu clang version 3.7.1
Результаты (i7 / Xeon, ms, проценты относительно LOOP_DOT на i7 для 100 векторов):
A. 100 уникальных векторов:
1. LOOP_DOT: 15.3 (100%) / 26.5 (173%)
2. SSE_DOT: 10.5 (68%) / 15.0 (98%)
3. SSE_DOT4: 3.6 (23%) / 6.5 (42%)
4. SSE3_DOT: 12.5 (81%) / 15.3 (99%)
5. SSE4_DOT: 23.1 (151%) / 16.0 (104%)
6. SSE4_DOT4: 18.4 (120%) / 15.3 (99%)
B. 10M уникальных векторов:
1. LOOP_DOT: 29.8 (195%) / 36.9 (241%)
2. SSE_DOT: 28.0 (185%) / 34.0 (222%)
3. SSE_DOT4: 26.1 (170%) / 36.9 (241%)
4. SSE3_DOT: 28.7 (187%) / 34.3 (224%)
5. SSE4_DOT: 34.3 (224%) / 33.5 (219%)
6. SSE4_DOT4: 31.5 (205%) / 33.5 (219%)
Выводы:
1. dpps из SSE4 и haddps из SSE3 нигде не взлетели только они существуют :)
2. SSE_DOT4 оказался самым оптимальным, аккурат в 4 раза быстрее не SSE версии.
3. Современный мобильный i7 существенно выиграл 6-ти летний Xeon).
4. На стареньком Xeon dpps не просела так сильно как на современном i7. Интересно бы посмотреть как она ведет себя на новом Xeon.
5. Выигрыш практически полностью пропадает когда выходим за границы кэша, что вполне ожидаемо, может быть prefetch помог бы, но это уже другая история.
Программа и скрипт для запуска теста: https://www.dropbox.com/sh/glu980nrryqgb7t/AADDxpiUH1Hp1VVHGFPmBYjZa
Да, 4 произведения за раз, конечно, показывают отличную производительность. Но необходимо, что бы данные были организованы соответствующим образом. Иначе формирование "правильных" вектором может свести выигрыш в производительности к нулю.
УдалитьИ если в случае двухмерного wavelet-преобразования, по крайней мере, на первом проходе, готовые векторы уже и так имеются в наличии, и все, что нужно, это поменять порядок горизонтальных и вертикальных проходов, то в случае одномерного преобразования уже не так все просто. Надо тестить. Четырехкратное ускорение очень привлекательно.