2010-06-02 2 views
2

Я пытался выяснить, как получить некоторое улучшение в моем коде на очень важных пару линий:Помощь с Assembly/SSE Умножение

float x = a*b; 
float y = c*d; 
float z = e*f; 
float w = g*h; 

все а, б, в ... являются поплавки ,

Я решил изучить SSE, но, похоже, не может найти улучшения, на самом деле он оказывается в два раза медленнее. Мой SSE код:

Vector4 abcd, efgh, result; 
abcd = [float a, float b, float c, float d]; 
efgh = [float e, float f, float g, float h]; 
_asm { 
movups xmm1, abcd 
movups xmm2, efgh 
mulps xmm1, xmm2 
movups result, xmm1 
} 

Я также попытался с помощью стандартных ассемблерного, но это не кажется, что я могу упаковать регистр с четырьмя плавающими точками, как я могу с SSE.

Любые комментарии или помощь были бы очень благодарны, мне в основном нужно понять, почему мои вычисления с использованием SSE медленнее, чем серийный код на C++?

Я компилирую в Visual Studio 2005 на Windows XP, используя Pentium 4 с HT, если это предоставляет любую дополнительную информацию для поимки.

Заранее спасибо!

+0

Я думаю, вам нужно предоставить больше контекста. Простое умножение четырех пар поплавков займет практически измеримое время на любом современном ПК. Это в цикле? Сохраняете ли вы результаты где-то или используете их в качестве промежуточных элементов для следующей итерации? –

+0

Я понимаю, что наибольшая выгода от SSE заключалась бы в том, чтобы делать много итераций, каждый раз упаковывая регистр, но все, что я планирую делать, - это использование чисел, генерируемых из этого, только что будет возвращено в некоторые вызовы сложения и вычитания, ничего, что я хотел бы включить в код SSE, но любое улучшение времени вычисления приведет к значительной экономии времени на протяжении всего срока службы кода. – Brett

+0

Это не обязательно так. Если это не в цикле, то любые преимущества будут совершенно незаметны в любом данном проходе через код. Конечно, если программное обеспечение используется в течение нескольких тысяч лет, общее время сохранения может быть значимым, но это действительно так. Не переустраивайте, сегодняшние компиляторы очень хороши. Если он работает слишком медленно, сначала профиль, затем оптимизируйте узкие места. – Donnie

ответ

3

Как вы узнали, просто замена нескольких инструкций SSE не будет работать, потому что вы должны перетасовывать данные в памяти, чтобы правильно загружать регистры SSE, и эти движущиеся данные в памяти (бит, который создает массивы) собирается убить вашу производительность, так как память очень медленная (жесткий диск в стороне, память всегда является узким местом в наши дни).

Кроме того, нет возможности переместить данные между SSE и FPU/ALU без использования записи в ОЗУ с последующим чтением. Современные чипы IA32 хорошо справляются с этим конкретным шаблоном (пишите, затем читайте), но все равно недействительны для кеша, который будет иметь эффект стука.

Чтобы получить максимальную отдачу от SSE, вам нужно посмотреть на весь алгоритм и данные, которые использует алгоритм. Значения a, b, c и d и e, f, g и h должны постоянно находиться в этих массивах, чтобы во время загрузки регистров SSE не было сдвинутых данных в памяти. Это не просто и может потребовать много повторного использования вашего кода и данных (вам может потребоваться хранить данные по-другому на диске).

Возможно, стоит упомянуть, что SSE является 32-разрядным (или 64-битным, если вы используете удваиваете), тогда как FPU составляет 80 бит (независимо от поплавка или двойной), поэтому при использовании SSE вы получите несколько разные результаты по сравнению с использованием FPU. Только вы знаете, если это будет проблемой.

+0

Из того, что я понимаю о вашем ответе, мне кажется, что я должен только попытаться использовать внутренности, если я могу использовать их для нескольких вычислений, правильно ли это? И аргументация в этом заключается в том, что я не очень эффективен в перемещении данных самостоятельно? Я не могу хранить значения a, b, c и d и e, f, g и h в этих массивах постоянно, так как они должны загружать текущие значения для каждого вычисления, поэтому мне было бы трудно увидеть пособие? Спасибо за любую помощь! – Brett

+0

@Brett: Да, это в основном это. Вам нужно держать все в SSE, чтобы получить выгоду. Есть немного подсказки в названии SSE - Streaming SIMD Extensions. Просто из любопытства, откуда берутся эти ценности, то есть какая картина? – Skizz

+0

Итак, большая картина состоит в том, что на самом деле это часть матрицы вращения, но я делаю одну матрицу вращения на итерацию через большой цикл, где я сравниваю векторы признаков. Из-за структуры я не вижу предсказуемого способа одновременного соединения нескольких SSE-вычислений, но при условии даже малейшего преимущества определенно приведет к значительному улучшению среды выполнения моей программы. Альтернативно, b, d, f, h представляют значения sin и cos, которые вычисляются на этапе инициализации, и в настоящее время я перехожу к хранению в выровненных блоках для более быстрого умножения. Thx за вашу помощь! – Brett

1

Вы можете включить использование SSE и SSE2 в параметрах программы в новых версиях VS и, возможно, в 2005 году. Скомпилировать с использованием экспресс-версии?

Кроме того, ваш код в SSE, вероятно, медленнее, потому что, когда вы компилируете серийный C++, компилятор умный и делает очень хорошую работу по его очень быстрой настройке, например, автоматически помещая их в нужные регистры в нужное время , Если операции происходят последовательно, компилятор может уменьшить влияние кэширования и подкачки, например. Однако встроенный ассемблер может быть оптимизирован в лучшем случае, и его следует избегать, когда это возможно.

Кроме того, вам необходимо будет выполнить ОГРОМНЫЙ объем работы для SSE/2, чтобы принести заметную пользу.

+0

Я думаю, что меня все еще смущает тот факт, что у меня есть некоторый код SSE/2 (у меня было многие версии кода, вставленные выше), и его фактически пошли медленнее, чем мой серийный код. Достаточно, чтобы моя ~ 10-секундная программа (написанная полностью серийно) затем занимает ~ 11,5 секунд (только с этой операцией в SSE/2) – Brett

+0

Компилятор, научитесь любить его. : P – Puppy

3

Вы используете нестандартные инструкции, которые очень медленные. Возможно, вы захотите выполнить правильное выравнивание данных, 16-байтовую границу и использование перемещений. Лучшей альтернативой является использование встроенных функций, а не сборка, потому что тогда компилятор может свободно заказывать инструкции, как представляется необходимым.

+0

Итак, я тестировал то, что, как я думаю, вы говорите, используя команду movups для хранения значений, выровненных в регистре, а затем используйте movaps для имитации выровненных данных и, наконец, быстрее, чем серийный код C++ так долго поскольку я запускаю свой таймер после выравнивания данных. Если я всегда начинаю с неуравновешенных данных, было бы ли для меня смысл не видеть выгоды от SSE/ASM? – Brett

1

Это старая тема, но я заметил ошибку в вашем примере. Если вы хотите, чтобы выполнить это:

float x = a*b; 
float y = c*d; 
float z = e*f; 
float w = g*h; 

Затем код должен быть таким:

Vector4 aceg, bdfh, result; // xyzw 
abcd = [float a, float c, float e, float g]; 
efgh = [float b, float d, float f, float h]; 
_asm { 
movups xmm1, abcd 
movups xmm2, efgh 
mulps xmm1, xmm2 
movups result, xmm1 
} 

И получить даже немного больше скорости, я бы предположить, что вы не используете отдельный регистр для «результата».

Для начала не все алгоритмы будут полезны при перезаписи в SSE. Алгоритмы, управляемые данными (например, алгоритмы, управляемые поисковыми таблицами) не очень хорошо переходят в SSE, потому что много времени теряется при упаковке и распаковке данных в векторы для SSE.

Надеюсь, это все еще помогает.

0

Во-первых, если у вас есть что-то 128 бит (16 байт), вы должны использовать MOVAPS, поскольку это может быть намного быстрее. Компилятор обычно должен давать вам выравнивание по 16 байт, даже на 32-битных системах.

Ваши линии C/C++ не делают то же, что и ваш sse-код.

Четыре поплавка в одном регистре xmm умножаются на четыре поплавка в другом регистре. Давать вам:

float x = a*e; 
float y = b*f; 
float z = c*g; 
float w = d*h; 

В SSE1 вы должны использовать SHUFPS, чтобы изменить порядок поплавков в обоих регистрах, прежде чем умножения.

Также для обработки данных, которые больше, чем кэш-памяти процессора, вы можете использовать невременные хранилища (MOVNTPS) для уменьшения загрязнения кэша. Обратите внимание, что в других случаях не временные хранилища намного медленнее.

 Смежные вопросы

  • Нет связанных вопросов^_^