Wniosek tutaj:
O ile lepsze są naprawdę kompilatory Fortran?
jest to, że gfortran i gcc są tak szybkie, jak prosty kod. Chciałem więc spróbować czegoś bardziej skomplikowanego. Wziąłem przykład strzelania do normy spektralnej. Najpierw wstępnie obliczam macierz 2D A (:, :), a następnie obliczam normę. (Myślę, że to rozwiązanie nie jest dozwolone podczas strzelaniny). Zaimplementowałem wersję Fortran i C. Oto kod:
https://github.com/certik/spectral_norm
Najszybsze wersje gfortran to spectral_norm2.f90 i spectral_norm6.f90 (jedna wykorzystuje wbudowane matmul i dot_product Fortrana, druga implementuje te dwie funkcje w kodzie - bez różnicy prędkości). Najszybszy kod C / C ++, jaki udało mi się napisać, to spectral_norm7.cpp. Czasy od wersji git 457d9d9 na moim laptopie wynoszą:
$ time ./spectral_norm6 5500
1.274224153
real 0m2.675s
user 0m2.520s
sys 0m0.132s
$ time ./spectral_norm7 5500
1.274224153
real 0m2.871s
user 0m2.724s
sys 0m0.124s
Więc wersja gfortran jest trochę szybsza. Dlaczego? Jeśli wyślesz żądanie ściągnięcia z szybszą implementacją C (lub po prostu wkleisz kod), zaktualizuję repozytorium.
W Fortranie przekazuję tablicę 2D, natomiast w CI używam tablicy 1D. Możesz swobodnie korzystać z tablicy 2D lub w dowolny inny sposób, który uznasz za odpowiedni.
Jeśli chodzi o kompilatory, porównajmy gcc vs gfortran, icc vs ifort i tak dalej. (W przeciwieństwie do strony z rzutami, która porównuje ifort z gcc.)
Aktualizacja : używając wersji 179dae2, która poprawia matmul3 () w mojej wersji C, są teraz tak szybkie:
$ time ./spectral_norm6 5500
1.274224153
real 0m2.669s
user 0m2.500s
sys 0m0.144s
$ time ./spectral_norm7 5500
1.274224153
real 0m2.665s
user 0m2.472s
sys 0m0.168s
Wersja wektoryzacyjna Pedro poniżej jest szybsza:
$ time ./spectral_norm8 5500
1.274224153
real 0m2.523s
user 0m2.336s
sys 0m0.156s
Wreszcie, jak podają poniższe raporty Laxxy dla kompilatorów Intela, wydaje się, że nie ma tu dużej różnicy, a nawet najprostszy kod Fortrana (spectral_norm1) jest jednym z najszybszych.
Odpowiedzi:
Przede wszystkim dziękuję za opublikowanie tego pytania / wyzwania! Jako zrzeczenie się, jestem rodzimym programistą C z pewnym doświadczeniem w Fortranie i czuję się jak w domu w C, dlatego skupię się tylko na ulepszeniu wersji C. Zapraszam też wszystkich hacków z Fortranu!
Aby przypomnieć początkującym o tym, o co w tym chodzi: Podstawowym założeniem w tym wątku było to, że gcc / fortran i icc / ifort powinny, ponieważ mają one odpowiednio te same zaplecza, produkować równoważny kod dla tego samego (semantycznie identycznego) programu, niezależnie od tego jest w C lub Fortran. Jakość wyniku zależy tylko od jakości odpowiednich wdrożeń.
Grałem trochę z kodem i na moim komputerze (ThinkPad 201x, Intel Core i5 M560, 2,67 GHz), używając wersji
gcc
4.6.1 i następujących flag kompilatora:Napisałem też wektoryzowaną przez SIMD wersję C języka C ++
spectral_norm_vec.c
:Wszystkie trzy wersje zostały skompilowane z tymi samymi flagami i tą samą
gcc
wersją. Zauważ, że zawinąłem wywołanie funkcji głównej w pętli od 0..9, aby uzyskać dokładniejsze czasy.Dzięki „lepszym” flagom kompilatora wersja C ++ przewyższa wersję Fortrana, a ręcznie kodowane wektoryzowane pętle zapewniają jedynie nieznaczną poprawę. Szybkie spojrzenie na asembler dla wersji C ++ pokazuje, że główne pętle również zostały wektoryzowane, aczkolwiek rozwinięte bardziej agresywnie.
Spojrzałem również na asembler wygenerowany przez
gfortran
i oto wielka niespodzianka: brak wektoryzacji. Przypisuję fakt, że jest on tylko nieznacznie wolniejszy, ponieważ problem ma ograniczoną przepustowość, przynajmniej w mojej architekturze. Dla każdego z mnożeń macierzy przemierzanych jest 230 MB danych, co prawie zapełnia wszystkie poziomy pamięci podręcznej. Jeśli użyjesz mniejszej wartości wejściowej, np.100
Różnice wydajności znacznie wzrosną.Na marginesie, zamiast obsesji na punkcie wektoryzacji, wyrównywania i flag kompilatora, najbardziej oczywistą optymalizacją byłoby obliczenie pierwszych kilku iteracji w arytmetyce pojedynczej precyzji, dopóki nie uzyskamy ~ 8 cyfr wyniku. Instrukcje pojedynczej precyzji są nie tylko szybsze, ale ilość pamięci, którą należy przenieść, jest również zmniejszona o połowę.
źródło
gcc
/gfortran
używasz? W poprzednich wątkach różne wersje dawały znacząco różne wyniki.matmul2
w wersji Fortran jest semantycznie równoważny zmatmul3
moją wersją C. Obie wersje są teraz takie same i dlatego powinnygcc
/gfortran
powinny dawać takie same wyniki dla obu, np. Żaden front-end / język nie jest lepszy od drugiego w tym przypadku.gcc
ma tę zaletę, że moglibyśmy wykorzystać wektoryzowane instrukcje, gdybyśmy chcieli.vector_size
atrybutu, aby kod był niezależny od platformy, tzn. używając tej składni,gcc
powinien móc generować wektoryzowany kod dla innych platform, np. używając AltiVec w architekturze IBM Power.odpowiedź użytkownika389 została usunięta, ale pozwól mi stwierdzić, że jestem mocno w jego obozie: nie widzę tego, czego się uczymy, porównując mikro-testy w różnych językach. Nie jest dla mnie zaskoczeniem, że C i Fortran uzyskują prawie taką samą wydajność na tym teście, biorąc pod uwagę jego krótki czas. Benchmark jest również nudny, ponieważ można go łatwo napisać w obu językach w kilkudziesięciu wierszach. Z punktu widzenia oprogramowania nie jest to reprezentatywny przypadek: powinniśmy dbać o oprogramowanie, które ma 10 000 lub 100 000 wierszy kodu i jak kompilują to. Oczywiście w tej skali można szybko dowiedzieć się innych rzeczy: ten język A wymaga 10 000 wierszy, a język B 50 000. Lub na odwrót, w zależności od tego, co chcesz zrobić. I nagle to
Innymi słowy, nie ma dla mnie większego znaczenia, że może moja aplikacja mogłaby być o 50% szybsza, jeśli opracowałbym ją w Fortran 77, jeśli zamiast tego zajmie mi to tylko 1 miesiąc, aby uruchomić ją poprawnie, podczas gdy zajmie mi to 3 miesiące w F77. Problem w tym pytaniu polega na tym, że koncentruje się on na aspekcie (poszczególnych jądrach), który moim zdaniem nie jest istotny w praktyce.
źródło
Okazuje się, że mogę napisać kod Pythona (używając Numpy do wykonywania operacji BLAS) szybciej niż kod Fortran skompilowany z kompilatorem gfortran mojego systemu.
foo1.py:
i sn6a.f90, bardzo lekko zmodyfikowany spectral_norm6.f90:
źródło
Sprawdzono to za pomocą kompilatorów Intel. Przy 11.1 (-szybkim, co sugeruje -O3), a przy 12.0 (-O2) najszybsze to 1,2,6,7 i 8 (tj. „Najprostsze” kody Fortran i C oraz ręcznie wektoryzowane C) - są nie do odróżnienia od siebie po ~ 1,5 s. Testy 3 i 5 (z tablicą jako funkcją) przebiegają wolniej; # 4 Nie mogłem skompilować.
W szczególności, jeśli kompilujemy z 12.0 i -O3, a nie -O2, pierwsze 2 („najprostsze”) kody Fortran spowalniają DUŻO (1,5 -> 10,2 sek.) - to nie pierwszy raz, kiedy widzę coś takiego to, ale może to być najbardziej dramatyczny przykład. Jeśli nadal tak jest w bieżącej wersji, myślę, że dobrym pomysłem byłoby zgłoszenie tego do Intela, ponieważ najwyraźniej coś jest nie tak z ich optymalizacjami w tej dość prostej sprawie.
W przeciwnym razie zgadzam się z Jonathanem, że nie jest to szczególnie pouczające ćwiczenie :)
źródło