Używam Xcode 6 Beta 6.
To jest coś, co mnie denerwuje od jakiegoś czasu, ale osiąga punkt, w którym jest ledwo użyteczny.
Mój projekt zaczyna mieć przyzwoity rozmiar 65 plików Swift i kilka zmostkowanych plików Objective-C (które tak naprawdę nie są przyczyną problemu).
Wygląda na to, że jakakolwiek niewielka modyfikacja dowolnego pliku Swift (np. Dodanie prostej białej spacji w klasie, która prawie nie jest używana w aplikacji) spowoduje rekompilację całego pliku Swift dla określonego celu.
Po głębszym dochodzeniu odkryłem, że prawie 100% czasu kompilatora zajmuje CompileSwift
faza, w której Xcode uruchamia swiftc
polecenie na wszystkich plikach Swift twojego celu.
Przeprowadziłem dalsze dochodzenie i jeśli utrzymam delegację aplikacji z domyślnym kontrolerem, kompilacja będzie bardzo szybka, ale ponieważ dodawałem coraz więcej plików projektu, czas kompilacji zaczynał się naprawdę spowalniać.
Teraz, mając tylko 65 plików źródłowych, kompilacja za każdym razem zajmuje około 8/10 sekund. Wcale niezbyt szybki .
Nie widziałem żadnego postu na ten temat, z wyjątkiem tego , ale była to stara wersja Xcode 6. Zastanawiam się, czy w tym przypadku jestem jedyny.
AKTUALIZACJA
Sprawdziłem kilka projektów Swift na GitHub, takich jak Alamofire , Euler i CryptoSwift , ale żaden z nich nie miał wystarczającej liczby plików Swift do porównania. Jedynym projektem, który znalazłem, który zaczynał mieć przyzwoity rozmiar, był SwiftHN i mimo że miał tylko tuzin plików źródłowych, nadal byłem w stanie zweryfikować to samo, jedną prostą przestrzeń i cały projekt wymagał ponownej kompilacji, która zaczynała zajmować mało czasu (2/3 sekundy).
W porównaniu z kodem Objective-C, w którym zarówno analizator, jak i kompilacja płoną szybko, to naprawdę wydaje się, że Swift nigdy nie będzie w stanie obsłużyć dużych projektów, ale powiedz mi, że się mylę.
AKTUALIZACJA Z Xcode 6 Beta 7
Nadal nie ma żadnej poprawy. To zaczyna być śmieszne. Z braku #import
Swift naprawdę nie rozumiem, jak Apple kiedykolwiek będzie w stanie to zoptymalizować.
AKTUALIZACJA Z Xcode 6.3 i Swift 1.2
Apple dodał przyrostowe kompilacje (i wiele innych optymalizacji kompilatora). Musisz migrować swój kod do Swift 1.2, aby zobaczyć te korzyści, ale Apple dodało narzędzie w Xcode 6.3, aby Ci to pomóc:
JEDNAK
Nie radujcie się tak szybko jak ja. Solver graficzny, którego używają do tworzenia kompilacji przyrostowej, nie jest jeszcze zbyt dobrze zoptymalizowany.
Rzeczywiście po pierwsze, nie sprawdza zmian sygnatur funkcji, więc jeśli dodasz spację w bloku jednej metody, wszystkie pliki zależne od tej klasy zostaną ponownie skompilowane.
Po drugie, wydaje się, że tworzy drzewo na podstawie plików, które zostały ponownie skompilowane, nawet jeśli zmiana ich nie dotyczy. Na przykład, jeśli przeniesiesz te trzy klasy do różnych plików
class FileA: NSObject {
var foo:String?
}
class FileB: NSObject {
var bar:FileA?
}
class FileC: NSObject {
var baz:FileB?
}
Teraz, jeśli zmodyfikujesz FileA
, kompilator oczywiście zaznaczy, FileA
że zostanie ponownie skompilowany. Spowoduje to również ponowną kompilację FileB
(to byłoby OK w oparciu o zmiany w FileA
), ale również FileC
dlatego, że FileB
została ponownie skompilowana, i to jest dość złe, ponieważ FileC
nigdy nie używa FileA
tutaj.
Mam więc nadzieję, że poprawią ten solver drzewa zależności ... Otworzyłem radar z tym przykładowym kodem.
AKTUALIZACJA Z Xcode 7 beta 5 i Swift 2.0
Wczoraj firma Apple wydała wersję beta 5, a w informacjach o wersji mogliśmy zobaczyć:
Swift Language & Compiler • Przyrostowe kompilacje: zmiana samej treści funkcji nie powinna już powodować przebudowywania zależnych plików. (15352929)
Spróbowałem i muszę powiedzieć, że teraz działa naprawdę (naprawdę!) Dobrze. Szybko zoptymalizowali przyrostowe kompilacje.
Gorąco polecam utworzenie swift2.0
gałęzi i aktualizowanie kodu za pomocą XCode 7 beta 5. Będziesz zadowolony z ulepszeń kompilatora (powiedziałbym jednak, że globalny stan XCode 7 jest wciąż powolny i błędny)
AKTUALIZACJA Z Xcode 8.2
Minęło trochę czasu od mojej ostatniej aktualizacji na ten temat, więc oto jest.
Nasza aplikacja ma teraz około 20 000 wierszy prawie wyłącznie kodu Swift, który jest przyzwoity, ale nie wyróżnia się. Przeszedł szybką migrację 2, a następnie szybką 3. Kompilacja na MacBooku Pro z połowy 2014 r. (Intel Core i7 2,5 GHz) trwa około 5/6 m, co jest dobre w przypadku czystej wersji.
Jednak przyrostowa kompilacja jest wciąż żartem, mimo że Apple twierdzi, że:
Xcode nie odbuduje całego obiektu docelowego, jeśli wystąpiły tylko niewielkie zmiany. (28892475)
Oczywiście myślę, że wielu z nas śmiało się po sprawdzeniu tego bzdury (dodanie jednej prywatnej (prywatnej!) Właściwości do dowolnego pliku mojego projektu spowoduje rekompilację całej rzeczy ...)
Chciałbym wskazać wam ten wątek na forach programistów Apple, który zawiera więcej informacji na temat problemu (a także docenia komunikację programistów Apple na ten temat od czasu do czasu)
Zasadniczo ludzie wymyślili kilka rzeczy, aby spróbować ulepszyć przyrostową kompilację:
- Dodaj
HEADER_MAP_USES_VFS
ustawienie projektu ustawione natrue
- Wyłącz
Find implicit dependencies
ze swojego programu - Utwórz nowy projekt i przenieś hierarchię plików do nowego.
Spróbuję rozwiązania 3, ale rozwiązanie 1/2 nie działało dla nas.
Jak na ironię zabawne w tej całej sytuacji jest to, że patrząc na pierwszy post w tej kwestii używaliśmy Xcode 6 z moim kodem swift 1 lub swift 1.1, kiedy osiągnęliśmy powolność pierwszych kompilacji, a teraz około dwa lata później, pomimo faktycznych ulepszeń od Apple sytuacja jest tak samo zła, jak w przypadku Xcode 6. Jak na ironię.
Naprawdę NAPRAWDĘ żałuję, że wybrałem Swift zamiast Obj / C do naszego projektu z powodu codziennej frustracji. (Nawet przełączam się na AppCode, ale to już inna historia)
W każdym razie widzę, że ten post SO ma ponad 32 000 wyświetleń i 143 wzloty w chwili pisania tego tekstu, więc chyba nie jestem jedyny. Trzymajcie się chłopaki, pomimo pesymizmu w tej sytuacji, może być trochę światła na końcu tunelu.
Jeśli masz czas (i odwagę!), Myślę, że Apple z zadowoleniem przyjmuje radar w tej sprawie.
Do następnego razu! Twoje zdrowie
AKTUALIZACJA Z Xcode 9
Natknąć się na to dzisiaj. Xcode po cichu wprowadził nowy system kompilacji w celu poprawy obecnej okropnej wydajności. Musisz włączyć to w ustawieniach obszaru roboczego.
Próbowałem jeszcze, ale zaktualizuje ten post po zakończeniu. Wygląda obiecująco.
Odpowiedzi:
Okazało się, że Rob Napier miał rację. Był to jeden plik (właściwie jedna metoda), który spowodował, że kompilator przeszedł na berzek.
Nie zrozum mnie źle. Swift za każdym razem rekompiluje wszystkie pliki, ale teraz wielką zaletą jest to, że Apple dodało informacje zwrotne dotyczące kompilacji w czasie rzeczywistym w stosunku do plików, które kompiluje, więc Xcode 6 GM pokazuje teraz, które pliki Swift są kompilowane i status kompilacji w czasie rzeczywistym jak widać na tym zrzucie ekranu:
Przydaje się to, aby wiedzieć, który z twoich plików trwa tak długo. W moim przypadku był to ten fragment kodu:
ponieważ właściwość
title
była typu,var title:String?
a nie typuNSString
. Kompilator zwariował, dodając go doNSMutableDictionary
.Zmiana na:
skróciło kompilację z 10/15 sekund (może nawet więcej) do jednej sekundy ... niesamowicie.
źródło
Próbowaliśmy z tym walczyć, ponieważ mamy około 100 000 linii kodu Swift i 300 000 linii kodu ObjC.
Naszym pierwszym krokiem była optymalizacja wszystkich funkcji zgodnie z danymi wyjściowymi czasów kompilacji funkcji (np. Jak opisano tutaj https://thatthinginswift.com/debug-long-compile-times-swift/ )
Następnie napisaliśmy skrypt łączący wszystkie szybkie pliki w jeden plik, co psuje poziomy dostępu, ale skróciło czas kompilacji z 5-6 minut do ~ 1 minuty.
Jest to teraz nieczynne, ponieważ zapytaliśmy o to Apple i doradzili, że powinniśmy wykonać następujące czynności:
'Fast, Whole Module Optimization'
'-Onone'
Po ustawieniu tych flag kompilator skompiluje wszystkie pliki Swift w jednym kroku. Odkryliśmy, że dzięki naszemu skryptowi scalania jest to znacznie szybsze niż samodzielne kompilowanie plików. Jednak bez
-Onone'
przesłonięcia zoptymalizuje również cały moduł, który jest wolniejszy. Kiedy ustawiamy'-Onone'
flagę w innych flagach Swift, zatrzyma ona optymalizację, ale nie przestanie kompilować wszystkich plików Swift w jednym kroku.Aby uzyskać więcej informacji na temat optymalizacji całego modułu, sprawdź post na blogu Apple tutaj - https://swift.org/blog/whole-module-optimizations/
Odkryliśmy, że te ustawienia pozwalają naszemu kodowi Swift na kompilację w 30 sekund :-) Nie mam dowodów na to, jak działałoby to w innych projektach, ale sugeruję wypróbowanie go, jeśli czasy kompilacji Swift nadal stanowią dla ciebie problem.
Uwaga dla kompilacji z App Store, należy pominąć
'-Onone'
flagę, ponieważ optymalizacja jest zalecana dla kompilacji produkcyjnych.źródło
-Onone
. Nie możemy na razie wykorzystać optymalizacji całego modułu, ponieważ powoduje to awarię kompilatora ... Ale twoja rada przyspiesza prawie x10 naszej prędkości kompilacji. Na MacBooku Air (co roku 2013) budowano około 8 minut, teraz jest to około 1 minuty i połowa tego czasu spędza na przełączaniu się między celami (mamy aplikację, rozszerzenia i kilka wewnętrznych ram) i kompiluje storyboardy-Onone
pomocy w celu skrócenia czasu kompilacji. Wielkie dzięki kolego!Prawdopodobnie ma to niewiele wspólnego z rozmiarem twojego projektu. To prawdopodobnie jakiś konkretny fragment kodu, być może nawet jedna linia. Możesz to przetestować, próbując skompilować jeden plik naraz, a nie cały projekt. Lub spróbuj obejrzeć dzienniki kompilacji, aby zobaczyć, który plik trwa tak długo.
Jako przykład rodzaju kodu, który może powodować problemy, ta 38-liniowa lista kompilacji w wersji beta7 zajmuje więcej niż minutę. Wszystko to jest spowodowane tym jednym blokiem:
Uprość to za pomocą linii lub dwóch i kompiluje się niemal natychmiast. Problem polega na tym, że powoduje to wykładniczy wzrost (prawdopodobnie wzrost czynnikowy) w kompilatorze. Oczywiście nie jest to idealne rozwiązanie, a jeśli potrafisz odizolować takie sytuacje, powinieneś otworzyć radary, aby pomóc w rozwiązaniu tych problemów.
źródło
CompileSwift
fazy. Zajmuje wszystkie szybkie pliki, nawet jeśli tylko jeden został zmodyfikowany. Więc jeśli jest to jeden plik, który zajmuje trochę czasu (co bardzo wątpię), kompilator nigdy nie powie ci, który to plik.swiftc
aby zobaczyć, jak długo one zajmują.Jeśli próbujesz zidentyfikować określone pliki, które spowalniają twój czas kompilacji, możesz spróbować skompilować go z wiersza poleceń za pomocą xctool, co da ci czasy kompilacji plik po pliku.
Należy zauważyć, że domyślnie buduje 2 pliki jednocześnie dla każdego rdzenia procesora i nie daje czasu, który upłynął, ale absolutnego czasu użytkownika. W ten sposób wszystkie czasy wyrównują się między równoległymi plikami i wyglądają bardzo podobnie.
Aby temu zaradzić, ustaw
-jobs
flagę na 1 , aby nie zrównoleglała kompilacji plików. Zajmie to więcej czasu, ale w końcu będziesz miał „netto” czasy kompilacji, które możesz porównać plik po pliku.To jest przykładowe polecenie, które powinno załatwić sprawę:
xctool -workspace <your_workspace> -scheme <your_scheme> -jobs 1 build
Dane wyjściowe fazy „Kompiluj szybkie pliki” byłyby takie:
Na podstawie tego wyniku można szybko określić, które pliki kompilują się dłużej niż inne. Co więcej, możesz z dużą dokładnością ustalić, czy refaktoryzacje (jawne rzutowania, podpowiedzi typu itp.) Skracają czasy kompilacji dla określonych plików, czy nie.
UWAGA: technicznie można to również zrobić,
xcodebuild
ale wyjście jest niezwykle szczegółowe i trudne do spożycia.źródło
Swift Compiler
→Optimization Level
dlaFast, Whole Module Optimization [-O -whole-module-optimization]
W moim przypadku Xcode 7 nie różnił się wcale. Miałem wiele funkcji, których kompilacja wymagała kilku sekund.
Przykład
Po rozpakowaniu opcjonalnych czas kompilacji spadł o 99,4% .
Zobacz więcej przykładów w tym poście i tym poście .
Build Time Analyzer dla Xcode
I opracowali wtyczkę Xcode , które mogą się przydać każdemu doświadcza tych problemów.
Wydaje się, że w Swift 3 pojawią się ulepszenia, więc mamy nadzieję, że dzięki temu nasz kod Swift będzie się kompilował szybciej.
źródło
Prawdopodobnie nie możemy naprawić kompilatora Swift, ale możemy naprawić nasz kod!
Jest ukryta opcja w Swift kompilatora, który wypisuje dokładnych odstępach czasowych że kompilator wykonuje skompilować każdą funkcję:
-Xfrontend -debug-time-function-bodies
. Pozwala nam znaleźć wąskie gardła w naszym kodzie i znacznie skrócić czas kompilacji.Po prostu uruchom następujące polecenie w terminalu i przeanalizuj wyniki:
Niesamowity Brian Irace napisał o tym świetny artykuł Profilowanie czasów kompilacji Swift .
źródło
alias grep='noglob grep'
najpierw używają Zsh, inaczej grep nie zadziałaRozwiązaniem jest odlewanie.
Miałem ogromny wachlarz ton słowników, takich jak ten:
Kompilacja zajęła około 40 minut. Aż rzuciłem takie słowniki:
Działa to w przypadku prawie każdego innego problemu, na jaki natknąłem się w odniesieniu do typów danych, które zapisałem na stałe w mojej aplikacji.
źródło
Należy zauważyć, że silnik wnioskowania typu Swift może być bardzo wolny w przypadku typów zagnieżdżonych. Możesz uzyskać ogólne pojęcie o tym, co powoduje spowolnienie, oglądając dziennik kompilacji dla pojedynczych jednostek kompilacji, które zajmują dużo czasu, a następnie kopiując i wklejając pełne polecenie spawnowane Xcode do okna terminala, a następnie naciskając CTRL- \, aby uzyskać trochę diagnostyki. Pełny przykład można znaleźć na http://blog.impathic.com/post/99647568844/debugging-slow-swift-compile-times .
źródło
Upewnij się również, że podczas kompilacji w celu debugowania (Swift lub Objective-C) ustawiono opcję Buduj tylko architekturę aktywną:
źródło
Ponieważ wszystkie te rzeczy są w wersji beta, a ponieważ kompilator Swift (przynajmniej na dzień dzisiejszy) nie jest otwarty, myślę, że nie ma prawdziwej odpowiedzi na twoje pytanie.
Po pierwsze, porównanie Objective-C z kompilatorem Swift jest w pewien sposób okrutne. Swift jest wciąż w fazie beta i jestem pewien, że Apple pracuje nad zapewnianiem funkcjonalności i naprawianiem błędów, a nie tylko błyskawicą (nie zaczynasz budować domu, kupując meble). Myślę, że Apple zoptymalizuje kompilator w odpowiednim czasie.
Jeśli z jakiegoś powodu wszystkie pliki źródłowe muszą zostać skompilowane jako kompletne, istnieje możliwość utworzenia oddzielnych modułów / bibliotek. Ale ta opcja nie jest jeszcze możliwa, ponieważ Swift nie zezwala na biblioteki, dopóki język nie będzie stabilny.
Domyślam się, że zoptymalizują kompilator. Z tego samego powodu, dla którego nie możemy tworzyć wstępnie skompilowanych modułów, być może kompilator musi skompilować wszystko od zera. Ale gdy język osiągnie stabilną wersję, a format plików binarnych już się nie zmienia, będziemy mogli tworzyć nasze biblioteki i być może (?) Kompilator będzie również w stanie zoptymalizować swoją pracę.
Tylko zgaduję, bo tylko Apple wie ...
źródło
W przypadku Xcode 8 przejdź do ustawień projektu, a następnie Edytor> Dodaj ustawienie kompilacji> Dodaj ustawienie zdefiniowane przez użytkownika i dodaj następujące elementy:
Dodanie tej flagi zmniejszyło nasze czasy kompilacji czystego kompilacji z 7 minut do 65 sekund w przypadku szybkiego projektu 40KLOC, w cudowny sposób. Może również potwierdzić, że 2 znajomych widziało podobne ulepszenia w projektach korporacyjnych.
Mogę tylko założyć, że jest to jakiś błąd w Xcode 8.0
EDYCJA: Wydaje się, że nie działa już w Xcode 8.3 dla niektórych osób.
źródło
Niestety kompilator Swift wciąż nie jest zoptymalizowany pod kątem szybkiej i przyrostowej kompilacji (od wersji Xcode 6.3 beta). Tymczasem możesz skorzystać z niektórych z poniższych technik, aby skrócić czas kompilacji Swift:
Podziel aplikację na frameworki, aby zmniejszyć wpływ ponownej kompilacji. Pamiętaj jednak, że musisz unikać cyklicznych zależności w swojej aplikacji. Aby uzyskać dalsze informacje na ten temat, sprawdź ten post: http://bits.citrusbyte.com/improving-swift-compile-time/
Użyj Swift do części projektu, które są dość stabilne i nie zmieniają się często. W przypadku innych obszarów, w których trzeba bardzo często zmieniać lub obszarów, które wymagają dużej liczby iteracji kompilacji / uruchamiania (prawie wszystkie rzeczy związane z interfejsem użytkownika), lepiej użyć Celu C z podejściem mieszania i dopasowywania.
Spróbuj wstrzyknąć kod środowiska wykonawczego za pomocą „Injection for Xcode”
Użyj metody roopc: http://roopc.net/posts/2014/speeding-up-swift-builds/
Zwolnij silnik szybkiego wnioskowania typu, podając wskazówki z wyraźnymi rzutami.
źródło
Szybka konstrukcja tablic i słowników wydaje się być bardzo popularną przyczyną tego (szczególnie dla tych, którzy pochodzą z Rubiego ), to znaczy,
prawdopodobnie będzie przyczyną, dla której powinno to naprawić:
źródło
Do debugowania i testowania należy użyć następujących ustawień, aby skrócić czas kompilacji z około 20 minut do mniej niż 2 minut,
Zmarnowałem niezliczone godziny, czekając na zakończenie projektu, aby zdać sobie sprawę, że muszę dokonać tej jednej małej zmiany i musiałem czekać całe kolejne 30 minut na przetestowanie. Te ustawienia działały dla mnie. (Nadal eksperymentuję z ustawieniami)
Ale upewnij się, że przynajmniej ustawiłeś „DWARF z dSYM” (jeśli chcesz monitorować swoją aplikację) i zbuduj Active Architecture na „NIE”, aby wydanie / archiwizacja przełączyło się na iTunes Connect (pamiętam też, że marnowałem tutaj kilka godzin).
źródło
Set Build for Active Architecture: YES
dało mi około 45% skrócenie czasu kompilacji. Wielkie dzięki.Kompilator spędza dużo czasu na wyszukiwaniu i sprawdzaniu typów. Dodanie adnotacji typu bardzo pomaga kompilatorowi.
Jeśli masz wiele powiązanych funkcji, takich jak
Następnie kompilator potrzebuje trochę czasu, aby dowiedzieć się, jaki
sum
powinien być typ . Dodanie typu pomaga. Pomaga również wyciągnięcie przerywanych kroków do osobnych zmiennych.Specjalnie dla typów liczbowych
CGFloat
,Int
może bardzo pomóc. Literalna liczba2
może reprezentować wiele różnych typów liczbowych. Tak więc kompilator musi dowiedzieć się z kontekstu, który to jest.+
Należy również unikać funkcji, których wyszukiwanie zajmuje dużo czasu . Używanie kilku+
do łączenia kilku tablic jest powolne, ponieważ kompilator musi dowiedzieć się, która implementacja+
powinna zostać wywołana dla każdej z nich+
. Więc jeśli to możliwe, użyjvar a: [Foo]
zappend()
.Możesz dodać ostrzeżenie, aby wykryć, które funkcje wolno kompilują się w Xcode .
W Ustawieniach kompilacji dla swojego celu wyszukaj Inne szybkie flagi i dodaj
-Xfrontend -warn-long-function-bodies=100
ostrzega przed każdą funkcją, której kompilacja trwa dłużej niż 100 ms.
źródło
W przypadku projektów, które mieszają Objective-C i kod SWIFT, możemy ustawić
-enable-bridging-pch
wOther Swift Flags
. Dzięki temu nagłówek mostkujący jest analizowany tylko raz, a wynik (tymczasowy „nagłówek prekompilowany” lub „PCH”) jest buforowany i ponownie wykorzystywany we wszystkich plikach Swift w celu. Apple twierdzi, że skraca czas kompilacji o 30%. Link referencyjny:UWAGA: Działa to tylko w wersji Swift 3.1 i nowszych.
źródło
Ponowne uruchomienie mojego komputera Mac spowodowało cuda dla tego problemu. Po ponownym uruchomieniu przeszedłem z 15-minutowych wersji do 30-sekundowych wersji.
źródło
Szybki czas kompilacji został poprawiony w nowym Xcode 6.3
źródło
Oto kolejny przypadek, który może powodować ogromne spowolnienia z wnioskami o typ. Operatorzy koalescencyjni .
Zmiana linii takich jak:
do
pomogło mi skrócić czas kompilacji z lat 70. do 13
źródło
Nic nie działało dla mnie w Xcode 6.3.1 - kiedy dodałem około 100 plików Swift, które Xcode losowo zawiesił na kompilacji i / lub indeksowaniu. Wypróbowałem modułową opcję bez powodzenia.
Instalacja i używanie Xcode 6.4 Beta faktycznie działało dla mnie.
źródło
Dla mnie to działa jak magia - kompilacja Speed Up Swift . Skrócił czas kompilacji do 3 minut z 10 minut.
Mówi należy włączyć
Whole Module Optimization
podczas dodawania-Onone
wOther Swift Flags
.Używam
Swift 3
naXcode 8.3
/Xcode 8.2
.źródło
Mieszanie literału całkowitego i zmiennoprzecinkowego w jednym wyrażeniu powoduje również długi czas kompilacji.
Wiele wyrażeń czasu kompilacji 1000 + ms jest zmniejszonych do 10 ~ 100 ms po tym,
.0
jak wstawiłem literał po liczbie całkowitej.źródło