O językach

Opublikowane:

29.12.2022

Sama wydajność języka programowania nie oddaje w pełni jego możliwości.

Sama wydajność języka programowania nie oddaje w pełni jego możliwości.

W ostatnim miesiącu pisałem o tym, jak ważna jest możliwość przenoszenia się pomiędzy rozwiązaniami w chmurze, pozwalająca na migrację aplikacji i danych w przypadku podniesienia ceny przez dostawcę lub jego bankructwa.

W tym miesiącu do napisania tego artykułu zainspirowała mnie rozmowa na LinkedIn o raporcie porównującym wydajność różnych języków. Teza tego raportu była taka, że Python, w przeciwieństwie do języków takich jak C i C++, jest bardzo niewydajny w działaniu. Wiele osób zamieszczało komentarze w stylu „Programiści Pythona niszczą planetę” lub „Czy taki język jak Python w ogóle do czegoś się nadaje?”.

Po pierwsze, wskazanie języka i stwierdzenie, że jest „niewydajny”, niczemu nie służy. Język składa się ze składni i semantyki, dzięki czemu przekłada to, co ludzie w nim piszą, na binarne zera i jedynki (inaczej mówiąc kod maszynowy), które zrozumie komputer. Zwykle przekład ten wykonuje program znany jako kompilator lub interpreter.

Kompilatory i interpretery korzystają w procesie konwersji z cykli procesora. Kompilatory najczęściej wykonują konwersję przed uruchomieniem programu, więc zasoby systemowe są wykorzystywane tylko przez działanie tak wygenerowanego kodu maszynowego i dlatego kod wynikowy jest „bardziej wydajny”. Ta wydajność jest czasami uzyskiwana kosztem utraty wielu dodatkowych informacji „wyrzuconych” przez kompilator. Interpretery, w których informacje te często są dostępne na etapie uruchomienia, mogą z nich skorzystać przy debugowaniu. Przykładowo, jeśli w czasie wykonania programu pojawi się problem, interpreter może być w stanie wskazać rzeczywistą instrukcję kodu źródłowego, która sprawia problemy. W przypadku kompilatora takie dane o kodzie źródłowym mogą być już utracone.

Po drugie, nawet w przypadku różnych kompilatorów tego samego języka możemy uzyskać różne poziomy wydajności. W języku C dostępnych jest dużo, bardzo dużo implementacji kompilatorów tego języka, tworzonych przez różne zespoły programistów w różnych firmach, często z powodu odmiennych potrzeb i z innymi poziomami optymalizacji.

Niektóre kompilatory C dostosowane są do systemów zagnieżdżonych i mogą generować kod zoptymalizowany pod względem rozmiaru. Inne, mające dostęp do większej pamięci, optymalizują szybkość działania. Ten sam język, różne optymalizacje. W zasadzie, jeśli przyjrzymy się kompilatorowi GCC, zauważymy wśród opcji różne sposoby optymalizacji i każdy z nich będzie wpływał na „wydajność” kodu w ten czy inny sposób.

Często programiści piszą kod i uruchamiają go bez żadnej optymalizacji, co przyspiesza proces programowania, ale potem włączają optymalizację dla ostatecznej kompilacji.

Mogłoby to być świetne rozwiązanie, jeśli optymalizacja nie powodowałaby błędów, które często są trudne do znalezienia.

Wiele osób czytających wspomniany na początku raport stwierdzało, że „interpretery i języki skryptowe zawsze są mniej wydajne”.

Często słyszałem o tym, jak to skrypty powłoki są niewydajne. Jednak polecenia Uniksa i Linuksa były przez lata przepisywane i są naprawdę wydajne, więc w zależności od aplikacji, dobrze napisany skrypt może być równie wydajny co słabo napisany program w C.

Interpretery często wykonują prekompilację, aby uzyskać „atomy” (lub to, co Java nazywa „ziarnami”), które są potem wykorzystywane przez bardzo wydajny silnik uruchomieniowy.

Istnieje też koncepcja „wątkowego języka interpretowanego”, gdzie tworzony jest „wątek”, który po prostu wywołuje bardzo szybko moduł po module.

Z mojego punktu widzenia C posiada minimalistyczną, czystą składnię, która jednak zmusza programistę do tworzenia własnych procedur lub korzystania z bibliotek, które są dostępne w różnych systemach operacyjnych. Wykorzystanie „wskaźników” w C to też bardzo potężna funkcja, ale często powoduje błędy w kodzie, jeśli programista nie jest ostrożny.

Z jednej strony, inne języki mają wiele wbudowanych funkcji (lub podfunkcji, takich jak czyszczenie pamięci) nie tylko skracających kod, który programista musi napisać, ale też dobrze współdziałających w ramach języka. Z drugiej strony, niektóre języki mają tak skomplikowaną składnię, że programista, który jest tylko śmiertelnikiem, może mieć kłopot z nauczeniem się tego wszystkiego. Przykładem jest tu Ada.

Ludzie pytają mnie, jak mogą zacząć programować. Wskazuję na skrypty powłoki jako „pierwszy język”. W środowisku Uniksa i Linuksa powłoka jest zawsze obecna, a programy narzędziowe, wykorzystywane jako pomoc w pracy, działają dobrze i skutecznie.

Następnie polecam Pythona jako „pierwszy język programowania”. Python jest względnie prosty do nauczenia i dostępnych jest do niego wiele samouczków.

Potem proponuję język maszynowy/asemblera, w zasadzie dowolny, aby programista nauczył się, jak komputer w rzeczywistości działa.

W dalszej kolejności sugeruję naukę C, C++ lub (na dzień dzisiejszy) Rust.

Na koniec dobry programista nauczy się jeszcze więcej języków do specjalnych przypadków, potrzebnych do konkretnej pracy lub naprawy kodu pozostawionego przez innego programistę.

W ten sposób zamienimy łatwość programowania wydajnością algorytmów, przy czym bardzo niewiele zależeć będzie od składki samego języka.

Autor:

maddog-2

Jon „maddog” Hall jest autorem, wykładowcą, informatykiem i jednym z pionierów Wolnego Oprogramowania. Od 1994 roku, kiedy po raz pierwszy spotkał Linusa Torvaldsa i ułatwił przeniesienie jądra na systemy 64-bitowe, pozostaje orędownikiem Linuksa. Obecnie jest prezesem Linux International®.

Autor: Jon „maddog” Hall

Aktualnie przeglądasz

Styczeń 2023 - Nr 227
LM227_Jan-2023

Top 5 czytanych

Znajdź nas na Facebook'u

Opinie naszych czytelników

Nagrody i wyróżnienia