Как заставить gcc использовать все регистры SSE (или AVX)? - PullRequest
9 голосов
/ 11 мая 2011

Я пытаюсь написать некоторый вычислительный код для Windows x64 target с SSE или новыми инструкциями AVX, компилируя в GCC 4.5.2 и 4.6.1, MinGW64 (сборка TDM GCC и некоторая пользовательская сборка). Мои параметры компилятора -O3 -mavx. (-m64 подразумевается)

Короче говоря, я хочу выполнить несколько длинных вычислений для 4-х трехмерных векторов упакованных чисел с плавающей точкой. Это требует 4x3 = 12 xmm или ymm регистров для хранения и 2 или 3 регистра для временных результатов. Это должно ИМХО плотно вписаться в 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 из 3 __m128, с естественным сложением и умножением на скаляр. Когда строка a2 -= v закомментирована, т. Е. Нам нужно только 3х3 регистра для хранения, так как мы игнорируем a2, полученный код действительно прост и не перемещается, все выполняется в регистрах xmm0-xmm10. Когда я удаляю комментарий a2 -= v, код довольно ужасный с большим количеством перетасовок между регистрами и стеком. Даже если компилятор может просто использовать регистры xmm11-xmm13 или что-то в этом роде.

На самом деле я еще не видел, чтобы GCC нигде не использовал ни одного из регистров xmm11-xmm15 во всем моем коде. Что я делаю неправильно? Я понимаю, что это регистры, сохраненные вызываемым пользователем, но эти издержки полностью оправдываются упрощением кода цикла.

Ответы [ 2 ]

13 голосов
/ 11 мая 2011

Два очка:

  • Во-первых, вы делаете много предположений. Распределение регистров довольно дешево на процессорах x86 (из-за быстрых кэшей L1 и теневого копирования регистров и других хитростей), а доступ к 64-битным регистрам является более дорогостоящим (с точки зрения больших инструкций), так что это может быть просто версия GCC так быстро или быстрее, чем вы хотите.
  • Во-вторых, GCC, как и любой компилятор, делает наилучшее распределение регистров, которое он может . Нет опции «пожалуйста, сделайте лучше регистр распределения», потому что если бы она была, она всегда была бы включена. Компилятор не пытается вас злить. (Как я помню, распределение регистров является полной проблемой NP, поэтому компилятор никогда не сможет сгенерировать идеальное решение. Лучшее, что он может сделать, - это приблизиться)

Итак, если вы хотите лучше распределить регистры, у вас есть два варианта:

  • написать лучший распределитель регистров и вставить его в GCC, или
  • Обход GCC и перезапись функции в сборке, чтобы вы могли точно контролировать, какие регистры используются, когда.
5 голосов
/ 18 июля 2014

На самом деле, вы видите, что это не разливы, это gcc, работающий с a1 и a2 в памяти, потому что он не может знать, есть ли у них псевдонимы. Если вы объявите последние два параметра как vect<__m128>& __restrict__, GCC может и зарегистрирует выделить a1 и a2.

...