Kiedy studiowałem na uniwersytecie, często słyszałem pomysł, że kompilatory Fortran produkują szybszy kod niż kompilatory C dla równoważnego programu.
Kluczowe rozumowanie wyglądało następująco: kompilator Fortran emituje średnio 1,1 instrukcji procesora na wiersz kodu, podczas gdy kompilator C emituje średnio 1,6 instrukcji procesora na wiersz kodu - nie pamiętam dokładnych liczb, ale Pomysł polegał na tym, że kompilatory C emitowały zauważalnie więcej kodu maszynowego i dlatego produkowały wolniejsze programy.
Jak ważne jest takie porównanie? Czy możemy powiedzieć, że kompilatory Fortran produkują szybsze programy niż kompilatory C lub odwrotnie i dlaczego ta różnica istnieje?
c
comparison
compiler
fortran
sharptooth
źródło
źródło
Odpowiedzi:
IIRC jednym z głównych powodów, dla których mówi się, że Fortran jest szybszy, jest brak aliasingu wskaźnika , dzięki czemu mogą korzystać z optymalizacji, których nie mogą użyć kompilatory C:
Ale zgadzam się z innymi tutaj: porównywanie średniej liczby instrukcji asemblera wygenerowanych dla linii kodu jest kompletnym nonsensem. Na przykład nowoczesny rdzeń x86 może wykonywać dwie instrukcje równolegle, jeśli nie mają dostępu do tych samych rejestrów. Dzięki temu możesz (teoretycznie) zwiększyć wydajność o 100% dla tego samego zestawu instrukcji , zmieniając ich kolejność . Dobre kompilatory często generują również więcej instrukcji asemblowania, aby uzyskać szybszy kod (myśl rozwijanie pętli, wstawianie). Całkowita liczba instrukcji asemblera niewiele mówi o wydajności fragmentu kodu.
źródło
restrict
kluczowe C pozwala autorowi funkcji określić, że wskaźnik nie ma aliasów. Czy to wystarczy, aby zaradzić różnicy, czy może jest coś więcej?Całkowicie nieprawidłowe porównanie.
Po pierwsze, jak zauważa @ Péter Török, musisz najpierw porównać liczbę linii w równoważnych programach z Fortran i C, aby było to nawet prawidłowe porównanie liczby wyprodukowanych linii.
Po drugie, mniej linii kodu nie zawsze oznacza szybsze programy . Nie wszystkie instrukcje maszyny wykonują tę samą liczbę cykli , ale występują również inne problemy, takie jak dostęp do pamięci , buforowanie itp.
Ponadto długie przebiegi kodu mogą być szybsze, ponieważ skutkuje to mniejszą liczbą wykonanych linii (tj. Liczba wierszy ! = Liczba wykonanych linii ).
źródło
Dan ma rację, dłuższe programy nie oznaczają wolniejszych programów. Bardzo zależy od tego, co robią.
Nie jestem ekspertem od Fortran, wiem trochę. Porównując je, uważam, że dobrze napisane C osiągnęłoby znacznie lepszą wydajność przy bardziej złożonych strukturach danych i funkcjonalności niż Fortran. Ktoś (proszę) popraw mnie, jeśli się tutaj mylę, ale myślę, że Fortran jest nieco na „niższym poziomie” niż C. Jeśli tak, jestem pewien, że pewne problemy pojawią się szybciej w Fortranie.
Kolejna rzecz, na pierwszy rzut oka myślałem, że pytasz, czy kompilatory są szybsze. Właściwie uważam, że Fortran generalnie kompiluje się szybciej dla podobnych ilości kodu, ale wynikowy program i sposób jego działania byłyby inną historią. Analizowanie jest prostsze.
źródło
Myślę, że po części jest to, że kompilatory FORTRAN są zaprojektowane do szybkiego wykonywania niektórych rodzajów matematyki. Dlatego ludzie korzystają z FORTRAN, aby wykonywać obliczenia tak szybko, jak to możliwe
źródło
To stwierdzenie mogło być prawdziwe w dawnych czasach (około późnych lat 70.), gdy C był w powijakach, a Fortran był wspierany przez wszystkich głównych producentów i był wysoce zoptymalizowany. Wczesne Fortrans były oparte na architekturze IBM, tak proste rzeczy jak arytmetyka, gdyby na pewno byłyby to jedna instrukcja na instrukcję montażu. Dotyczy to starszych maszyn, takich jak Data General i Prime, które miały 3 skoki. Nie działa to na nowoczesnych zestawach instrukcji, które nie mają skoku w 3 kierunkach.
Linie kodu nie są równe instrukcjom kodu. Wcześniejsze wersje Fortran dopuszczały tylko jedną instrukcję w wierszu. Późniejsze wersje Fortran mogą przyjmować wiele instrukcji w wierszu. C może mieć wiele instrukcji w wierszu. W szybszych kompilatorach produkcyjnych, takich jak Intel IVF (wcześniej CVF, MS Powerstation) i Intel C, naprawdę nie ma między nimi żadnej różnicy. Te kompilatory są wysoce zoptymalizowane.
źródło
FORTRAN w starym stylu wymagał, aby programista, który chciał udostępnić część tablicy dla funkcji, musiał przekazać odwołanie do całej tablicy, wraz z jedną lub większą liczbą liczb całkowitych określającą indeks dolny i indeks dolny końcowy lub liczbę elementów . C umożliwia uproszczenie tego do przekazywania wskaźnika na początek interesującej części wraz z liczbą elementów. Mówiąc wprost, przyspieszyłoby to sprawę (przekazując dwie rzeczy zamiast trzech). Pośrednio może to jednak spowolnić proces, ograniczając rodzaje optymalizacji, które może przeprowadzić kompilator.
Rozważ funkcję:
jeśli kompilator wiedziałby, że każdy ze wskaźników identyfikuje początek tablicy, mógłby wygenerować kod, który działałby na elementy tablicy równolegle lub w dowolnej kolejności, ponieważ dla dowolnego x! = y operacje na dest [x ] nie wpłynie na src1 [y] ani src2 [y]. Na przykład w niektórych systemach kompilator może skorzystać z generowania kodu odpowiadającego:
Zauważ, że każda operacja, która ładuje lub oblicza wartość, ma co najmniej jedną dodatkową operację między nią a następną operacją, która korzysta z tej wartości. Niektóre procesory mogą nakładać się na przetwarzanie różnych operacji, gdy takie warunki są spełnione, co poprawia wydajność. Zauważ jednak, że ponieważ kompilator C nie ma możliwości dowiedzenia się, że kod nie zostanie przekazany wskaźnikom do częściowo pokrywających się regionów wspólnej tablicy, kompilator C nie może dokonać powyższej transformacji. Kompilatory FORTRAN, które otrzymały równoważny kod, mogły jednak dokonać takiej transformacji.
Podczas gdy programista C mógłby próbować osiągnąć porównywalną wydajność poprzez jawne wypisanie kodu, który rozwinął pętlę i nałożył się na operacje sąsiednich przejść, taki kod mógłby łatwo obniżyć wydajność, gdyby użył tak wielu zmiennych automatycznych, że kompilator musiałby je „przelać” do pamięć. Optymalizator kompilatora FORTRAN prawdopodobnie wiedziałby więcej niż programista o tym, jakie formy przeplotu zapewniłyby optymalną wydajność w danym scenariuszu, i takie decyzje często najlepiej pozostawić takim kompilatorom. Podczas gdy C99 próbował nieco poprawić sytuację C, dodając
restrict
kwalifikator, można go tutaj użyć tylko wtedy, gdydest[]
jest oddzielną tablicą od obusrc1[]
isrc2[]
, lub jeśli programista dodał osobne wersje pętli, aby obsłużyć przypadki, w których wszystkodest
było rozłącznesrc1
isrc2
gdziesrc1[]
idest
były równe isrc2
były rozłączne, gdziesrc2[]
idest[]
były równe isrc1
były rozłączne, i gdzie wszystkie trzy tablice były równe. FORTRAN, z drugiej strony, mógłby bez problemu obsługiwać wszystkie cztery przypadki przy użyciu tego samego kodu źródłowego i tego samego kodu maszynowego.źródło