O ile lepsze są naprawdę kompilatory Fortran?

74

To pytanie jest rozszerzeniem dwóch dyskusji, które pojawiły się ostatnio w odpowiedziach na „ C ++ vs Fortran for HPC ”. I jest to trochę więcej wyzwanie niż pytanie ...

Jednym z najczęściej słyszanych argumentów na korzyść Fortrana jest to, że kompilatory są po prostu lepsze. Ponieważ większość kompilatorów C / Fortran ma ten sam zaplecze, kod wygenerowany dla semantycznie równoważnych programów w obu językach powinien być identyczny. Można jednak argumentować, że kompilacja C / Fortran jest mniej lub bardziej łatwa w optymalizacji.

Postanowiłem więc wypróbować prosty test: dostałem kopię daxpy.f i daxpy.c i skompilowałem je z gfortran / gcc.

Teraz daxpy.c jest tylko tłumaczeniem f2c daxpy.f (automatycznie generowany kod, brzydki jak cholera), więc wziąłem ten kod i trochę go wyczyściłem (poznaj daxpy_c), co w zasadzie oznaczało ponowne zapisanie wewnętrznej pętli jako

for ( i = 0 ; i < n ; i++ )
    dy[i] += da * dx[i];

Na koniec przepisałem go ponownie (wpisz daxpy_cvec), używając składni wektorowej gcc:

#define vector(elcount, type)  __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;

vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
    vy[i] += va * vx[i];
    vy[i+1] += va * vx[i+1];
    }
for ( i = n & ~3 ; i < n ; i++ )
    dy[i] += da * dx[i];

Zauważ, że używam wektorów o długości 2 (to wszystko na co pozwala SSE2) i że przetwarzam jednocześnie dwa wektory. Jest tak, ponieważ w wielu architekturach możemy mieć więcej jednostek mnożenia niż elementów wektorowych.

Wszystkie kody zostały skompilowane przy użyciu gfortran / gcc w wersji 4.5 z flagami „-O3 -Wall-msse2 -march = native -ffast-matematyka -fomit-frame-pointer -malign-double -fstrict-aliasing”. Na moim laptopie (procesor Intel Core i5, M560, 2,67 GHz) otrzymałem następujące dane wyjściowe:

pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.

Tak więc oryginalny kod Fortrana zajmuje nieco ponad 8,1 sekundy, jego automatyczne tłumaczenie zajmuje 10,5 sekundy, naiwna implementacja C robi to w wersji 7.9, a kod wyraźnie wektoryzowany robi to w wersji 5.6, nieznacznie mniej.

To Fortran jest nieco wolniejszy niż naiwna implementacja C i 50% wolniejszy niż wektoryzowana implementacja C.

Oto pytanie: jestem rodzimym programistą C i jestem przekonany, że wykonałem dobrą robotę w tym kodzie, ale kod Fortrana został ostatnio zmieniony w 1993 roku i dlatego może być nieco nieaktualny. Ponieważ nie czuję się tak komfortowo kodować w Fortranie, jak inni tutaj, czy ktoś może wykonać lepszą robotę, tj. Bardziej konkurencyjną w porównaniu do którejkolwiek z dwóch wersji C?

Ponadto, czy ktoś może wypróbować ten test za pomocą icc / ifort? Składnia wektorowa prawdopodobnie nie zadziała, ale byłbym ciekawy, jak zachowuje się naiwna wersja C. To samo dotyczy każdego, kto leży wokół XLC / XLF.

Mam przesłanych źródła i Makefile tutaj . Aby uzyskać dokładne czasy, ustaw CPU_TPS w teście. C na liczbę Hz na twoim CPU. Jeśli znajdziesz jakieś ulepszenia którejkolwiek z wersji, opublikuj je tutaj!

Aktualizacja:

Dodałem kod testowy stali do plików online i uzupełniłem go o wersję C. Zmodyfikowałem programy tak, aby tworzyły pętle 1'000'000 na wektorach o długości 10'000, aby były spójne z poprzednim testem (i ponieważ moja maszyna nie mogła przydzielić wektorów o długości 1'000'000'000, jak w oryginale stali kod). Ponieważ liczby są teraz nieco mniejsze, skorzystałem z opcji, -par-threshold:50aby kompilator miał większe szanse na równoległość. Zastosowana wersja icc / ifort to 12.1.2 20111128, a wyniki są następujące

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU

Podsumowując, wyniki są, dla wszystkich praktycznych celów, identyczne zarówno dla wersji C, jak i Fortran, a oba kody są równoległe automatycznie. Pamiętaj, że szybkie czasy w porównaniu z poprzednim testem wynikają z zastosowania arytmetyki zmiennoprzecinkowej o pojedynczej precyzji!

Aktualizacja:

Chociaż tak naprawdę nie podoba mi się to, dokąd zmierza ciężar dowodu, przekodowałem przykład mnożenia macierzy stali w C i dodałem go do plików w Internecie . Oto wyniki potrójnej pętli dla jednego i dwóch procesorów:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 triple do time   3.46421700000000     
3.63user 0.06system 0:03.70elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 triple do time   5.09631900000000     
5.26user 0.06system 0:02.81elapsed 189%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU

Zauważ, że cpu_timew Fortran mierzy czas procesora, a nie zegar ścienny, więc zawinąłem wywołania, timeaby porównać je dla 2 procesorów. Nie ma prawdziwej różnicy między wynikami, z wyjątkiem tego, że wersja C działa nieco lepiej na dwóch rdzeniach.

Teraz matmulpolecenie, oczywiście tylko w Fortranie, ponieważ ta właściwość nie jest dostępna w C:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 matmul    time   23.6494780000000     
23.80user 0.08system 0:23.91elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 matmul    time   26.6176640000000     
26.75user 0.10system 0:13.62elapsed 197%CPU

Łał. To absolutnie okropne. Czy ktoś może dowiedzieć się, co robię źle, lub wyjaśnić, dlaczego ta wewnętrzna właściwość jest w jakiś sposób dobra?

Nie dodałem dgemmwywołań do testu porównawczego, ponieważ są to wywołania biblioteczne do tej samej funkcji w Intel MKL.

Dla przyszłych badań, może ktoś zasugerować przykład znany być wolniejszy niż w C w Fortran?

Aktualizacja

Aby zweryfikować twierdzenie stali, że matmulistotna jest „rząd wielkości” szybciej niż wyraźny produkt macierzowy na mniejszych matrycach, zmodyfikowałem swój własny kod, aby pomnożyć macierze o rozmiarze 100x100, stosując obie metody, po 10 000 razy każda. Wyniki na jednym i dwóch procesorach są następujące:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
 matmul    time   3.61222500000000     
 triple do time   3.54022200000000     
7.15user 0.00system 0:07.16elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
 matmul    time   4.54428400000000     
 triple do time   4.31626900000000     
8.86user 0.00system 0:04.60elapsed 192%CPU

Aktualizacja

Grisu słusznie wskazuje, że bez optymalizacji gcc konwertuje operacje na liczbach zespolonych na wywołania funkcji biblioteki, podczas gdy gfortran opisuje je w kilku instrukcjach.

Kompilator C wygeneruje ten sam, zwarty kod, jeśli opcja -fcx-limited-rangejest ustawiona, tzn. Kompilator jest instruowany, aby ignorować potencjalne przepływy / niedomiar w wartościach pośrednich. Ta opcja jest jakoś domyślnie ustawiona w gfortran i może prowadzić do niepoprawnych wyników. Zmuszanie -fno-cx-limited-rangew gfortran nic nie zmieniło.

Jest to zatem argument przemawiający przeciwko używaniu gfortranu do obliczeń numerycznych: operacje na złożonych wartościach mogą przekraczać / spadać, nawet jeśli prawidłowe wyniki mieszczą się w zakresie zmiennoprzecinkowym. To w rzeczywistości standard Fortrana. W gcc lub ogólnie w C99 domyślnie robi się ściśle (czytaj zgodnie z IEEE-754), chyba że określono inaczej.

Przypomnienie: należy pamiętać, że głównym pytaniem było to, czy kompilatory Fortran produkują lepszy kod niż kompilatory C. To nie jest miejsce do dyskusji na temat ogólnych zalet jednego języka nad drugim. Chciałbym naprawdę zainteresować się tym, czy ktokolwiek znajdzie sposób na nakłonienie gfortranu do wyprodukowania daxpy tak wydajnego jak ten w C przy użyciu wyraźnej wektoryzacji, ponieważ ilustruje to problemy z poleganiem na kompilatorze wyłącznie w celu optymalizacji SIMD lub przypadek, w którym kompilator Fortran wyprzedza swój odpowiednik C.

Pedro
źródło
Jednym z problemów związanych z taktowaniem jest to, że jeśli twój procesor pracuje w trybie krokowym / turbo, wyniki mogą być na całej mapie.
Bill Barth
1
Twój daxpy_c.c obecnie aktualizuje x wielokrotnością x i wcale nie dotyka y. Możesz to naprawić, aby było sprawiedliwie ...
Jack Poulson
1
@JackPoulson: Dobry chwyt, naprawiono i zaktualizowano wyniki.
Pedro
2
Jestem też całkiem pewien, że różnica wynika całkowicie z ręcznego rozwijania w wersji Fortran, myląc kompilator. Kiedy zastąpię ją tą samą prostą pętlą, którą umieściłeś w swojej wersji C, wydajność między nimi jest prawie identyczna. Bez zmian wersja Fortran działała wolniej dzięki kompilatorom Intela.
Jack Poulson
1
@permeakra: W rzeczywistości standard C99 określa restrictsłowo kluczowe, które mówi kompilatorowi dokładnie to: aby założyć, że tablica nie pokrywa się z żadną inną strukturą danych.
Pedro

Odpowiedzi:

37

Różnica w taktowaniu wydaje się wynikać z ręcznego rozwijania daxpy z Fortranem . Poniższe czasy dotyczą Xeon X5650 2,67 GHz za pomocą polecenia

./test 1000000 10000

Kompilatory Intel 11.1

Fortran z ręcznym rozwijaniem: 8,7 s
Fortran bez ręcznego rozwijania: 5,8 s
C bez ręcznego rozwijania: 5,8 s

Kompilatory GNU 4.1.2

Fortran z ręcznym rozwijaniem: 8,3 s
Fortran bez ręcznego rozwijania: 13,5 s
C bez ręcznego rozwijania: 13,6 s
C z atrybutami wektorowymi: 5,8 s

Kompilatory GNU 4.4.5

Fortran z ręcznym rozwijaniem: 8,1 sek.
Fortran bez ręcznego rozwijania: 7,4 sek.
C bez ręcznego rozwijania: 8,5 sek.
C z atrybutami wektorowymi: 5,8 sek.

Wnioski

  • Ręczne rozwijanie pomogło kompilatorom GNU 4.1.2 Fortran w tej architekturze, ale szkodzi nowszej wersji (4.4.5) i kompilatorowi Intel Fortran.
  • Kompilator GNU 4.4.5 C jest znacznie bardziej konkurencyjny w przypadku Fortran niż w wersji 4.2.1.
  • Wewnętrzne elementy wektorowe umożliwiają dopasowanie wydajności GCC do kompilatorów Intel.

Czas przetestować bardziej skomplikowane procedury, takie jak dgemv i dgemm?

Jack Poulson
źródło
Dzięki za wyniki! Jakiej wersji gcc używałeś i czy możesz być bardziej szczegółowy w odniesieniu do procesora?
Pedro
2
Twój kompilator jest starszy niż twój procesor ... Czy możesz spróbować z gcc-4.5?
Pedro
1
Właśnie tego spróbowałem. Wersja wektoryzowana z GCC 4.4.5 dokładnie odpowiada wynikom Intel 11.1.
Jack Poulson
1
Właśnie zainstalowałem gcc / gfortran w wersji 4.4.5 i nie mogę odtworzyć różnic bez rozwijania. W rzeczywistości w asemblerze wygenerowanym dla obu przypadków najbardziej wewnętrzna pętla jest identyczna, z wyjątkiem użytych nazw rejestrów, które są wymienne. Czy możesz ponownie uruchomić testy, aby się upewnić?
Pedro
4
Czy możemy powiedzieć, że ten rodzaj ustala odwieczną debatę „używamy fortran, ponieważ jest bardziej wydajny”, abyśmy mogli w końcu wrzucić go do śmietnika?
Stefano Borini,
16

Spóźniam się na to przyjęcie, więc ciężko jest mi podążać w tę i z powrotem z góry. Pytanie jest duże i myślę, że jeśli jesteś zainteresowany, można je podzielić na mniejsze części. Jedno, co mnie zainteresowało, to po prostu wydajność twoich daxpywariantów i to, czy Fortran jest wolniejszy niż C na tym bardzo prostym kodzie.

Działając zarówno na moim laptopie (Macbook Pro, Intel Core i7, 2,66 GHz), względna wydajność Twojej wektoryzowanej wersji C i nie wektoryzowanej wersji Fortran zależy od zastosowanego kompilatora (z Twoimi własnymi opcjami):

Compiler     Fortran time     C time
GCC 4.6.1    5408.5 ms        5424.0 ms
GCC 4.5.3    7889.2 ms        5532.3 ms
GCC 4.4.6    7735.2 ms        5468.7 ms

Wydaje się więc, że GCC lepiej wektoryzowało pętlę w gałęzi 4.6 niż wcześniej.


Wydaje mi się, że w ogólnej debacie można napisać szybki i zoptymalizowany kod zarówno w języku C, jak i Fortran, prawie tak jak w asemblerze. Zwrócę jednak uwagę na jedno: tak jak asembler jest bardziej żmudny w pisaniu niż C, ale daje lepszą kontrolę nad tym, co jest wykonywane przez procesor, tak C jest bardziej niski niż Fortran. W ten sposób daje większą kontrolę nad szczegółami, co może pomóc w optymalizacji, gdzie standardowa składnia Fortran (lub jego rozszerzenia dostawcy) może nie mieć funkcjonalności. Jednym z przypadków jest jawne użycie typów wektorów, innym jest możliwość ręcznego określenia wyrównania zmiennych, czego Fortran nie jest w stanie.

F'x
źródło
witamy w scicomp! Zgadzam się, że wersje kompilatora są w tym przypadku równie ważne jak język. Czy w ostatnim zdaniu miałeś na myśli „zamiast” zamiast „off”?
Aron Ahmadia
9

Sposób, w jaki napisałbym AXPY w Fortranie, jest nieco inny. To jest dokładne tłumaczenie matematyki.

m_blas.f90

 module blas

   interface axpy
     module procedure saxpy,daxpy
   end interface

 contains

   subroutine daxpy(x,y,a)
     implicit none
     real(8) :: x(:),y(:),a
     y=a*x+y
   end subroutine daxpy

   subroutine saxpy(x,y,a)
     implicit none
     real(4) :: x(:),y(:),a
     y=a*x+y
   end subroutine saxpy

 end module blas

Teraz wywołajmy powyższą procedurę w programie.

test.f90

 program main

   use blas
   implicit none

   real(4), allocatable :: x(:),y(:)
   real(4) :: a
   integer :: n

   n=1000000000
   allocate(x(n),y(n))
   x=1.0
   y=2.0
   a=5.0
   call axpy(x,y,a)
   deallocate(x,y)

 end program main

Teraz skompilujmy i uruchommy ...

login1$ ifort -fast -parallel m_blas.f90 test.f90
ipo: remark #11000: performing multi-file optimizations
ipo: remark #11005: generating object file /tmp/ipo_iforttdqZSA.o

login1$ export OMP_NUM_THREADS=1
login1$ time ./a.out 
real    0 m 4.697 s
user    0 m 1.972 s
sys     0 m 2.548 s

login1$ export OMP_NUM_THREADS=2
login1$ time ./a.out 
real    0 m 2.657 s
user    0 m 2.060 s
sys     0 m 2.744 s

Zauważ, że nie używam żadnych pętli ani wyraźnych dyrektyw OpenMP . Czy byłoby to możliwe w C (to znaczy bez użycia pętli i automatycznej równoległości)? Nie używam C, więc nie wiem.

stali
źródło
Automatyczna równoległość jest funkcją kompilatorów Intel (zarówno Fortran, jak i C), a nie języka. Zatem równoważnik w C powinien również być równoległy. Z ciekawości, jak to działa dla bardziej umiarkowanego n = 10000?
Pedro
3
To był cały punkt. Autopar jest łatwiejszy w Fortranie, ponieważ Fortran (w przeciwieństwie do C) obsługuje operacje na całej tablicy, takie jak matmult, transpose itp. Optymalizacja kodu jest więc łatwiejsza dla kompilatorów Fortran. GFortran (z którego korzystałeś) nie ma zasobów programistycznych do optymalizacji kompilatora Fortran, ponieważ obecnie skupia się na implementacji standardu Fortran 2003 zamiast na optymalizacji.
stali
Uhmm ... Kompilator Intel C / C ++ iccwykonuje również automatyczną równoległość. Dodałem plik icctest.cdo innych źródeł. Czy możesz go skompilować z tymi samymi opcjami, które użyłeś powyżej, uruchomić go i zgłosić czasy? Musiałem dodać instrukcję printf do mojego kodu, aby uniknąć optymalizacji wszystkiego przez gcc. To tylko szybki hack i mam nadzieję, że nie zawiera błędów!
Pedro
Pobrałem najnowsze kompilatory icc / ifort i sam wykonałem testy. Pytanie zostało zaktualizowane, aby uwzględnić te nowe wyniki, tj. Automatyczna automatyzacja Intela działa zarówno w Fortran, jak i C.
Pedro
1
Dzięki. Tak, zauważyłem, że różnica jest niewielka, być może dlatego, że pętle są proste, a operacje są BLAS poziomu 1. Ale, jak powiedziałem wcześniej, ze względu na zdolność Fortrana do wykonywania operacji na całej tablicy i użycia słów kluczowych, takich jak PURE / ELEMENTAL, jest więcej miejsca na optymalizację kompilatora. Sposób, w jaki kompilatory wykorzystują te informacje i co tak naprawdę robi, to inna sprawa. Możesz także wypróbować matmul, jeśli chcesz bpaste.net/show/23035
stali
6

Myślę, że nie tylko interesujące jest to, jak kompilator optymalizuje kod dla nowoczesnego sprzętu. Zwłaszcza między GNU C i GNU Fortran generowanie kodu może być bardzo różne.

Rozważmy więc inny przykład, aby pokazać różnice między nimi.

Używając liczb zespolonych, kompilator GNU C generuje duży narzut dla prawie bardzo podstawowych operacji arytmetycznych na liczbie zespolonej. Kompilator Fortran daje znacznie lepszy kod. Rzućmy okiem na następujący mały przykład w Fortran:

COMPLEX*16 A,B,C
C=A*B

daje (gfortran -g -o complex.fo -c complex.f95; objdump -d -S complex.fo):

C=A*B
  52:   dd 45 e0                fldl   -0x20(%ebp)
  55:   dd 45 e8                fldl   -0x18(%ebp)
  58:   dd 45 d0                fldl   -0x30(%ebp)
  5b:   dd 45 d8                fldl   -0x28(%ebp)
  5e:   d9 c3                   fld    %st(3)
  60:   d8 ca                   fmul   %st(2),%st
  62:   d9 c3                   fld    %st(3)
  64:   d8 ca                   fmul   %st(2),%st
  66:   d9 ca                   fxch   %st(2)
  68:   de cd                   fmulp  %st,%st(5)
  6a:   d9 ca                   fxch   %st(2)
  6c:   de cb                   fmulp  %st,%st(3)
  6e:   de e9                   fsubrp %st,%st(1)
  70:   d9 c9                   fxch   %st(1)
  72:   de c2                   faddp  %st,%st(2)
  74:   dd 5d c0                fstpl  -0x40(%ebp)
  77:   dd 5d c8                fstpl  -0x38(%ebp)

Które mają 39 bajtów kodu maszynowego. Gdy rozważymy to samo w C.

 double complex a,b,c; 
 c=a*b; 

i spójrz na wynik (zrobiony w taki sam sposób jak powyżej), otrzymujemy:

  41:   8d 45 b8                lea    -0x48(%ebp),%eax
  44:   dd 5c 24 1c             fstpl  0x1c(%esp)
  48:   dd 5c 24 14             fstpl  0x14(%esp)
  4c:   dd 5c 24 0c             fstpl  0xc(%esp)
  50:   dd 5c 24 04             fstpl  0x4(%esp)
  54:   89 04 24                mov    %eax,(%esp)
  57:   e8 fc ff ff ff          call   58 <main+0x58>
  5c:   83 ec 04                sub    $0x4,%esp
  5f:   dd 45 b8                fldl   -0x48(%ebp)
  62:   dd 5d c8                fstpl  -0x38(%ebp)
  65:   dd 45 c0                fldl   -0x40(%ebp)
  68:   dd 5d d0                fstpl  -0x30(%ebp)

Które są również 39-bajtowym kodem maszynowym, ale odnoszą się do kroku funkcji 57, wykonuje odpowiednią część pracy i wykonuje żądaną operację. Mamy więc 27-bajtowy kod maszynowy do uruchomienia operacji wielu. Za tą funkcją odpowiada muldc3, którego libgcc_s.sokod maszynowy ma rozmiar 1375 bajtów. Spowalnia to znacznie kod i daje ciekawe wyniki przy korzystaniu z profilera.

Kiedy implementujemy powyższe przykłady BLAS zaxpyi przeprowadzamy ten sam test, kompilator Fortran powinien dawać lepsze wyniki niż kompilator C.

(Użyłem GCC 4.4.3 w tym eksperymencie, ale zauważyłem to zachowanie, które wydaje inne GCC).

Więc moim zdaniem nie myślimy tylko o równoległości i wektoryzacji, kiedy zastanawiamy się, który jest lepszym kompilatorem, musimy również sprawdzić, jak podstawowe rzeczy są tłumaczone na kod asemblera. Jeśli to tłumaczenie daje zły kod, optymalizacja może wykorzystywać te rzeczy tylko jako dane wejściowe.

MK alias Grisu
źródło
1
Właśnie przygotowałem przykład według twojego kodu complex.ci dodałem go do kodu online. Musiałem dodać wszystkie wejścia / wyjścia, aby upewnić się, że nic nie jest zoptymalizowane. Dostaję telefon tylko, __muldc3jeśli nie używam -ffast-math. Ze -O2 -ffast-mathmam 9 linii inlined asemblerze. Czy możesz to potwierdzić?
Pedro
Znalazłem bardziej konkretną przyczynę różnicy w generowanym asemblerze i dodałem to do mojego pytania powyżej.
Pedro
Użycie -O2 prowadzi kompilator do obliczania wszystkich możliwych możliwości w czasie wykonywania, dlatego takie konstrukcje są czasami gubione. Opcji -ffast-matematyki nie należy używać w obliczeniach naukowych, jeśli chcesz polegać na wynikach.
MK alias Grisu,
1
Cóż, przez ten argument (nie -ffast-math) nie powinieneś używać Fortrana do obliczeń o złożonej wartości. Jak opisuję w aktualizacji mojego pytania -ffast-mathlub, bardziej ogólnie, -fcx-limited-rangezmusza gcc do korzystania z tych samych obliczeń o ograniczonym zakresie, które nie są zgodne z IEEE, co są standardowe w Fortran. Więc jeśli chcesz mieć pełny zakres wartości złożonych i poprawić Infs i NaNs, nie powinieneś używać Fortran ...
Pedro
2
@Pedro: Jeśli chcesz, aby GCC zachowywał się jak GFortran wrt. złożone mnożenie i dzielenie, powinieneś użyć -fcx-fortran-rules.
janneb
4

Ludzie,

Uważam tę dyskusję za bardzo interesującą, ale zdziwiłem się, widząc, że zmiana kolejności pętli w przykładzie Matmula zmieniła obraz. Nie mam kompilatora Intel dostępnego na mojej bieżącej maszynie, więc używam gfortran, ale przepisałem pętle w mm_test.f90 na

call cpu_time(start)  
do r=1,runs  
  mat_c=0.0d0  
     do j=1,n  
        do k=1,n  
  do i=1,n  
           mat_c(i,j)=mat_c(i,j)+mat_a(i,k)*mat_b(k,j)  
        end do  
     end do  
  end do  
end do  
call cpu_time(finish)  

zmieniłem całe wyniki dla mojej maszyny.

Wyniki synchronizacji poprzedniej wersji były następujące:

#time ./mm_test_f 10000 100
 matmul    time   6.3620000000000001     
 triple do time   21.420999999999999     

podczas gdy z potrójnymi pętlami ułożonymi ponownie, jak wyżej:

#time ./mm_test_f 10000 100
 matmul    time   6.3929999999999998     
 triple do time   3.9190000000000005    

To jest gcc / gfortran 4.7.2 20121109 na procesorze Intel (R) Core (TM) i7-2600K @ 3.40GHz

Użyto flag kompilatora z pliku Makefile, który tu dostałem ...

Schatzi
źródło
3
Nie jest to zaskakujące, ponieważ przechowywanie macierzy w pamięci sprzyja jednemu zamówieniu, tzn. Jeśli wiersze są przechowywane w sposób ciągły, lepiej jest zapętlić wiersze najbardziej od środka, ponieważ wtedy można załadować każdy wiersz jeden raz do szybkiej pamięci lokalnej w porównaniu do wielokrotnego ładowania (kawałek ), aby uzyskać dostęp do jednego elementu. Zobacz stackoverflow.com/questions/7395556 .
Christian Clason,
Wydaje mi się, że byłem zaskoczony, że „wewnętrzna matmula” nie zostałaby zakodowana do robienia tego w ten sposób. Jest znacznie szybszy z potrójnym zamówieniem w drugi sposób. Wygląda na to, że jest w tym kompilatorze, ponieważ wcześniejsze wersje gfortran, do których mogę się dostać, były bardziej „płaskie” pod względem czasu - nie miało znaczenia, w jaki sposób zrobiłeś multikomputer - zajęło to prawie tyle samo czasu.
Schatzi
-2

To nie języki przyspieszają działanie kodu, chociaż pomagają. To kompilator, procesor i system operacyjny sprawiają, że kody działają szybciej. Porównywanie języków jest po prostu mylące, bezużyteczne i bez znaczenia. Nie ma to żadnego sensu, ponieważ porównujesz dwie zmienne: język i kompilator. Jeśli jeden kod działa szybciej, nie wiesz, ile to jest język ani ile to jest kompilator. Nie rozumiem, dlaczego społeczność informatyczna po prostu tego nie rozumie :-(

Wadud Miah
źródło