Как да принудя gcc да използва всички SSE (или AVX) регистри?

Опитвам се да напиша някакъв код с интензивни изчисления за цел на Windows x64, със SSE или новите инструкции на AVX, компилиран в GCC 4.5.2 и 4.6.1, MinGW64 (TDM GCC компилация и някои персонализирани компилации). Моите опции за компилатор са -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 от 3 __m128, с естествено събиране и умножение по скалар. Когато редът a2 -= v е коментиран, т.е. имаме нужда само от 3x3 регистъра за съхранение, тъй като игнорираме a2, произведеният код наистина е ясен без движения, всичко се изпълнява в регистри xmm0-xmm10. Когато премахна коментара a2 -= v, кодът е доста ужасен с много разбъркване между регистри и стек. Въпреки че компилаторът може просто да използва регистри xmm11-xmm13 или нещо подобно.

Всъщност все още не съм виждал GCC да използва някой от регистрите xmm11-xmm15 никъде в целия ми код. какво правя грешно Разбирам, че те са записани от извиквания регистри, но това допълнително натоварване е напълно оправдано чрез опростяване на кода на цикъла.


person Norbert P.    schedule 11.05.2011    source източник


Отговори (2)


Две точки:

  • Първо, правите много предположения. Прехвърлянето на регистър е доста евтино на x86 CPU (поради бързите L1 кешове и засенчването на регистъра и други трикове), а само 64-битовите регистри са по-скъпи за достъп (от гледна точка на по-големи инструкции), така че може да се окаже, че версията на GCC е толкова бърз или по-бърз от този, който искате.
  • Второ, GCC, като всеки компилатор, прави най-доброто разпределение на регистъра, което може. Няма опция „моля, направете по-добре регистрирайте разпределението“, защото ако имаше, винаги щеше да е активирана. Компилаторът не се опитва да те обиди. (Доколкото си спомням, разпределянето на регистър е NP-пълен проблем, така че компилаторът никога няма да може да генерира перфектно решение. Най-доброто, което може да направи, е да приближи)

Така че, ако искате по-добро разпределение на регистъра, основно имате две възможности:

  • напишете по-добър разпределител на регистър и го закърпете в GCC, или
  • заобиколете GCC и пренапишете функцията в асемблиране, така че да можете да контролирате точно кои регистри да се използват кога.
person jalf    schedule 11.05.2011
comment
Всъщност има още една опция, която трябваше да спомена: поиграйте с вашия код, за да го направите по-разбираем за GCC и неговия разпределител на регистри. Декларирането на нови променливи вместо повторното използване на стари може да помогне, минимизирането на обхвата и продължителността на живота на всяка променлива, както и обикновеното експериментиране напред-назад може да ви помогне да убедите GCC да създаде различен код - person jalf; 11.05.2011
comment
Благодаря за бързия отговор. Прав си, че предполагах твърде много. Оказва се, че компилаторът произвежда желания код, ако съхраня a1 и a2 във временни локални променливи за продължителността на изчислението. По някаква причина това не беше проблем за компилатора, когато a2 -= v беше коментиран. Не знам защо. Що се отнася до дължината на инструкцията, новото VEX кодиране прави достъпа до всички 16 регистъра еквивалентен. - person Norbert P.; 11.05.2011
comment
Оптималното разпределение на регистъра (без разливане/запълване) е линейно O(N) за SSA-form IR, използвайки или SEO, или PEO. Не мога да си спомня какъв е текущият най-добър резултат за разпределение с разливане/запълване. - person thechao; 28.03.2012
comment
Ами да, когато говоря за разпределение на регистър, имам предвид проблема като цяло, а не само няколкото специални случая, които са лесни за справяне. - person jalf; 28.03.2012
comment
@jalf Повече четене показва, че крайната фаза на разливане е полиномна, въпреки че нещо като O(V^3). Не съм сигурен какво имате предвид под „няколко специални случая“, всички програми могат да бъдат представени в SSA-форма и всички програми с SSA-форма могат да бъдат оцветени с k в линейно време, използвайки SEO или PEO. - person thechao; 30.03.2012
comment
@thechao: Имам предвид точно това, което казах. В общия случай оптималното (както при перфектното) разпределение на регистъра е NP-пълен проблем. Има много случаи, в които е по-лесно, но като цяло е доказано, че е NP-пълно. Което означава, че ако можете да го направите в линейно време, тогава сте решили цяла група неприятни проблеми, които най-умните глави на планетата не са успели да направят. Което е доста хубаво. (Не съм проверил дали наградата за хилядолетието все още е в сила, но ако е така, побързайте и говорете с тях. Имат няколко милиона долара, които чакат човека, който решава този проблем. ;) - person jalf; 30.03.2012
comment
@jalf Звучи сякаш не сте запознат с модерния дизайн на компилатора. Ето следните документи и презентации за O(N) оптимално разпределение на регистъра за общия случай: cs.cmu.edu/afs/cs/academic/class/15745-s07/www/papers/ docstoc.com/docs/11350331/ Разбирам, че оцветяването на графиките може да бъде намалена до разпределение на регистъра, когато RA се поставя като проблем с обща графика. Програмите обаче не са общи графики и като такива са оптимално оцветени в O(N). Това е сравнително нов резултат. - person thechao; 12.04.2012
comment
@thechao Това не е изводът от това изследване. Можете да преобразувате всяка програма в SSA и да направите оптимално разпределение на регистър на SSA в линейно време, но това не означава, че сте направили оптимално разпределение на регистър за вашата оригинална програма. SSA въвежда повече променливи, за да опрости структурата на графиката. С тази по-проста структура можете оптимално да разпределите регистри, но това е оптимално само за тази по-проста структура с по-голям брой променливи; крайният резултат не е оптимално разпределение за първоначалния проблем. Прочетете презентациите, към които сте направили връзка; казват точно това! - person Brian Campbell; 17.11.2012
comment
Благодаря и на двама ви. @thechao прав си, не знаех за тези документи. Благодаря, че ми обърнахте внимание. Много интересни неща. Но както Браян казва, все още има няколко предупреждения. Въпреки това се поправям. :) - person jalf; 17.11.2012

Всъщност това, което виждате, не са разливи, а gcc, работещ върху a1 и a2 в паметта, защото не може да знае дали те са псевдоними. Ако декларирате последните два параметъра като vect<__m128>& __restrict__ GCC може и ще регистрира разпределяне на a1 и a2.

person user511824    schedule 18.07.2014