Który język programowania jest najszybszy?

W szranki staje 10 najpopularniejszych języków z GitHuba, na których można odpalić porównywalne zadania. Poznajmy uczestników: JavaScript, Java, Python, Ruby, PHP, C++, C#, C, Go, Swift. Swift dostał dziką kartę, w związku z tym, że Objective-C odchodzi do lamusa. Na pierwszy rzut oka wydaje się, że C i C++ zdeklasują konkurencję. Go, JavaScript (od powstania V8) i Swift są dość powszechnie uważane za szybkie. Za moment dowiemy się jaka jest...

 

... Prawda

No właśnie! Jaka jest ta prawda? Taka, że ciężko ustalić jednoznaczną odpowiedź na to pytanie, bo jak to w przypadku inżynierii - to zależy. Zależy od tego jakiego typu operacje dany test mierzy, zależy od testowanej implementacji i pomimo działania zgodnego ze sztuką ten wynik i tak będzie daleki od tego co zobaczymy w rzeczywistej aplikacji. Dążenie do poznania prędkości rzeczywistej aplikacji też jest w dużej mierze ułudą, tam wiele zależy od bazy danych czy nawet od architektury aplikacji. Dodatkowo w realnym świecie CPU rzadko ogranicza prędkość działania programu, częściej będziemy mieli do czynienia z problemami z I/O. Stąd wynika, że porównanie musi być syntetyczne, by odizolować możliwie dużo czynników i możliwie proste, by nie gubić się w problemach z architekturą.

Z pomocą przychodzi “The Computer Language Benchmarks Game”. Jest to projekt prowadzony z przerwami od 2002 roku. Polega implementowaniu w różnych językach programowania tych samych, prostych zadań i odpalaniu ich na tym samym sprzęcie. Nie dość, że walczą ze sobą języki, to walczą też implementacje. Oczywiście takie porównywanie ze sobą prędkości nie jest idealne, ale udostępnione dane można opracować w ciekawy sposób. Interesujące są też dodatkowe informacje, które można wyczytać na stronie projektu - o poprawnym mierzeniu czasu wykonania, o tym, że czas wykonania programu w Javie czy JavaScripcie w syntetycznym teście będzie nieco inny niż w większej, obciążonej aplikacji. Polecam lekturę!

Nasze 10 języków zostanie porównane przy pomocy 9 benchmarków. Sam czas wykonania zadania jest mało interesujący, gdyż nie daje odniesienia. Dlatego by lepiej pokazać jak dany wynik się ma do reszty języków dzielę czas jaki uzyskał dany język przez najlepszy czas dla tego benchmarku w ogóle. Na wykresie natomiast naniosę 3 dane - najlepszy relatywyny wynik, najgorszy relatywy wynik i średnią geometryczną

 

Niech przemówią dane

Który język programowania jest najszybszy?

Na dwóch pierwszych pozycjach bez niespodzianek. C wyprzedziło C++. Było to do przewidzenia, w końcu C z tego grona jest względnie najbliższy kodowi maszynowemu i będzie wykonywać najmniej operacji, stąd szybkość (tak, uproszczenie, chodzi bardziej o to, że łatwiej to uzyskać niż w językach wyższego poziomu). C++ na zmierzonych testach jest około 45% wolniejsze niż C - ale na tę liczbę rzutuje głównie słaby wynik w teście wyrażeń regularnych, gdzie C++ było aż 11x wolniejsze niż C. C++ używało biblioteki wyrażeń regularnych z Boosta a C PCRE, więc na ile ten konkretny benchmark mierzy możliwości języka jest nieco dyskusyjne. Gdyby odrzucić ten wynik okaże się, że C było tylko 10% szybsze.

Na 3 miejscu podium - Java. Tak, ten sam język, który oskarżany jest o bycie wolnym. Kto z nas nie korzystał z jakiegoś dziwnego javowego API, które odpowiadało przy dobrych wiatrach po sekundzie? Ten dysonans między benchmarkiem a rzeczywistością dobitnie pokazuje, że najbardziej liczy się to jak wykorzystujesz język i nawet najszybszy z nich nie uratuje cię gdy nie uwzględnisz problemów z I/O i zastosujesz algorytmy o złożoności nie przystającej do przetwarzanej ilości danych. A wracając do zalet Javy to 3 miejsce wywalczyła głównie tym, że żaden z 9 testów nie znalazł słabego jej punktu. Najgorszy wynik to 8,5x wolniej niż C w przypadku benchmarku z wyrażeniami regularnymi. W sumie okazała się tylko 2,7x wolniejsza niż C.

Kolejny miejsce to najnowszy język w tym zestawieniu - Swift, zaprezentowany przez Apple w 2014 roku. Jak widać rozwój języka idzie świetnie. Przy okazji tego, że jest dużo lepszy niż Objective-C, to jest bardzo szybki. Tylko 2,95x wyniku C. Jest to kolejny język, który miał problem z wyrażeniami regularnymi, gdyby usunąć je z zestawienia to prawdopodobnie by wylądował na podium. Szybkość Swifta może się przydać w kontekście tego, że może być uruchamiany nie tylko na iOS, ale też na innych platformach. Są liczne próby pisania aplikacji backendowych w tym języku.

W połowie stawki melduje się C#. Język Microsoftu uzyskał wynik 3,1x gorszy niż C, ale w wielu benchmarkach prezentował równą formę - uzyskując czasy między 2 a 3 krotność najlepszego wyniku. Co prawda największy konkurent - Java - pokonała go, ale C# był od niej wolniejszy tylko o 15%. To jest różnica, która w realnym świecie może być łatwo zniwelowana.

Miejsce 6 należy do Go. To o tyle ciekawe, że spodziewałem się wyżej tego języka, szczególnie, że jednym haseł sprzedażowych Go jest szybkość. Jego wynik to 3,25x czas wykonania testów w C. Go miałoby szansę w walce o podium gdyby nie kiepskie wyniki w problemie z drzewami binarnymi i wyrażeniami regularnymi. W świecie realnego developmentu sytuacja Golanga wygląda o tyle lepiej, że koncentruje się na prostocie i lekkiej wielowątkowości. Obydwie cechy są bardzo mocnym atutem dla programistów.

Kolejny jest JavaScript (warto dodać, że pod postacią Node.js). Tu widać już spory spadek formy, gdyż dla naszych 9 testów jest ponad 2x wolniejszy niż Go, które jest na poprzednim miejscu. Względem C to różnica 7,7x. Co ciekawe - mimo, że już kilkukrotnie wolniejsze niż C, to JavaScript nadal jest uważany za szybki język backendowy. Zaraz wyjaśni się dlaczego.

Miejsce 3 od końca tego zestawienia zajęło PHP - tak bardzo popularne i tak bardzo nielubiane w pewnych kręgach. Warto zauważyć, że bardzo wiele serwisów z dużym ruchem działa bez problemu właśnie na PHP. Mimo, że to język ponad 18x wolniejszy niż C. To już ponad rząd wielkości wolniej.

Dwa ostatnie miejsca omówimy jako parkę. Ta parka to Python (prawie 40x wolniejszy niż C) i Ruby (nieco ponad 44x wolniejsze niż C). To dwa języki, które bez skrępowania wykręcały kilkuminutowe czasy wykonania testów. Trzeba przyznać - to już nie są zawrotne prędkości. Mimo to obydwa języki sprawdzają się w web devie, a Python ostatnio robi furorę w innych obszarach takich jak data science, machine learning i AI. W obydwu czynnikiem mocno zachęcającym jest ładna, ekspresyjna i czytelna składnia.

Teraz już wiemy które języki zasuwają jak powyższe chomiczki, a które nie.

 

No i co z tego?

Pewnie dla szarego programisty, który nie pracuje przy niewiadomo jak obciążonych aplikacjach to w zasadzie tylko ciekawostka. Nie poczuje przecież potrzeby zmiany języka programowania ze względów wydajnościowych, szczególnie, że po drodze jest mnóstwo komponentów, które można zoptymalizować. Ba, nawet z kiepsko napisaną, powolną aplikacją przy rozsądnym cache’owaniu można zajść dość daleko. Gorzej gdy ktoś nie zauważy, że potrzeba optymalizacji. Tak się stało z Naszej Klasie u szczytu jej sukcesu - ewidentnie widać było, że ta aplikacja nie została zaprojektowana pod duży ruch. Szybkość języka ma oczywiście duże znaczenie właśnie w przypadku dużego ruchu właśnie albo w przypadku przetwarzania w czasie bliskim rzeczywistemu. Tu raczej Ruby i Pythona nie znajdziemy. Facebook zdecydował, że wydajność PHP to za mało i rozwinął maszynę wirtualną do odpalania tego języka, aby dać sobie radę z rosnącą liczbą użytkowników. Pewnie gdyby pisali w Javie nie robili by maszyny wirtualnej, ale rozwiązywali inne problemy związane z tą technologią.

Trzeba przyznać, że mierzenie wydajności w ten sposób i przywiązywanie dużej wagi do konkretnego miejsca, i wyniku do dwóch miejsc po przecinku ma niewielki sens. Tak naprawdę szybkość C i C++ może być porównywalna, tak jak Javy, Swifta, C# i Go. Możliwe też, że Node.js nie będzie w realnym świecie aż 2x wolniejszy niż Go. Natomiast to, że PHP jest ponad 2x szybsze niż Python i Ruby będzie odczuwalne w wielu zastosowaniach, tak samo jak to, że nie dotrzymuje kroku czołówce.

Na liście implementacji widać wiele podejść do napisania tej najszybszej. W niektórych językach - szczególnie tych wysokiego poziomu - znajdziemy po kilka prób, gdzie najszybsza od najwolniejszej różni się prawie o rząd wielkości. Stąd wynika, że napisanie szybkiego kodu może być trudne i wymaga włożenia w to energii, czasu i myśli. Pokazuje też, że czasem wystarczy zmienić implementację, a nie język, żeby dostać przyzwoicie działający kod.

PS: Jest jeszcze jeden kozacki język, którego nie ma w tym zestawieniu. Rust - pobił on w dwóch benchmarkach C, mimo to ogólnie jest 27% wolniejszy. Nieźle jak na język, w którym nie musisz robić malloc i free.