2010-06-29 15 views
8

Wpadłem w ciekawy problem. Algorytm pracuję nad składa się z wielu obliczeń, jak to32-bitowe i 64-bitowe obliczenia zmiennoprzecinkowe

q = x(0)*y(0)*z(0) + x(1)*y(1)*z(1) + ... 

gdzie długość sumowania jest między 4 a 7.

Oryginalne obliczenia są wykonywane przy użyciu 64-bitową precyzją. Do eksperymentów próbowałem używać 32-bitowej precyzji dla wartości wejściowych x, y, z (tak, że obliczenia są wykonywane przy użyciu 32-bitów), a przechowywanie końcowego wyniku jako wartości 64-bitowej (rzutowanie proste).

Oczekiwano, że wydajność 32-bitowa będzie lepsza (rozmiar pamięci podręcznej, rozmiar SIMD itd.), Ale ku mojemu zaskoczeniu nie było różnicy w wydajności, a może nawet spadku.

Omawiana architektura to Intel 64, Linux i GCC. Oba kody wydają się używać SSE, a tablice w obu przypadkach są wyrównane do granicy 16 bajtów.

Dlaczego tak się stanie? Domyślam się, że 32-bitowa precyzja może wykorzystywać SSE tylko na pierwszych czterech elementach, a reszta jest wykonywana szeregowo przez rzutowanie rzutów.

+0

Dodałeś nagrodę - co Ci się nie podobało w odpowiedzi na pytanie dotyczące dsimcha? Warto również wypróbować najnowszą wersję GCC lub kompilatora Intela http://software.intel.com/en-us/articles/non-commercial-software-download/, aby sprawdzić, czy wykonują one lepszą pracę kompilującą/wektoryzującą . – Rup

+0

@Rup Podoba mi się jego odpowiedź, niemniej jednak chciałbym również innych opinii, więc postawiłem nagrodę – Anycorn

Odpowiedz

24

Przynajmniej na x87 wszystko dzieje się w wewnętrznej 80-bitowej precyzji. Precyzja tak naprawdę określa, ile z tych bitów jest przechowywanych w pamięci. Jest to po części powodem, dla którego różne ustawienia optymalizacji mogą nieznacznie zmienić wyniki: zmieniają one wartość zaokrąglania z 80-bitowego na 32- lub 64-bitowy.

W praktyce użycie 80-bitowego zmiennoprzecinkowego (long double w C i C++, real w D) jest zwykle wolne, ponieważ nie ma wydajnego sposobu na załadowanie i przechowywanie 80 bitów z pamięci. 32- i 64-bitowe są zwykle równie szybkie, pod warunkiem, że przepustowość pamięci nie jest wąskim gardłem, czyli jeśli wszystko jest w pamięci podręcznej. 64-bit może być wolniejszy, jeśli wystąpi jedno z poniższych zdarzeń:

  1. Szerokość pasma pamięci jest wąskim gardłem.
  2. Numery 64-bitowe nie są poprawnie wyrównane na 8-bajtowych granicach. Numery 32-bitowe wymagają tylko 4-bajtowego wyrównania dla optymalnej wydajności, więc są mniej wybredne. Niektóre kompilatory (kompilator Digital Mars D przychodzi na myśl) nie zawsze są odpowiednie dla 64-bitowych plików podwójnych przechowywanych na stosie. Powoduje to dwukrotne zwiększenie ilości operacji pamięciowych koniecznych do załadowania jednego, w praktyce daje około 2x wydajność w porównaniu do poprawnie wyrównanych 64-bitowych spławików lub 32-bitowych pływaków.

Jeśli chodzi o optymalizacje SIMD, należy zauważyć, że większość kompilatorów jest okropna w auto-wektoryzacji kodu. Jeśli nie chcesz pisać bezpośrednio w języku asemblerowym, najlepszym sposobem skorzystania z tych instrukcji jest użycie takich operacji jak macierz, które są dostępne na przykład w D i implementowane w zakresie instrukcji SSE. Podobnie, w C lub C++, prawdopodobnie będziesz chciał użyć biblioteki wysokiego poziomu funkcji, które są zoptymalizowane pod kątem SSE, ale nie znam dobrego z góry mojej głowy, ponieważ głównie programuję w D.

+4

"x87" - Nieco lepiej niż te stare procesory x86. :-) – Thanatos

+4

http://pl.wikipedia.org/wiki/X87 – Adam

0

Jest tak prawdopodobnie dlatego, że procesor nadal wykonuje 64-bitowe zliczanie, a następnie przycina liczbę. Była jakaś flaga CPU, którą można zmienić, ale nie pamiętam ...

0

Najpierw sprawdź ASM, który zostanie wyprodukowany. Może nie być tym, czego oczekujesz.

spróbować także pisanie go jako pętla:

typedef float fp; 
fp q = 0 
for(int i = 0; i < N; i++) 
    q += x[i]*y[i]*z[i] 

Niektóre kompilator może zauważyć pętli i nie forma unrolled.

Wreszcie, twój kod używał () zamiast []. Jeśli twój kod wykonuje wiele wywołań funkcji (od 12 do 21), to spowolni koszt FP, a nawet usunięcie wszystkich obliczeń fp nie zrobi dużej różnicy. Zainstalowanie OTOH może.

+0

dziękuję, faktycznie 'q()' to makra konwertujące bezpośrednio do dostępu do surowego wskaźnika – Anycorn

+0

@aaa: Cóż, jeśli w ogóle jest jakaś matematyka, to może nadal być dużym procentem. Nie wiem też, jak dobrze radzi sobie kompilator z miksowaniem FP i innych rzeczy. To może wystarczyć, by zablokować korzystanie z ops wektorów. – BCS

Powiązane problemy