Как заставить gcc использовать все регистры SSE (или AVX)?
я пытаюсь написать некоторый вычислительно интенсивный код для цели Windows x64, с SSE или новыми инструкциями AVX, компилируя в GCC 4.5.2 и 4.6.1, MinGW64 (TDM GCC build и некоторая пользовательская сборка). Мои параметры компилятора -O3 -mavx
. (-m64
подразумевается)
короче говоря, я хочу выполнить некоторые длительные вычисления на 4 3D векторах упакованных поплавков. Для этого требуется 4x3=12 XMM или ymm регистров для хранения и 2 или 3 регистра для временных результатов. Это должно соответствовать IMHO плотно в 16 доступных регистрах SSE (или AVX), доступных для 64-битных целей. Однако GCC производит очень неоптимальный код с регистровым разливом, используя только регистры xmm0-xmm10
и перетасовка данных из и в стек. Мой вопрос:
есть ли способ убедить GCC использовать все регистры xmm0-xmm15
?
чтобы исправить идеи, рассмотрим следующий код SSE (только для иллюстрации):
void example(vect<__m128> q1, vect<__m128> q2, vect<__m128>& a1, vect<__m128>& a2) {
for (int i=0; i < 10; i++) {
vect<__m128> v = q2 - q1;
a1 += v;
// a2 -= v;
q2 *= _mm_set1_ps(2.);
}
}
здесь vect<__m128>
это просто struct
of 3 __m128
, С естественным сложением и умножением на скалярное. Когда линия a2 -= v
закомментировано, т. е. нам нужны только регистры 3x3 для хранения, так как мы игнорируем a2
, произведенный код действительно прост без ходов, все выполняется в регистрах xmm0-xmm10
. Когда я удаляю комментарий a2 -= v
, код довольно ужасен с большим количеством перетасовки между регистрами и стеком. Хотя компилятор может просто использовать registers xmm11-xmm13
или что-то в этом роде.
I на самом деле не видел GCC использовать любой из регистров xmm11-xmm15
везде во всех моих код. Что я делаю не так? Я понимаю, что они являются регистрами, сохраненными вызываемым абонентом, но эти накладные расходы полностью оправданы упрощением кода цикла.
2 ответов
две точки:
- во-первых, вы делаете слишком много предположений. Регистровый разлив довольно дешев на процессорах x86 (из-за быстрых кэшей L1 и затенения регистра и других трюков), а 64-разрядные только регистры более дороги для доступа (с точки зрения больших инструкций), поэтому может быть, что версия GCC так же быстра или быстрее, чем та, которую вы хотите.
- во-вторых, GCC, как и любой компилятор, делает лучшее распределение регистра это может. Нет - пожалуйста, сделай!--6-->лучше register allocation", потому что если бы он был, он всегда был бы включен. Компилятор не пытается досадить вам. (Распределение регистров-это NP-полная проблема, насколько я помню, поэтому компилятор никогда не сможет создать идеальное решение. Лучшее, что он может сделать, это приблизиться)
Итак, если вы хотите лучше зарегистрировать распределение, у вас в основном есть два варианта:
- напишите лучший распределитель регистра и исправьте его в GCC, или
- обход GCC и переписать функцию в сборке, так что вы можете точно контролировать, какие регистры используются, когда.
на самом деле, то, что вы видите, не разливы, это gcc, работающий на a1 и a2 в памяти, потому что он не может знать, являются ли они псевдонимами. Если вы объявите последние два параметра как vect<__m128>& __restrict__
GCC может и зарегистрирует выделение a1 и a2.