Jak Swift może być znacznie szybszy od Celu C w tych porównaniach?

115

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:

wprowadź opis zdjęcia tutaj

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ę:

  1. Jak nowy język programowania może być znacznie szybszy?
  2. Czy wyniki Objective-C są spowodowane złym kompilatorem, czy jest coś mniej wydajnego w Objective-C niż Swift?
  3. 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?
Żółty
źródło
13
@MathewFoscarini Obj-C idzie do asemblera, ale ma drogi mechanizm wysyłania wiadomości obiektowych. Dla większości prac GUI to nie ma znaczenia, ale przy sortowaniu ma to duże znaczenie.
Donal Fellows
17
Porównanie do Pythona jest tutaj prawdziwym drapieżnikiem.
asthasr
9
@syrion marketing, a język wydaje się zapożyczać ze składni Pythona (podobnie jak golang). Próbują powiedzieć „hej, programiści pythona, możesz napisać coś niezbyt obcojęzycznego na komputerze Mac i
4
@MichaelT Rozumiem, ale to wciąż dziwne. Każdy, kto wie coś o językach, zorientuje się, że Python, jako język interpretowany, po prostu nie będzie na tym samym boisku, co Objective-C lub inne skompilowane języki (do większości zadań). Używanie go jako punktu odniesienia jest dziwne.
asthasr
7
Prawdopodobnie oznaczają czas potrzebny na napisanie kodu ...
Lukas Eder

Odpowiedzi:

62

Po pierwsze (IMO) porównywanie z Pythonem jest prawie bez znaczenia. Jedyne porównanie z Objective-C ma znaczenie.

  • Jak nowy język programowania może być znacznie szybszy?

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ż ...

  • Czy Objective-C wynika ze złego kompilatora, czy jest coś mniej wydajnego w Objective-C niż Swift?

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.

  • 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?

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_unretainedi 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ć.

Eonil
źródło
4
Odśmiecanie, szczególnie pokoleniowe, jest zwykle znacznie szybsze niż liczenie referencji.
Jan Hudec
9
@JanHudec Twoje znacznie szybsze jest po prostu bez znaczenia w dziedzinie grafiki w czasie rzeczywistym. Dlatego wspominam, że GC wymaga ogromnego skoku . Generacyjne GC nie jest nawet bliskie braku kolców zarówno teoretycznie, jak i praktycznie.
Eonil
5
Szybsze i bezkolcowe są kategoriami całkowicie ortogonalnymi. Generatory śmieciarek są szybsze . Są one nie kolec darmo.
Jan Hudec
4
Mówisz o przepustowości . Szybsze określenie zawsze było niejasne i może mieć znaczenie w kontekście. Jeśli chcesz spierać się o znaczenie terminów, powinieneś użyć bardziej precyzyjnego terminu, a nie szybciej, zwłaszcza biorąc pod uwagę obecny kontekst - grafikę w czasie rzeczywistym.
Eonil
11
@JanHudec: Na urządzeniach mobilnych lub dowolnym urządzeniu z ograniczonymi zasobami GC nie jest znacznie szybszy i w rzeczywistości jest główną częścią problemu.
Mason Wheeler
72

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.

Jan Hudec
źródło
13
Nie do końca rozumiem sens tej odpowiedzi. Czy odpowiadasz „jak szybko jest szybciej”, mówiąc, że „testy porównawcze są wadliwe”? Czy o to ci chodzi? Nie rozumiem, jak to odpowiada na pytania.
Bryan Oakley,
15
@BryanOakley: Nie sądzę, aby testy były wadliwe, ale z pewnością należy wziąć pod uwagę możliwość wybrania jednego testu porównawczego, w którym Swift był szybszy.
Jan Hudec
23
Możliwe, że odpowiedź na pytanie „Jak jest szybszy?” może być „To nie jest tak naprawdę” @BryanOakley; taki jest sens odpowiedzi Jana. W końcu „kłamstwa, cholerne kłamstwa i statystyki”.
Josh Caswell
4
Jakiś czas temu porównaliśmy test Codename One działający na iOS, a nasza implementacja Java była znacznie szybsza niż Objective-C codenameone.com/blog/... Jan ma rację, dynamiczna wysyłka jest naprawdę powolna, jeśli nawet trochę ją poprawią, to niektóre benchmarki będą wykazać ogromną poprawę. Jeśli poprawią ARC nawet o ułamek (dzięki lepszej analizie kodu), mogą pozbyć się absurdalnej złożoności. Im bardziej ograniczony język, tym więcej kompilator może zrobić, aby zoptymalizować (patrz Java), a Swift dodaje ograniczenia.
Shai Almog,
7
Odpowiedź Jana jest idealną odpowiedzią na pierwszy i prawdopodobnie drugi kwartał. Kiedy zobaczyłem wzorce na wydarzeniu marketingowym jako myśl przewodnią, pomyślałem: „Wow, tylko 1,3x w wybranym najlepszym przypadku. Jaki będzie średni wynik? 0,3x?”
Amin Negm-Awad
5

Cel C dynamicznie wywołuje każde wywołanie metody.

Hipoteza: Test porównawczy używa pisania statycznego, aby umożliwić kompilatorowi Swift comparewyciągnięcie metody z sortpę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).

Jerry101
źródło
3

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ę.

greg.casamento
źródło
2
to brzmi bardziej jak nieprzyzwoity komentarz, patrz Jak odpowiedzieć
gnat
2
Czy teraz jest lepiej? :)
greg.casamento
0

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).

Pomalowane na czarno
źródło
13
Wykonanie testu porównawczego „złożonego sortowania obiektów” byłoby trochę trudne w języku takim jak C, który nie ma natywnej implementacji obiektów. Biorąc pod uwagę, że publiczność na tym wykładzie była prawdopodobnie w 100% programistami Objective-C, porównanie z językiem takim jak C ++ również nie ma większego sensu. Celem szybkiego nie jest „hej, to najlepszy język na świecie!” ale raczej „hej, to jest szybsze niż język, którego używasz teraz do programowania OSX / iOS”.
Bryan Oakley
10
C ma doskonale dobrze, qsortco 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::sortzawstydziłby Swifta. (Ponieważ jest to szablon, kompilator C ++ może go znacznie zoptymalizować, aż do rozwinięcia pętli.)
MSalters
@MSalters: W pełni się z tobą zgadzam. Zarówno C, jak i C ++ mają możliwości wyprzedzenia Swift. Czy byłoby możliwe uzyskanie wykonanego testu. Jestem więcej niż chętny do wykonania tego samego testu porównawczego w Swift, Objective-C, C ++ i C.
Painted Black
@BryanOakley również: „Ten język wymaga mniejszej liczby nawiasów kwadratowych!”
Nick Bedford
1
Ta odpowiedź w ogóle nie trzyma wody i jest strasznie myląca. OO nie jest naprawdę wolne, w rzeczywistości najszybszymi systemami, które znajdziesz, będą C ++, Java i C #, a styl (mocno OO lub nie) programowania będzie miał bardzo niewiele wspólnego z wynikowymi prędkościami, chyba że naprawdę zły kod.
Bill K