Float vs Double Performance

91

Zrobiłem kilka testów czasowych, a także przeczytałem kilka artykułów, takich jak ten (ostatni komentarz) i wygląda na to, że w wersji Release build, float i double wartości zajmują taki sam czas przetwarzania.

Jak to jest możliwe? Kiedy liczba zmiennoprzecinkowa jest mniej dokładna i mniejsza w porównaniu do wartości podwójnych, w jaki sposób CLR może podwoić się w tym samym czasie przetwarzania?

Joan Venge
źródło
10
Nie sądzę, że jest to dokładny duplikat, ponieważ ten pyta o przyczynę, gdzie inny użytkownik pyta, czy jest rzeczywiście szybszy, ale niekoniecznie dlaczego,
Joan Venge
Podobno dokładny duplikat Are podwaja się szybciej niż zmienno w C #? (zgłoszony w 2009 roku przez innego użytkownika).
Peter Mortensen

Odpowiedzi:

156

Przynajmniej na procesorach x86 floati doublekażdy z nich zostanie przekonwertowany na 10-bajtową wartość rzeczywistą przez FPU w celu przetworzenia. Jednostka FPU nie ma oddzielnych jednostek przetwarzania dla różnych obsługiwanych typów zmiennoprzecinkowych.

Odwieczna rada, która floatjest szybsza niż doublestosowana 100 lat temu, kiedy większość procesorów nie miała wbudowanych jednostek FPU (i niewiele osób miało oddzielne układy FPU), więc większość operacji zmiennoprzecinkowych była wykonywana w oprogramowaniu. Na tych maszyn (które były napędzane parą generowanego przez doły lawy), to było szybsze w użyciu floats. Teraz jedyną realną korzyścią z floats jest to, że zajmują mniej miejsca (co ma znaczenie tylko wtedy, gdy masz ich miliony).

P tato
źródło
9
Być może nie 100 lat temu ... Niektóre FPU obsługują natywną obsługę na poziomach zmiennoprzecinkowych, podwójnych i 80-bitowych i będą działać szybciej przy krótszych długościach. Niektórzy faktycznie będą wykonywać niektóre rzeczy wolniej przy krótszych długościach ... :-)
Brian Knoblauch
4
Możliwy wyjątek: myślę, że czas na podziały zależy od liczby bitów (1 cykl zegara / 2 bity). Czasy, które stworzyłem dla podziału zmiennoprzecinkowego i podwójnego, wydają się zgadzać z tym.
Neil Coffey
21
Uwaga dotycząca kodu SIMD - ponieważ w rejestrze SIMD (np. SSE) można spakować 2x zmiennoprzecinkowe niż podwójne, potencjalnie operowanie na pływakach może być szybsze. Ale ponieważ jest to C #, prawdopodobnie tak się nie stanie.
Calyth
13
@P Daddy: Powiedziałbym, że przewaga miejsca ma znaczenie na każdym poziomie hierarchii pamięci podręcznej. Gdy pamięć podręczna danych pierwszego poziomu ma 16 KB i przetwarzasz tablicę 4000 liczb, float może być szybszy.
Peter G.,
4
@artificialidiot Never say never;). SIMD jest obsługiwany w .NET od 4.6
ghord
14

Zależy to od systemu 32-bitowego lub 64-bitowego . Jeśli kompilujesz do wersji 64-bitowej, podwojenie będzie szybsze. Skompilowany do 32-bitowego na 64-bitowym (komputer i system operacyjny) sprawił, że unosił się około 30% szybciej:

    public static void doubleTest(int loop)
    {
        Console.Write("double: ");
        for (int i = 0; i < loop; i++)
        {
            double a = 1000, b = 45, c = 12000, d = 2, e = 7, f = 1024;
            a = Math.Sin(a);
            b = Math.Asin(b);
            c = Math.Sqrt(c);
            d = d + d - d + d;
            e = e * e + e * e;
            f = f / f / f / f / f;
        }
    }

    public static void floatTest(int loop)
    {
        Console.Write("float: ");
        for (int i = 0; i < loop; i++)
        {
            float a = 1000, b = 45, c = 12000, d = 2, e = 7, f = 1024;
            a = (float) Math.Sin(a);
            b = (float) Math.Asin(b);
            c = (float) Math.Sqrt(c);
            d = d + d - d + d;
            e = e * e + e * e;
            f = f / f / f / f / f;
        }
    }

    static void Main(string[] args)
    {
        DateTime time = DateTime.Now;
        doubleTest(5 * 1000000);
        Console.WriteLine("milliseconds: " + (DateTime.Now - time).TotalMilliseconds);

        time = DateTime.Now;
        floatTest(5 * 1000000);
        Console.WriteLine("milliseconds: " + (DateTime.Now - time).TotalMilliseconds);

        Thread.Sleep(5000);
    }
Bitterblue
źródło
3
Czy zastanawiałeś się, czy te 30% może być spowodowane dodatkowymi rzutami, których używasz?
Rasmus Damgaard Nielsen
@RasmusDamgaardNielsen Obsady są częścią problemu, ponieważ Mathdziałają z dublowaniem . Ale źle odczytałeś mój post: moje testy pokazały, że pływałem lepiej pod względem wydajności.
Bitterblue
2
Wyniki zamieszczone powyżej są fałszywe. Moje testy pokazują, że na starszej 32-bitowej maszynie z .NET 4.0 w trybie wydania wydajność floati doublesą praktycznie identyczne. Mniej niż 0,3% różnicy po uśrednieniu z wielu niezależnych prób, w których każda próba polegała na mnożeniu, dzieleniu i dodawaniu operacji na kolejno połączonych łańcuchach zmiennych (aby uniknąć przeszkadzających optymalizacji kompilatora). Próbowałem drugi zestaw testów z Math.Sin()i Math.Sqrt()a także dostał identyczne wyniki.
Special Sauce
13

Miałem mały projekt, w którym użyłem CUDA i pamiętam, że tam również float był szybszy niż dwukrotnie. Raz ruch między hostem a urządzeniem jest mniejszy (host to procesor, a „normalna” pamięć RAM, a urządzenie to procesor graficzny i odpowiednia pamięć RAM). Ale nawet jeśli dane znajdują się na urządzeniu przez cały czas, jest wolniejsze. Chyba gdzieś przeczytałem, że to się ostatnio zmieniło lub ma się zmienić wraz z następnym pokoleniem, ale nie jestem pewien.

Wygląda więc na to, że GPU po prostu nie radzi sobie natywnie z podwójną precyzją w tych przypadkach, co również wyjaśniałoby, dlaczego zwykle używany jest GLFloat zamiast GLDouble.

(Jak powiedziałem, to tylko o ile pamiętam, po prostu natknąłem się na to, szukając liczby zmiennoprzecinkowej vs. podwójnej na procesorze).

Mene
źródło
7
GPU to zupełnie inne zwierzęta niż FPU. Jak wspominali inni, rodzimym formatem FPU jest 80-bitowa podwójna precyzja. I to już od dawna. Jednak procesory graficzne podchodzą do tego pola z pojedynczą precyzją. Powszechnie wiadomo, że ich wydajność DP FP (zmiennoprzecinkowa podwójnej precyzji) często stanowi dokładnie połowę wydajności SP FP. Wydaje się, że często mają jednostki zmiennoprzecinkowe SP i muszą ponownie wykorzystać tę jednostkę, aby pokryć podwójną precyzję. Co daje dokładnie dwa cykle w porównaniu z jednym. To ogromna różnica w wydajności , która mnie zaskoczyła, gdy się z nią spotkałem.
Csaba Toth
1
Niektóre obliczenia naukowe wymagają DP FP, a wiodący producenci GPU nie reklamowali obniżenia wydajności. Teraz oni (AMD, nVidia) wydają się nieco poprawiać ten temat DP vs SP. Wiele rdzeni procesora Intel Xeon Phi zawiera FPU Pentium i zauważ, że Intel podkreślał jego możliwości podwójnej precyzji . To może naprawdę być w stanie konkurować z potworami GPGPU.
Csaba Toth
12

Nadal istnieją przypadki, w których preferowane są zmiennoprzecinkowe - na przykład przy kodowaniu OpenGL znacznie częściej stosuje się typ danych GLFloat (zwykle mapowany bezpośrednio na 16-bitowy float), ponieważ jest on bardziej wydajny na większości GPU niż GLDouble.

Cruachan
źródło
3
Może z powodu większej przepustowości danych? Jeśli masz macierz liczb (bufor z itp.), Rozmiar danych staje się ważniejszy, a unikanie konwersji między liczbami zmiennoprzecinkowymi i podwójnymi przyspiesza obsługę. Zgaduję że.
Lucero
2
Niewątpliwie przepustowość. Biorąc również pod uwagę kontekst specjalistyczny, jest mało prawdopodobne, aby cokolwiek było widoczne, używając podwójnych danych zamiast zmiennoprzecinkowych, więc po co marnować pamięć - zwłaszcza, że ​​jest ona krótsza na GPU niż procesory
Cruachan
1
Przepustowość, a także fakt, że SP FP (zmiennoprzecinkowa pojedyncza precyzja) jest bardziej natywnym formatem wewnętrznych FPU GPU niż DP FP (podwójna precyzja). Zobacz mój komentarz do odpowiedzi @ Mene. Układy FPU i FPU procesora są bardzo różnymi zwierzętami, FPU procesora myśli w DP FP.
Csaba Toth