Obecnie mamy wiele dostępnych kompilatorów. Obecnie GCC jest najpopularniejszym kompilatorem (zbiorem kompilatorów) pod Linuksami, *BSD i innych OpenSource’owych systemów, a także ma swój port na Windowsa (w pakiecie MinGW). Ale są także inne. Czym one się różnią między sobą i jaki mają wpływ na wydajność aplikacji? Jak duży wpływ na wydajność programu ma jego optymalizacja przez kompilator? Czy ma to w ogóle sens, czy te różnice są zauważalne. O tym w dowiecie się z tego artykułu.

Do testów postanowiłem wybrać następujące kompilatory:

  • GCC w wersji 4.6.3 (obecna na Ubuntu)
  • GCC w wersji 4.7.1 (ręcznie skompilowany)
  • Clang 3.0 (z repozytoriów)
  • TinyCC (mały kompilator C, dostępny w repo)
  • Open64 (pobrany z oficjalnej strony w formie skompilowanej)

Który z tych kompilatorów okaże się najlepszy? Małego benchmark został wykonany na mojej maszynie Acer Aspire 5552:

  • AMD Athlon X2 P320 2100MHz
  • 6 GiB RAM DDR3
  • system Linux Mint 13 64-bit (Linux 3.2.0-29-generic x86_64)
  • lekkie środowisko razor-qt z menedżerem OpenBox

Kompilatory testował będę z różnymi opcjami kompilacji przy pomocy kilku algorytmów i OpenSource’owych praktycznych aplikacji:

  • wyznacznik macierzy 10×10 (C i C++)
  • sha-1 – liczenie sumy kontrolnej dużych plików (C i C++)
  • Adobe C++ Benchmark
  • lame mp3 – kodowanie plików mp3 (C)
  • POV-Ray – renderer grafiki 3D

Niepewność pomiarów

Jako że pomiary czasów wykonywane są w wielozadaniowym systemie, na czas jest zależny od tego, co system w danym czasie ma do roboty. Zmieniłem więc ociężałe KDE na lekkiego razor-qt z menedżerem okiem OpenBox. W czasie wykonywania testów system był odłączony od internetu i stał kompletnie nieużywany, w tym czasie czytałem pewną książkę, albo grałem na starym piecu.

Testy były w dużej mierze zautomatyzowane, dzięki czemu mogłem je uruchomić i zostawić na godzinę. Nie ukrywam, że wiele razy musiałem poprawiać skrypty i powtarzać testy. Jeśli chodzi o niedokładność, to była niewielka: ok 2%. Testy były wykonywane kilka razy, przy zbyt dużym odchyleniu powtarzałem serię testów uzyskując bardzo zbliżone wyniki.

Kompilatory

GCC każdy zna. Podstawowy kompilator w Linuksie i wielu innych systemach. Stabilny, w miarę szybki, funkcjonalny.

CLang to nieco „nowość”:, wprowadzany jako standardowy w systemach BSD. Jest rozwijany (a raczej finansowany) przez Apple, którego korci jego licencja: BSD. Pisany jest pod wzór GCC i jest obsługiwany tak samo, jednak potrafi kompilować podobno do 4 razy szybciej, a kody wynikowe są do 20% szybsze. Clang jest częścią LLVM, jako standardowy kompilator C/C++.

Open64 to otwarty kompilator C/C++ i Fortrana. Sam o nim nie wiedziałem zbyt wiele do niedawna. Również ma taką samą składnię wywołań jak GCC, dzięki czemu łatwo było napisać testy.

TinyCC to raczej tylko ciekawostka. Malutki kompilatorek nieposiadający żadnych specjalnych cech.

Profile kompilacji

Testy na prostszych przykładach wykonywałem na następujących profilach:

  • I. „-O0” – brak optymalizacji, flaga dodana, ponieważ Open64 domyślnie ma ustawioną chyba -O2.
  • II. „-O2” – optymalizacja drugiego poziomu
  • III. „-O3” – optymalizacja trzeciego poziomu
  • IV. „-O3 -ffast-math” -trzeci poziom z przyspieszoną arytmetyką
  • V. „-O3 -march=k8 -msse2” – z dopasowanem do procesorów k8 + SSE2*
  • VI. „-O3 -ffast-math -march=k8 -msse2” – punkty 5+4

*W kodach nie było „wymuszenia” SSE2, jednak drogą optymalizacji kompilator może znaleźć pętle z operacjami, które może zwektoryzować i wykonać poprzez SSE.

Wybrany został procesor k8, ponieważ jest najbliższy mojej architektury, a native nie działa na wszystkich tych kompilatorach (przykładowo wg.Open64 nie posiadam SSE2). Czasy na wykresach podane w sekundach, chyba że napisano inaczej.

Wyznacznik macierzy 10×10

Test na szybkość przy wywołaniach rekurencyjnych, alokowaniu i dealokowaniu danych oraz obliczeniach zmiennoprzecinkowych. Dysponowałem dwoma algorytmami: czysty C i rozwiązanie w C++ z klasami. Algorytm jest ten sam: rekurencyjne rozwinięcie Laplace’a. Program obliczał wyznaczniki 10 macierzy wczytanych z pliku.

Wykres dla C:
Wyznacznik macierzy C

Wykres dla C++:
Wyznacznik macierzy C++

Opcje kompilacji nie miały większego znaczenia (oczywiście poza -O2, który zawsze przyspiesza). W C najszybszy kod wynikowy wyprodukował Clang i Open64, choć i tak przewaga jest niewielka w stosunku do GCC. W C++ zaś najszybszy kod wyprodukował GCC 4.6.1. Zauważyć można też przyspieszenie przy użyciu flagi -ffast-math.

Wyznaczanie SHA-1

Niewiele dający, ale jednak. Plikiem którego suma SHA-1 była wyznaczana był videotutorial do Blendera. Ważył on 206 MiB. Jako że następuje tu odczytywanie z dysku, wyniki mogły zakłócić dodatkowe operacje na dysku.

Wykres dla C:
Wyznaczanie SHA-1 – C

Wykres dla C++:
Wyznaczanie SHA-1 – C++

Jeśli chodzi o C, opcje kompilacji również miały tu niewiele do powiedzenia, choć przy Open64 można zauważyć że kod „-O2” jest szybszy niż „-O3”. Przypadek? Może, aczkolwiek wynik taki wyszedł w przypadku wszystkich 3 przeprowadzonych serii. W algorytmie C++ jest znacznie większe zróżnicowanie. Open64 również pokazuje, że kod z „-O2” jest szybszy od trzeciego poziomu, ale dostosowanie architektury poprawia nieco wynik.

Nowy GCC natomiast jakby kompletnie nie wiedział co z tą architekturą zrobić, wynikowy kod jest wolniejszy. Co prawda w tym teście nie występują liczby zmiennoprzecinkowe, więc „-ffast-math” po prostu nie ma wpływu na wynik, ale pokazuje, że wyniki są w miarę stabilne (pary III i IV, oraz V i VI mają względnie takie same czasy).

Benchmark Adobe’a

Szukając algorytmów do testów natknąłem się na paczkę testów od Adobe’a, można go znaleźć na tej stronie: stlab.adobe.com/performance.

Nie zmieniałem opcji kompilacji, zmierzyłem czas wykonywania całej serii testów dla wszystkich 4 testowanych kompilatorów C++. Testy były baaaardzo długie, więc byłem zmuszone je trochę skrócić (zmniejszyć ilość iteracji). To benchmark z prawdziwego zdarzenia testujący konkretne optymalizacje. Ten test był wykonany tylko 2 razy, co i tak trwało 2*40 minut (wyniki były bardzo zbliżone, niemal identyczne). Ukazuje mocne i słabe strony kompilatorów.

Benchmark Adobe

Poszczególne czasy nie są istotne (były dość rozbieżne czasy poszczególnych testów, więc nie wyglądałoby to zbyt czytelnie na jednym wykresie), wszystkie są pokazane względem czasów GCC 4.6.3. Według tego testu GCC nie jest aż taki szybki. Testy wykonane były z tylko jedną flagą: -O3. Najszybszy jest to Open64! Najdłuższym testem był ./loop_unroll. Jednak benchmark benchmarkiem, jak jest w praktyce?

Lame MP3 – konwersja WAV na MP3

Do testów posłużył oryginalny WAV ze zrippowanej oryginalnej płyty (ponad 5-minutowy Thrash Metalowy utwór). Tiny C Compiler niestety nie podołał temu zadaniu. Wykonałem 2 serie testów mierząc czas wszystkich etapów, gdzie kompilator ma coś do gadania. Kompilacja odbyła się przy domyślnych w tym projekcie opcjach kompilacji:

-O3 -fomit-frame-pointer -ffast-math -Wall -pipe

Lame mp3 - konwersja WAV na mp3

Czasy konwersji nie były zbyt zróżnicowane, jednak ciekawe są przypadki konfiguracji i budowania. Skrypt ./configure najdłużej wykonywał się dla Open64 i to aż trzykrotnie! Najszybszym kompilatorem okazał się Clang, a najwolniejszym Open64. Tu również GCC 4.7.1 przegrywa ze swoim poprzednikiem. Stwierdziłem że to za mało. Pobawiłem się nieco flagami i uzyskałem lepszy czas:

Lame MP3 - konwersja WAV na MP3

Ale niestety nic za darmo: suma kontrolna pliku test.mp3 była inna (osobiście nie słyszę różnicy). Dowodzi to temu, że przesadzenie z agresywnymi niebezpiecznymi optymalizacjami może wpłynąć nie tylko na szybkość, ale i sam wynik zbudowanego programu! Flagi użyte przy kompilacji oznaczonej opt:

O3 -pipe -march=k8 -msse -msse2 -funsafe-math-optimizations -funroll-loops -funsafe-loop-optimizations -m3dnow -mfpmath=sse

Oczywiście najlepszy efekt osiągnął GCC w wersji 4.6.3. Pozostałe kompilatory nie były testowane, ponieważ nie rozpoznawały części tych flag (nie znalazłem odpowiedników), a bez nich różnica była nieznacząca. Przy użyciu jakże profesjonalnego oprogramowania zwany Audacity postanowiłem zbadać różnicę. Załadowałem plik skonwertowany normalnym lame’em i tym zbyt zoptymalizowanym, jeden z nich „odwróciłem w pionie” i zmiksowałem obie ścieżki. Jak wychodzi z prostego równania: x+x*(-1), musi wyjść 0, a tu proszę:

Lame MP3 - konwersja WAV na MP3 - Audacity

Przy przybliżeniu okazuje się, że cała ścieżka jest miejscami pofałdowana, czego nie osobiście nie słyszę ani na moich słuchawkach na USB, ani na głośnikach (może na sprzęcie za 10 tys. zł byłoby słychać jakąś różnicę). Mimo wszystko technicznie różnica jest. O ile pierwszy skok można wytłumaczyć nagłą zmianą nastroju utworu, to nie rozumiem czemu są takie nagłe zmiany w środku utworu, gdzie nie działo się nic specjalnego.

POV-Ray

To samodzielny renderer grafiki 3D znany ze swojej prostoty i względnie sporych możliwościach. Ten test nie objął nowego GCC – były problemy z automatyzacją – nie chciał się zbudować z nieznanych powodów, gdy ustawiałem własny prefiks (podobnie Open64, ale jego zbadałem ręcznie). Flagi kompilacji wykorzystałem standardowe:

-O3 -march=k8 -mtune=k8 -msse -msse2 -mfpmath=sse -malign-double -minline-all-stringops

Oraz zrobiłem test dla GCC z następującymi flagami:

-O3 -pipe -march=k8 -msse -msse2 -funsafe-math-optimizations -funroll-loops -funsafe-loop-optimizations -malign-double -m3dnow -mfpmath=sse

Wyniki testu
POV-Ray

Znów Clang okazał się demonem prędkości kompilacji, a GCC wyprodukował szyszy kod. Niebezpieczne optymalizacje skróciły czas rysowania sceny benchmark, jednak jakim kosztem? Oto porównanie:

POV-Ray - OK POV-Ray - Błąd

Po lewej stronie mamy poprawny render, a po prawej agresywną niebezpieczną optymalizacją
Różnica tych 2 obrazów wykonana w GIMPie
POV-Ray: różnica tych 2 obrazów wykonana w GIMPie
W tym przypadku różnice widać nawet gołym okiem (zmieniając z jednego obrazka na drugi). Różnice też pokazały się na jednej ze scen radiosity, jednak dotyczą one jedynie kilku krawędzi, który były inaczej wygładzone. Wynik sceny benchmark również był inny dla kompilatora Open64, różnica względem oryginału była podobna do różnicy pokazanej wyżej, a oto różnice w serii radiosity (po lewej normalny, pośrodku wynik OpenCC, po prawej różnica):

OpenCC - Różnice

Podsumowanie

Testy wyraźnie pokazują, że powiedzienie szybki język nie jest poprawne: wszystko zależy od implementacji języka (kompilatora) oraz opcji kompilacji. Optymalizacje kompilatora skracają czas wykonania kodu wynikowego często nawet o połowę! Czasem włączenie flag pod architekturę k8 spowodowało nieznaczne zwolnienie..

Mimo że na specjalnym benchmarku królował wręcz Open64, w praktyce nie szło mu tak dobrze, w testowanych programach nie było tak szablonowych pętel, które kompilatory mogłyby bardzo łatwo rozpoznać. Rozczarowujący jest wynik GCC 4.7.1, który osiąga gorsze rezultaty od swojego poprzednika, jednak lepiej wspiera nowsze standardy takie jak C++11.

W każdym razie test pokazał że kompilator kompilatorowi nierówny i nie da się jednoznacznie wyłonić najlepszego. Tym samym może się okazać, że kompilatory tworzą zupełnie różne programy tworzące różne wyniki.

Poprzedni artykułIcedTea-Web 1.3
Następny artykułKeyboard PC z Ubuntu

35 KOMENTARZE

    • W sklepie. Możesz go nabyć za ciężkie pieniądze, zupełnie nieproporcjonalne do jego jakości.

    • A ja nie widz – do zastosowań niekomercyjnych dla Linuksa jest za darmo: http://software.intel.com/en-us/non-commercial-so
      Więc skoro jest podany kompilator AMD (Open64) to i Intela sprawdzić można… w dodatku kompilatory Sun (w darmowym Sun Studio/Oracle Studio) czy dragonegg też byłyby miłe (tym bardziej jeśli się wrzuca do testu zamiast nich TinyCC).

      OFC wyżej pomijam ogólnie sens takiego porównywania, jeśli nie jest robiony przez osoby znające dane kompilatory i dostosowujące flagi do konkretnego programu (już nawet nie mówię o tym, że w GCC włączenie LTO, porozwijanie pętli, skorzystanie z PGO, jak i podobne zabiegi w innych kompilatorach mogłyby tą sytuację zmienić (chociażby podstawowa znajomość clang nakazywałaby skorzystać z -O4, a nie -O3 włączając IPO))

    • Licencja własnościowa, nawet nie wiem jak to pobrać dla Linuksa, na oficjalnej stronie jest tylko 30-dniowe demo dla windowsa.

    • Jako że to kompilator Intela, różnica większa byłaby właśnie na intelach. Samym gołym wykresom nie ma co wierzyć: tu w normalnym benchmarku Open64 wygrał, a w praktyce GCC był lepszy. Okrężną drogą udało mi się dotrzeć do triala pod Linuksa, jak się ściągnie (a trochę waży) i go opanuję, dam znać.

    • Ok. Lepszy byłby na Intelach. Więc kto to kupi? Nie twórcy oprogramowania skoro mają GCC? To po co taki kompilator? Co np. by firmę tworzącą oprogramowanie skusiło do jego kupna?

    • ci wszyscy, którzy bardzo wydajny aczkolwiek binarnie zamknięty kod? (ot chociażby Oracle?)

    • Jeśli faktycznie różnica wydajności byłaby tak ogromna jak to się chwalą na swojej stronie, to np. serwerownie i farmy. Zakup tego kompilatora mógłby się zwrócić w postaci mniejszych rachunków za prąd.

    • Nie robię teraz zbyt dokładnych testów, ale na moim procesorze ten kompilator nie błyszczy – wyniki zbliżone do clanga, a kompilacja dłuższa niż Open64. Jutro zrobię test povraya i adobe'a. Używa też nieco innych flag, co też jest nieco wkurzające, bo trzeba podawać je ręcznie zmienione. Co do testu lame: kompilacja trwała ponad 4 minuty (GCC zajęło to 38 sekund, może zbyt system obciążony, ale bez przesady!), a konwersja trwała porównywalnie tak samo długo. Również jest odchylenie "od normy" sumując plik wyjściowy z odwróconym plikiem wyjściowym normalnego GCC. Póki co nie widzę sensu wydawać kasy na ten kompilator. Ten test powinien przeprowadzić jeszcze ktoś z procesorem Intel, o porównywalnej wydajności, przygotuję paczkę ze skryptami jak będę miał czas.

    • To czy błyszczy czy nie, zazwyczaj zależy od znajomości kompilatora – flagi, które zastosowałeś do GCC, Clang nie świadczą o znajomości jakiegokolwiek kompilatora, więc pewnie nie dałeś -ipo i -fast, nie rozwijałeś pętli, nie wykorzystałeś PGO, itp. Ogólnie ICC powinien błyszczeć na prockach AMD i wykonywać program szybciej niż konkurencja… mimo, że nie stosuje dla nich optymalnego kodu (ICC dla AMD i Via ma osobny kod, szybki, ale jak CPUID jest Intela to wykonuje drugą ścieżkę wykonawczą i tu dopiero błyszczy (w praktyce ICC nie kompiluje programu raz tylko kilka razy)).

    • clang używa tych samych flag co GCC, żeby zachować kompatybilność. Open64 używa w większości tych samych. ICC jak zauważyłem tylko -O3 ma te same i tylko na nim na razie testowałem, aczkolwiek w dokumentacji zauważyłem kilka, które się włącza tylko ta intele. Przejrzę dokumentację dzisiaj i znajdę odpowiedniki tych flag i przetestuję jeszcze raz. BTW. w -fast jest już -ipo.

    • To, że używają tych samych flag nie oznacza, że to te same flagi oznaczające to samo – przykładowo GCC przy -O3 korzysta z IPO, ale dla pojedynczych plików jeśli chcesz faktycznie skorzystać z IPO to dodaj -fwhole-program –combine (czego już Clang i Open64 nie zrozumieją), w wypadku Clang znowu aby to osiągnąć musisz ustawić optymalizacje na -O4 (co w GCC nie przynosi, żadnych zmian). Ustawienie wszędzie -O3 niczego nie załatwia – w każdym kompilatorze oznacza to zupełnie inne optymalizacje.
      Co do fast i ipo to musiało coś się zmienić, bo wcześniej z tego co pamiętam -fast dodawało tylko -ip (optymalizacje IPO dla pojedynczych plików jak w GCC z -O3, a nie dla całego programu jak GCC z -fwhole-program –combine).

    • Co do ICC znalazłem jakiegoś PDFa z listą flag (z 2006 roku), ale połowy nie rozumie, więc pewnie wiele zmienili. Aktualniejsza jest chyba angielska wiki.
      Testy Adobe'a: icc wypadł lepiej od Open64, ale niewiele: 76% czasu GCC (Open64: 86%). Kompletnie nie radzi sobie z ./simple_types_constant_folding sięgając do 195% czasu GCC, za to wektoryzacja piękna: 75%. Z -fast poradził sobie odrobinę gorzej.
      -fwhole-program i -O4 nie robią żadnej różnicy przy moich testach, właściwie -O4 całkowicie unieruchamia konsolidator (/tmp/matrix-a7XA1E.o: file not recognized: File format not recognized). A –combine jest w ogóle niezrozumiały dla mojego GCC.

  1. "ociężałe KDE"

    Jaki wpływ może mieć KDE na wynik kompilacji? Autor tego nie zmierzył. Przecież kompilacja jest realizowana przez osobne procesy (jeśli wie się co oznacza separacja przestrzeni i scheduling), a autor ma 6 GiB RAM. Nie ma obowiązku odpalania w czasie kompilacji niczego zajmującego procesor – u mnie w KDE 4 procesy śpią a do kompilacji (laptop!) używane jest 8 rdzeni.

    • W tle KDE działa wiele "nieprzewidywalnych" procesów, które w każdej chwili mogą zdecydować, że coś zaczną robić. Chociażby widget pogodowy, wiecznie rysujące się plasmoidy… Wolałem tego uniknąć. Na KDE miałem nieco bardziej rozbieżne wyniki niż na razorze.

    • Wystarczyło uśrednić wyniki kilku kompilacji wyrzucając skrajne i licząc średnią z reszty (bo skąd niby masz pewność, że któryś z demonów w tle nie obudził się (czyli jakbyś robił bez jakichkolwiek Xów a jedynie w konsoli) coś zrobić i nie zawalił Ci testów?).

    • Przy okazji można by skorzystać z PGO i stworzyć, kilka razy puścić benchmark i użyć wygenerowanych danych do kompilacji z najlepszymi według testów optymalizacjami do danego programu w danym kompilatorze.

    • Pisałem że wykonywałem kilka testów (zwykle po 3 zestawy) i powtarzałem zestaw, gdy wyniki za bardzo się odchylały od pozostałych.

    • Gadu gadu. "Nieprzewidywalnych" procesów bo się nie zna wystaczająco systemów operacyjnych. W pomiarach liczy się czas procesora dla procesów kompilatora a nie czas zegarowy.

      Nie widzę listy poleceń jakie były wykonywane. Wiele z nich to pewnie nie kompilacja a np. konfiguracja i testy.

      Mam nadzieję, że nie mierzyłeś wydajności "configure" i odjąłeś jego narzut.

      Nie uśredniłeś wyników dla chociażby drugiej maszyny z procesorem nie-AMD.

      Ogólnie, informacje przedstawione w artykule nie wystarczają na ponowienie testów przez inną osobę.

    • Tak, odpalałem każdy test zosobna ze stoperem w ręce…. Czytanie nie boli: wykonywałem po 3 serie testów, jeśli czasy w którymś były zbyt odchylone od pozostałych – seria była powtarzana. Testy wykonywane na lekkim środowisku, nie ważne jak długo konto jest używane i jak często, zawsze chodzi tak samo. Jeśli chodzi o lame i POV-Ray, to tak: mierzyłem czas configure'a, bo w tym czasie trwa testowanie, czy ten kompilator i środowisko w ogóle się nada – wyszło nawet że Open64 jest strasznie oporny. Ale czasy mierzone były osobno! Do pomiaru czasu posłużyło nie polecenie time, tylko program time (/usr/bin/time ≠ time), a za czas posłużył nie "czas zegarowy", tylko "Total number of CPU-seconds that the process used directly (in user mode)".

    • Robisz benchmarki na koncie używanym na codzień? Co ma kompilator do GUI? Wystarczyło odpalić sesję w terminalu. Jak już musisz myszą operować (nie wiem, może w Eclipse niektórzy muszą kliknąć ikonkę by kompilować) to przecież wystarczy założyć konto testowe bez tych "widgetów pogodowych". Albo odpalić niemal gołe X11 jeśli już ma się wykluczyć wpływ Desktopu.

  2. … Co powiecie na "uwaga" kompilowanie na windowsie. Sam nigdy nie kompilowałem tam, nigdy nie miałem potrzeby używania Windowsowskich narzędzi…..

    … ale byłoby to ciekawe porównanie.

    PS…
    Szkoda, że nie masz czegoś mocniejszego PIIx6, Buldek x8, chodzi o to by sprawdzić też skalowanie. Ty na dwurdeniowym procesorze eliminujesz ten czynnik.

    Np. w programach 3d u mnie najszybszym systemem był XPx64… nie linuks, nie mac, nie …? 2xCore… gdy jednak przesiadłem się na 12 wątków sprawa przestała być oczywiste.

  3. kurde ile hejtu.. koles wlozyl sporo pracy zeby przygotowac te wyniki, powstal ciekawy artykul. zamiast podejsc do tematu konstruktywnie, od razu hejtujecie (a ja bym to zrobil lepiej, a o tym zapomniales, a to warto bylo, a srodowisko niewazne, a najlepiej to na konsoli kompilowac, ple ple ple). to kurde zrob to czlowieku, poswiec tyle wolnego czasu i zobacz jakie to 'proste’

  4. Niedługo zamieszczę drugą część benchmarka, tym razem uwzględnię icc i przeprowadzę na 2 albo 3 procesorach: Athlon X2 2.1GHz, Dual Core 1.6 GHz i Core 2 Duo 2.4GHz. Muszę sobie tylko trochę bardziej zautomatyzować te testy, bo mam różne konfiguracje na tych komputerach.

  5. Przydałoby się wyjaśnienie skąd się biorą różnice w wynikach działania dźwięku i grafiki, skoro testowane były tylko kompilatory. Czy wyniki obliczania macierzy też wyszły różne zależnie od kompilatora lub opcji? To by było ciekawe…

    • Są to minimalne różnice wynikające z tego, że używa szybszych zaokrągleń liczb zmiennoprzecinkowych, gubi precyzję, ale (niezauważalnie) szybciej działa.

ZOSTAW ODPOWIEDŹ

Proszę wpisać swój komentarz!
Proszę podać swoje imię tutaj