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?
c#
.net
clr
performance
Joan Venge
źródło
źródło
Odpowiedzi:
Przynajmniej na procesorach x86
float
idouble
każ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
float
jest szybsza niżdouble
stosowana 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życiufloat
s. Teraz jedyną realną korzyścią zfloat
s jest to, że zajmują mniej miejsca (co ma znaczenie tylko wtedy, gdy masz ich miliony).źródło
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); }
źródło
Math
działają z dublowaniem . Ale źle odczytałeś mój post: moje testy pokazały, że pływałem lepiej pod względem wydajności.float
idouble
są 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 zMath.Sin()
iMath.Sqrt()
a także dostał identyczne wyniki.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).
źródło
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.
źródło