Вопрос

Я пытался выяснить, как добиться некоторого улучшения в моем коде на очень важных линиях пары:

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

Все A, B, C ... плавания.

Я решил изучить использование 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, если это предоставляет какую -либо дополнительную информацию для ассистения.

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

Это было полезно?

Решение

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

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

Чтобы получить максимальную отдачу от SSE, вам нужно посмотреть на весь алгоритм и данные, которые алгоритм использует. Значения A, B, C и D и E, F, G и H должны постоянно в этих массивах, чтобы перед загрузкой регистров SSE не было никаких изменений в памяти. Это не просто и может потребовать много переработки вашего кода и данных (вам может потребоваться по -разному хранить данные на диске).

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

Другие советы

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

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

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

Кроме того, вам придется выполнить огромное количество работы для SSE/2, чтобы принести заметную выгоду.

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

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.

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

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

Ваши строки C/C ++ не делают то же самое, что и ваш код SSE.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top