Странный / плохой вывод сборки от gcc? - PullRequest
0 голосов
/ 25 августа 2018

У меня есть следующий код (минимальный пример):

#include <iostream>
#include <immintrin.h>

using namespace std;

int main(){
    __m128i a = _mm_set_epi32(rand(),rand(),rand(),rand());
    __m128i b = _mm_set_epi32(rand(),rand(),rand(),rand());
    __m128i c = _mm_add_epi32(a,b);
    int d[4];
    _mm_storeu_si128((__m128i*)d,c);
    cout<<d[0]<<endl;
    cout<<d[1]<<endl;
    cout<<d[2]<<endl;
    cout<<d[3]<<endl;
    return 0;
}

При компиляции с g++ -O3 -march=native он производит странную / плохую / неэффективную сборку (https://godbolt.org/z/TQgbim). Он сохраняет c один раз, а затем выполняет выравниваемую загрузку и извлечение для доступа к элементу (каждый раз). Я понимаю, почему он должен хранить его в памяти, и вижу, насколько эффективными могут быть выровненные загрузка и извлечение, но я не понимаю, почему ему нужно продолжать загружать одни и те же данные обратно в регистр xmm после Кроме того, когда d изменяется так, что он выделяется в куче (https://godbolt.org/z/Pk7qP2),, он даже не выполняет выравниваемые нагрузки, он просто обрабатывает d как обычный массив и обращается к элементам. таким образом. Может кто-нибудь объяснить, почему он это делает и какую возможную пользу он может принести? Спасибо.

1 Ответ

0 голосов
/ 25 августа 2018

Да, это забавная пропущенная оптимизация.

Похоже, что было решено оптимизировать векторное хранилище / скалярную перезагрузку в векторный экстракт, что обычно хорошо.

Но это было сделано безпринять во внимание соглашение о вызовах, в котором нет сохраняемых вызовов векторных регистров.Этот код был бы хорош в Windows x64, где он мог бы использовать, например, xmm6.

Этот код также был бы в порядке, если бы вы вызывали функции, которые встроены, или если вы передали все 4 элемента в качестве аргументов вта же функция.(например, printf).

GCC имеет несколько проходов, и не зависящие от архитектуры средние проходы, работающие на GIMPLE-представлении логики программы, иногда не могут воспользоваться полными деталями, которые не известны до регистрациивремя распределения.Некоторые оптимизации трудны для gcc, потому что они просто не могут видеть их.


Кстати, если вы заботитесь об этом уровне эффективности, используйте '\n' вместо endl.Вам не нужно явно очищать cout там.

...