Apple wprowadził nowy język programowania Swift na WWDC14 . W prezentacji dokonali porównań wydajności między Objective-C i Python. Poniżej znajduje się zdjęcie jednego ze slajdów, porównania tych trzech języków wykonujących skomplikowane sortowanie obiektów:
Był jeszcze bardziej niesamowity wykres porównania wydajności przy użyciu algorytmu szyfrowania RC4 .
Oczywiście jest to rozmowa marketingowa i nie szczegółowo opisali, w jaki sposób została zaimplementowana w każdym z nich. Ale zastanawiam się:
- Jak nowy język programowania może być znacznie szybszy?
- Czy wyniki Objective-C są spowodowane złym kompilatorem, czy jest coś mniej wydajnego w Objective-C niż Swift?
- Jak wytłumaczysz wzrost wydajności o 40%? Rozumiem, że wyrzucanie elementów bezużytecznych / automatyczna kontrola referencji może generować dodatkowe koszty, ale tyle?
Odpowiedzi:
Po pierwsze (IMO) porównywanie z Pythonem jest prawie bez znaczenia. Jedyne porównanie z Objective-C ma znaczenie.
Cel C jest wolnym językiem. (Tylko część C jest szybka, ale to dlatego, że to C) Nigdy nie była wyjątkowo szybka. Był wystarczająco szybki do ich celu (Apple) i szybszy niż ich starsze wersje. I było wolno, ponieważ ...
Cel C gwarantował dynamiczne wysłanie każdej metody. W ogóle nie ma wysyłki statycznej. To uniemożliwiło dalszą optymalizację programu Objective-C. Cóż, może technologia JIT może być pomocna, ale AFAIK, Apple naprawdę nienawidzi nieprzewidywalnych parametrów wydajności i żywotności obiektu. Nie sądzę, żeby przyjęli jakieś JIT. Swift nie ma takiej dynamicznej gwarancji wysyłki, chyba że umieścisz jakiś specjalny atrybut dla zgodności z Objective-C.
GC lub RC nie ma tutaj znaczenia. Swift przede wszystkim wykorzystuje RC. Nie ma GC, a także nie, chyba że nastąpi ogromny skok architektoniczny w technologii GC. (IMO, to na zawsze) Wierzę, że Swift ma dużo więcej miejsca na optymalizację statyczną. Szczególnie algorytmy szyfrowania niskiego poziomu, ponieważ zwykle opierają się one na ogromnych obliczeniach numerycznych, co jest ogromną wygraną dla języków statycznie wysyłających.
Byłem zaskoczony, bo 40% wydaje się za małe. Spodziewałem się znacznie więcej. W każdym razie jest to pierwsze wydanie i myślę, że optymalizacja nie była głównym problemem. Swift nie jest nawet kompletny! Sprawią, że będzie lepiej.
Aktualizacja
Niektórzy mnie denerwują, twierdząc, że technologia GC jest lepsza. Chociaż poniższe rzeczy mogą być dyskusyjne i to tylko moja bardzo stronnicza opinia, ale myślę, że muszę powiedzieć, aby uniknąć tego niepotrzebnego argumentu.
Wiem, czym są GC konserwatywne / śledzące / generacyjne / przyrostowe / równoległe / w czasie rzeczywistym i jak się różnią. Myślę, że większość czytelników już to wie. Zgadzam się również, że GC jest bardzo fajny w niektórych dziedzinach, a także wykazuje wysoką przepustowość w niektórych przypadkach.
W każdym razie podejrzewam, że twierdzenie o przepustowości GC jest zawsze lepsze niż RC. Większość kosztów RC pochodzi z operacji ponownego liczenia i blokowania w celu ochrony zmiennej liczby zliczeń. A implementacja RC zazwyczaj zapewnia sposób uniknięcia operacji liczenia. W Objective-C jest
__unsafe_unretained
i w Swift (choć dla mnie to nadal jest trochę niejasne)unowned
. Jeśli koszt operacji ponownego liczenia jest nie do przyjęcia, możesz spróbować zrezygnować z nich selektywnie, korzystając z mechaniki. Teoretycznie możemy symulować niemal unikalny scenariusz własności, używając bardzo agresywnych referencji, aby uniknąć narzutu RC. Spodziewam się również, że kompilator może automatycznie wyeliminować niektóre oczywiste niepotrzebne operacje RC.W przeciwieństwie do systemu RC, AFAIK, częściowa rezygnacja z typów referencyjnych nie jest opcją w systemie GC.
Wiem, że wiele wydanych grafik i gier korzysta z systemu opartego na GC, a także wiem, że większość z nich cierpi z powodu braku determinizmu. Nie tylko dla charakterystyki wydajności, ale także zarządzania czasem życia obiektu. Jedność jest napisana głównie w C ++, ale niewielka część C # powoduje wszystkie dziwne problemy z wydajnością. Hybrydowe aplikacje HTML i wciąż cierpiące z powodu nieprzewidywalnych skoków w dowolnym systemie. Powszechnie stosowany nie oznacza, że jest lepszy. Oznacza to po prostu, że jest to łatwe i popularne dla osób, które nie mają wielu opcji.
Aktualizacja 2
Ponownie, aby uniknąć niepotrzebnych dyskusji lub dyskusji, dodaję więcej szczegółów.
@Asik przedstawił interesującą opinię na temat szczytów GC. Dlatego możemy traktować podejście typu wartość wszędzie jako sposób na rezygnację z GC. Jest to dość atrakcyjne, a nawet możliwe do wykonania w niektórych systemach (na przykład podejście czysto funkcjonalne). Zgadzam się, że to ładnie w teorii. Ale w praktyce ma kilka problemów. Największym problemem jest częściowe zastosowanie tej sztuczki, która nie zapewnia prawdziwej charakterystyki bezskokowej.
Ponieważ problem z opóźnieniem zawsze stanowi problem „ wszystko albo nic” . Jeśli masz jeden skok ramki na 10 sekund (= 600 klatek), wtedy cały system najwyraźniej zawodzi. Tu nie chodzi o to, jak lepiej czy gorzej. To po prostu przekazać lub nie. (lub mniej niż 0,0001%) Więc gdzie jest źródło skoku GC? To zły rozkład obciążenia GC. A to dlatego, że GC jest zasadniczo nieokreślony. Jeśli zrobisz jakieś śmieci, aktywuje ono GC, a w końcu nastąpi skok. Oczywiście w idealnym świecie, w którym obciążenie GC będzie zawsze idealne, tak się nie stanie, ale żyję raczej w prawdziwym świecie niż w wyimaginowanym świecie idealnym.
Następnie, jeśli chcesz uniknąć kolca, musisz usunąć wszystkie typy ref z całego systemu. Jest to jednak trudne, szalone, a nawet niemożliwe ze względu na niemożliwą do usunięcia część, taką jak system .NET core i biblioteka. Korzystanie z systemu innego niż GC jest znacznie łatwiejsze .
W przeciwieństwie do GC, RC jest zasadniczo deterministyczny i nie musisz używać tej szalonej optymalizacji (tylko z typem wyłącznie wartości) tylko po to, aby uniknąć gwałtownego wzrostu. Musisz tylko wyśledzić i zoptymalizować część, która powoduje skok. W systemach RC skok jest problemem lokalnym algorytmem, ale w systemach GC, skok jest zawsze problemem globalnym.
Wydaje mi się, że moja odpowiedź nie jest już zbytnio tematyczna, a przeważnie jest to powtórzenie istniejących dyskusji. Jeśli naprawdę chcesz się pochwalić wyższością / niższością / alternatywą lub czymkolwiek innym w GC / RC, istnieje wiele dyskusji na tej stronie i StackOverflow, i możesz dalej tam walczyć.
źródło
Będąc 3,9 razy szybszym od Pythona, język, który konsekwentnie traci większość benchmarków o znaczny margines (ok, jest na równi z Perlem, Ruby i PHP; ale przegrywa z jakimkolwiek statycznym typem), nie powinien się niczym chwalić.
Test porównawczy pokazuje programy C ++, które w większości przypadków są o ponad rząd wielkości szybsze niż programy pythonowe. Nie jest to znacznie lepsze w porównaniu z Javą, C # (na Mono), OCaml, Haskell, a nawet Clojure, który nie jest statycznie wpisany.
Tak więc prawdziwe pytanie brzmi: w jaki sposób Objective-C jest tylko 2,8 razy szybszy od Pythona. Najwyraźniej ostrożnie wybrali punkt odniesienia, w którym bardzo boli powolna, w pełni dynamiczna wysyłka ObjC. Każdy statycznie wpisany język powinien być w stanie radzić sobie lepiej. W złożonym sortowaniu obiektów istnieje wiele wywołań metod służących do porównywania obiektów, a rzeczywiste porównanie prawdopodobnie nie było bardzo skomplikowane. Jeśli więc Swift przynajmniej skorzysta z informacji o typie, może z łatwością lepiej wykonywać wywołania metod i nie ma wystarczającej liczby innych operacji, w których ObjC mógłby być lepszy.
Oczywiście, jak wyraźnie pokazuje gra porównawcza , względna wydajność w różnych zadaniach jest bardzo zróżnicowana, więc jeden test porównawczy nie jest tak naprawdę reprezentatywny. Gdyby mieli benchmark, w którym miałoby to większą przewagę, pokazaliby nam to zamiast tego, więc przy innych zadaniach prawdopodobnie nie jest ani lepiej, ani wcale.
źródło
Cel C dynamicznie wywołuje każde wywołanie metody.
Hipoteza: Test porównawczy używa pisania statycznego, aby umożliwić kompilatorowi Swift
compare
wyciągnięcie metody zsort
pętli. Wymaga to wąskiego typu ograniczenia, które zezwala tylko na obiekty złożone w tablicy, a nie na podklasy złożone.(W Objective-C możesz naprawdę ręcznie wyszukać metodę, jeśli naprawdę chcesz, wywołując obsługę środowiska wykonawczego języka w celu wyszukania wskaźnika metody. Lepiej upewnij się, że wszystkie instancje w tablicy należą do tej samej klasy .)
Hipoteza: Swift optymalizuje wywołania liczenia referencji poza pętlą.
Hipoteza: Test porównawczy Swift używa Złożonej struktury zamiast obiektu Objective-C, więc porównania sortowania nie wymagają dynamicznych wysyłek metod (ponieważ nie można ich podklasować) ani pracy z liczeniem referencji (ponieważ jest to typ wartości).
(W Objective-C możesz wrócić do C / C ++ pod względem wydajności, o ile nie obejmuje on obiektów Objective-C, np. Posortuj tablicę C struktur).
źródło
Szczerze mówiąc, chyba że udostępnią źródło testów, których używają, nie ufałbym nic, co Apple ma do powiedzenia na ten temat. Pamiętaj, że jest to firma, która przeszła z PPC na Intel na podstawie obaw o moc, gdy 6 miesięcy wcześniej mówili, że Intel ssał i faktycznie podpalił króla Intela w reklamie. Chciałbym zobaczyć twardy niezaprzeczalny dowód, że Swift jest szybszy niż ObjC w większej liczbie kategorii niż tylko sortowanie.
Dodatkowo musisz zakwestionować wszelkie statystyki, które są publikowane w WWDC, ponieważ mają w sobie zapach marketingu.
Biorąc to wszystko pod uwagę, nie przeprowadziłem żadnych testów między swift a ObjC, ale z tego, co wiem, swift ma swoje własne rozszerzenia LLVM IR i możliwe jest, że w czasie kompilacji przeprowadzana jest większa optymalizacja niż w ObjC.
Pełne ujawnienie: Piszę kompilator Swift typu open source znajdujący się pod adresem https://ind.ie/phoenix/
Jeśli ktoś chciałby pomóc w upewnieniu się, że Swift nie jest dostępny tylko na sprzęcie Apple, daj mi znać, a chętnie Cię dołączę.
źródło
Zmagałem się z samouczkiem Swift i wydaje mi się, że Swift jest bardziej przyziemny (kojarzy mi się z Visual Basic) z mniejszym „uzewnętrznieniem obiektów” niż Objective-C. Gdyby wzięli pod uwagę C lub C ++, zakładam, że ten drugi by wygrał, ponieważ są one jeszcze bardziej czas kompilacji.
W tym przypadku zakładam, że Objective-C jest ofiarą swojej obiektowej czystości (i narzutu).
źródło
qsort
co pozwoli na skomplikowane sortowanie obiektów; po prostu używa wywołania zwrotnego, które rozumie obiekty pod ręką. Podejrzewam, że brakuje C ++, ponieważstd::sort
zawstydziłby Swifta. (Ponieważ jest to szablon, kompilator C ++ może go znacznie zoptymalizować, aż do rozwinięcia pętli.)