Czy funkcjonalne języki programowania mają więcej możliwości optymalizacji czasu kompilacji?

10

Czytałem książkę „Programowanie funkcjonalne w prawdziwym świecie”. Zaczęło się od porównania imperatywnych i funkcjonalnych języków programowania. I stwierdził, w jaki sposób „wartości” i „wyrażenia” w programowaniu funkcjonalnym różnią się od „zmiennych” i „funkcji” programowania imperatywnego. Na podstawie dyskusji opracowałem pomysł, który -

Funkcjonalne języki programowania mają więcej okazji do optymalizacji czasu kompilacji niż ich imperatywne odpowiedniki.

Czy to prawda?

Gulszan
źródło

Odpowiedzi:

16

Funkcjonalne języki programowania zapewniają znacznie większą optymalizację czasu kompilacji. Jednym z powodów jest czystość - współbieżność jest trywialna, ponieważ nie ma stanu. Dzięki temu kompilator może pobierać dwa rozgałęzienia i współbieżnie je łatwo bez zmiany zachowania programu.

Jednocześnie wszystko, co można obliczyć bez stanu (tj. Wszystko, co nie jest monadyczne w haskell), może być obliczone z wyprzedzeniem przez kompilator, ale takie obliczenia mogą być drogie i dlatego prawdopodobnie są wykonywane tylko częściowo.

Ponadto wszystko, co nie jest potrzebne obliczeniowo, można całkowicie zoptymalizować z poziomu programu.

alternatywny
źródło
1
+1: Programowanie bez skutków ubocznych jest bardzo, bardzo łatwe do optymalizacji.
S.Lott,
@mathepic: w rzeczywistości równoległość (w Haskell) zachodzi zarówno w czasie kompilacji, jak i w czasie wykonywania. Czas kompilacji decyduje, czy warto utworzyć odłamek (ziarno wątku), a odłamek przetwarza odłamki, jak to możliwe, w zależności od określonej liczby wątków. Jeśli podasz tylko jeden wątek, odłamki zostaną utworzone, ale zostaną przetworzone jeden po drugim.
Matthieu M.
2
@mathepic: kolejne użycie czystości -> lenistwo i brak obliczeń. Jeśli możesz udowodnić, że wartość nie jest potrzebna, całkowicie usuń obliczenia. Jeśli to konieczne, użyj leniwego młotka. Jeśli wiesz, że będzie to potrzebne, oblicz go na wprost (aby uniknąć leniwego obciążenia). Czystość jest (po prostu) niesamowita :)
Matthieu M.
@ Matthieu dobry punkt
alternatywny
1
@Masse Nawet monada IO jest czysta. Tyle, że wynik „uruchomienia” monady we / wy nie jest.
alternatywnie
7

To, że w zasadzie istnieje więcej możliwości optymalizacji czasu kompilacji dla języków funkcjonalnych niż dla ich imperatywnych odpowiedników, jest prawdopodobnie prawdą.

Bardziej interesujące jest to, że jeśli są one faktycznie zaimplementowane w bieżących kompilatorach i jak istotne są te optymalizacje w praktyce (tj. Końcowa wydajność idiomatycznego kodu „prawdziwego życia” w środowisku produkcyjnym, z przewidywalnymi ustawieniami kompilatora a priori).

np. zgłoszenia Haskella do niesławnej gry Benchmarki języka komputerowego (może to być złe - ale nie jest tak, że w tej chwili jest coś znacznie lepszego) sprawiają wrażenie, że spędzono znaczną ilość czasu na ręczne optymalizacje, które skonfrontowane z twierdzeniem o „możliwych optymalizacjach kompilatora z powodu insert some property about FP languages here” sprawiają, że optymalizacje są (przynajmniej przynajmniej) bardziej teoretyczną możliwością niż istotną rzeczywistością.

Byłbym zadowolony, gdybym udowodnił, że się mylę w tej kwestii.

Alexander Battisti
źródło
1
Przyczyna ręcznej optymalizacji w Haskell ma więcej wspólnego z faktem, że niektóre „proste” operacje są nieco czasochłonne (z punktu widzenia złożoności) w Haskell. Na przykład powiedz, że chcesz uzyskać ostatni element listy. W Javie jest to dość prosta operacja; w Haskell naiwne podejście wymaga przejścia całej listy aż do jej końca (z powodu leniwej natury list), co czyni ją operacją O (n). To (częściowo) przychodzi ręczna optymalizacja.
mipadi,
2
Jestem pewien, że istnieją uzasadnione powody dla ręcznego walcowania optymalizacji Haskella, ale są one niezbędne do „prostych” operacji, dają wrażenie, że większe możliwości optymalizacji kodu są (obecnie) istotne tylko teoretycznie.
Alexander Battisti
6
To bardziej różnica między optymalizacją algorytmów a optymalizacją generowanego kodu. Weźmy na przykład C: jeśli piszę algorytm wyszukiwania, mogę napisać naiwny algorytm, który po prostu przegląda listę, lub mogę użyć wyszukiwania binarnego. W obu przypadkach kompilator zoptymalizuje mój kod, ale nie zmieni naiwnego algorytmu wyszukiwania w wyszukiwanie binarne. Wiele ręcznych optymalizacji w kodzie Haskell ma więcej wspólnego z optymalizacją samych algorytmów, niż z optymalizacją generowanego kodu.
mipadi
2
@mipadi, ale wygląda na to, że prosta wersja algorytmu Haskell nie jest tak dobra, jak prosta wersja algorytmów innych języków. (Podejrzewam, że z powodu niedopasowania między modelem funkcjonalnym a architekturą komputera) Nawet jeśli jest dobry w generowaniu kodu, nie wystarcza, aby rozwiązać ten problem.
Winston Ewert
>> jak źle może być << - Czy wiesz, czy to źle, czy nie? Co masz na myśli mówiąc, że jest „zły”? Proszę, bądź konkretny.
igouy
2

W funkcjonalnym stylu przepływ wartości przez program jest bardzo, bardzo widoczny (zarówno dla kompilatora, jak i programisty). Daje to kompilatorowi dużą swobodę w decydowaniu, gdzie przechowywać wartości, jak długo je przechowywać itd.

W imperatywnym języku kompilator obiecuje programiście model, w którym większość zmiennych odpowiada faktycznym lokalizacjom w pamięci, które pozostają przez określony czas. Potencjalnie każda instrukcja może odczytać (lub zapisać do!) Dowolną z tych lokalizacji, więc kompilator może jedynie zastąpić lokalizacje pamięci alokacją rejestru, scalić dwie zmienne w jedną lokalizację pamięci lub wykonać podobne optymalizacje po przeprowadzeniu drobiazgowej analizy tego, gdzie jeszcze w programie do tej zmiennej można się odwoływać.

Teraz są dwa zastrzeżenia:

  • Społeczność języka programowania poświęciła (zmarnowała?) Wiele wysiłku w ciągu ostatnich pięćdziesięciu lat na opracowanie sprytnych sposobów przeprowadzania tej analizy. Istnieją dobrze znane sztuczki, takie jak kolorowanie rejestrów itp., Które pozwalają na wykonanie niektórych łatwiejszych przypadków przez większość czasu; ale powoduje to duże, wolne kompilatory i ciągły kompromis złożoności kompilacji pod względem jakości wynikowego kodu
  • Jednocześnie większość funkcjonalnych języków programowania również nie jest czysto funkcjonalna; wiele rzeczy, które programy naprawdę muszą zrobić, takie jak reagowanie na operacje wejścia / wyjścia, są z natury niefunkcjonalne, więc żaden kompilator nie może być całkowicie wolny od tych sztuczek, a żaden język całkowicie ich nie omija - nawet Haskell, co jest nieco zbyt pure jak na mój gust (Twój przebieg może się różnić) może jedynie kontrolować i eliminować niefunkcjonalne części twojego kodu, a nie całkowicie ich unikać.

Ale aby odpowiedzieć na ogólne pytanie, tak, funkcjonalny paradygmat daje kompilatorowi dużą swobodę w optymalizacji, której nie ma w bezwzględnie koniecznym otoczeniu.

jimwise
źródło
Wszystko w Haskell jest funkcjonalne, po prostu mainjest to funkcja przekształcająca stan, a nie coś, co wykorzystuje sam stan.
alternatywny
Tak i nie - koncepcyjnie całkiem słusznie jest powiedzieć, że program Haskell jest czystą funkcją nad interakcjami użytkownika z tym programem (i stanem generatora liczb losowych w systemie, a także opóźnieniami sieci dzisiaj i wszelkimi innymi z natury niefunkcjonalne dane wejściowe, na które program reaguje), ale w praktyce jest to rozróżnienie bez różnicy.
jimwise
@ jimwise Przejrzystość referencyjna to bardzo duża różnica.
alternatywny
Tyle że tak naprawdę tego nie masz, przynajmniej do omawianego tutaj celu. Istotą operacji takich jak IO lub generowanie liczb losowych jest to, że powinny zwracać inną wartość za każdym razem, gdy są wywoływane z tymi samymi danymi wejściowymi, co z natury ogranicza funkcjonalne rozumowanie na ich temat, zarówno dla programisty, jak i kompilatora.
jimwise
1
@mathepic Tak, oczywiście możesz koncepcyjnie postrzegać losowość lub dane wejściowe użytkownika (lub opóźnienie sieci lub obciążenie systemu) jako nieskończony strumień lub funkcję od stanu do stanu, ale nie jest to widok, który nadaje się do użytecznego rozumowania na temat zachowania programu przez ty lub twój kompilator - Bob Harper dobrze omawia tę kwestię w ostatnim poście na swoim blogu o nowym programie CSU dotyczącym programowania funkcjonalnego.
jimwise