Dlaczego inlining kompilatora generuje wolniejszy Kod niż inlining ręczny?

Tło

[10]}następująca pętla krytyczna kawałka oprogramowania numerycznego, napisanego w C++, zasadniczo porównuje dwa obiekty przez jednego z ich członków: [11]}
for(int j=n;--j>0;)
    asd[j%16]=a.e<b.e;

a i {[6] } należą do klasy ASD:

struct ASD  {
    float e;
    ...
};

Badałem efekt umieszczenia tego porównania w funkcji członu lekkiego:

bool test(const ASD& y)const {
    return e<y.e;
}

I używanie go w ten sposób:

for(int j=n;--j>0;)
    asd[j%16]=a.test(b);

Kompilator wprowadza tę funkcję, ale problem polega na tym, że kod złożenia będzie inny i powodują >10% nadmiarowości czasu pracy. Mam pytanie:

Pytania

  1. Dlaczego kompilator prodrucinguje inny kod asemblera?

  2. Dlaczego produkowany montaż jest wolniejszy?

EDIT: na drugie pytanie odpowiedziała sugestia @KamyarSouri (j%16). Kod złożenia wygląda teraz prawie identycznie (zobacz http://pastebin.com/diff.php?i=yqXedtPm ). jedyne różnice to linie 18, 33, 48:

000646F9  movzx       edx,dl 

Materiał

[[10]} Ten wykres pokazuje FLOP/S (do skalowania factor) za 50 testów mojego kodu.

Tutaj wpisz opis obrazka

Skrypt gnuplot do generowania wykresu: http://pastebin.com/8amNqya7

Opcje Kompilatora:

/Zi /W3 /WX- /MP /Ox /Ob2 /Oi /Ot /Oy /gl /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /MT /GS- /Gy /arch:SSE2 /fp:precise /Zc:wchar_t /ZC:forScope /Gd /analyze-

Opcje Linkera: / INCREMENTAL: NO " kernel32.lib "" user32.lib "" gdi32.lib "" winspool.lib" "comdlg32lib "" advapi32.lib "" shell32.lib "" ole32.lib "" oleaut32.lib "" uuid.lib "" odbc32.lib "" odbccp32.w związku z tym, że nie jest to możliwe, nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe.]}

Author: Mysticial, 2011-12-21

2 answers

Krótka Odpowiedź:

Twoja asd Tablica jest zadeklarowana jako:

int *asd=new int[16];

Dlatego użyj int jako typu zwracanego, a nie bool.
Alternatywnie Zmień typ tablicy na bool.

W każdym przypadku upewnij się, że typ zwracanej funkcji test odpowiada typowi tablicy.

Przejdź do dołu, aby uzyskać więcej szczegółów.

Długa Odpowiedź:

W wersji ręcznie inlined, "rdzeń" jednej iteracji wygląda jak to:

xor         eax,eax  

mov         edx,ecx  
and         edx,0Fh  
mov         dword ptr [ebp+edx*4],eax  
mov         eax,dword ptr [esp+1Ch]  
movss       xmm0,dword ptr [eax]  
movss       xmm1,dword ptr [edi]  
cvtps2pd    xmm0,xmm0  
cvtps2pd    xmm1,xmm1  
comisd      xmm1,xmm0  

Kompilator w wersji jest całkowicie identyczny z wyjątkiem pierwszej instrukcji.

Gdzie zamiast:

xor         eax,eax

Ma:

xor         eax,eax  
movzx       edx,al

Ok, więc to jest jedna dodatkowa Instrukcja. Oboje robią to samo-zerują rejestr. To jedyna różnica, jaką widzę...

Instrukcja movzx ma jednokrotne opóźnienie i obiegową przepustowość cyklu 0.33 na wszystkich nowszych architekturach. Więc nie mogę sobie wyobrazić, jak to mogło zrób 10% różnicy.

W obu przypadkach wynik zerowania jest używany tylko 3 Instrukcje później. Więc jest bardzo możliwe, że to może być na krytycznej ścieżce egzekucji.


Chociaż nie jestem inżynierem Intela, oto moje przypuszczenie:

Większość nowoczesnych procesorów zajmuje się operacjami zerowania (takimi jak xor eax,eax) poprzezzmianę nazwy rejestru NA bank rejestrów zerowych. Całkowicie omija jednostki wykonawcze. Jest jednak możliwe, że to Specjalna obsługa może spowodować powstanie bańki rurociągu, gdy dostęp do (częściowego) rejestru jest możliwy poprzez movzx edi,al.

Ponadto istnieje również false zależność odeax w kompilatorze w wersji inlined:

movzx       edx,al  
mov         eax,ecx  //  False dependency on "eax".

To, czyout-of-order execution jest w stanie rozwiązać ten problem, nie mam pojęcia.


OK, to w zasadzie zmienia się w kwestię inżynierii wstecznej kompilatora MSVC...

Tutaj wyjaśnię dlaczego to dodatkowe movzx jest generowane, jak również dlaczego pozostaje.

Kluczem jest tutaj wartość zwracana bool. Najwidoczniej, bool typy danych są prawdopodobnie zapisywane jako 8-bitowe wartości wewnątrz wewnętrznej reprezentacji MSVC. Dlatego, gdy w domyśle przekształcisz z bool na int tutaj:

asd[j%16] = a.test(b);
^^^^^^^^^   ^^^^^^^^^
 type int   type bool

Istnieje promocja 8-bitowa -> 32-bitowa liczba całkowita. Z tego powodu MSVC generuje instrukcję movzx.

Gdy inlining jest wykonywany ręcznie, kompilator ma wystarczająco dużo informacji, aby zoptymalizuj tę konwersję i zachowaj wszystko jako 32-bitowy typ danych IR.

Jednakże, gdy kod jest umieszczony w jego własnej funkcji z wartością zwracaną bool, kompilator nie jest w stanie zoptymalizować 8-bitowego pośredniego typu danych. Dlatego movzx zostaje.

Gdy oba typy danych są takie same (int lub bool), konwersja nie jest potrzebna. W związku z tym problem jest całkowicie unikany.

 31
Author: Mysticial,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-21 03:39:33

lea esp,[esp] zajmuje 7 bajtów i-cache i znajduje się wewnątrz pętli. Kilka innych wskazówek sprawia, że wygląda na to, że kompilator nie jest pewien, czy jest to kompilacja release czy debug.

Edit:

lea esp,[esp] nie jest w pętli. Pozycja wśród otaczających mnie instrukcji wprowadzała mnie w błąd. Teraz wygląda na to, że celowo zmarnował 7 bajtów, a następnie kolejne zmarnowane 2 bajty, aby rozpocząć rzeczywistą pętlę na granicy 16 bajtów. Co oznacza, że to faktycznie przyspiesza, jak widać Autor: Johennes Gerer

Kompilator nadal wydaje się być niepewny, czy jest to kompilacja debugowa czy release.

Kolejna edycja:

Pastebin diff różni się od Pastebin diff, które widziałem wcześniej. Ta odpowiedź może być teraz usunięta, ale ma już komentarze, więc ją zostawię.

 1
Author: Windows programmer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-21 02:12:40