Dlaczego czas kompilacji Swift jest taki wolny?

210

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 CompileSwiftfaza, w której Xcode uruchamia swiftcpolecenie 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 #importSwift 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:

Wpisz opis zdjęcia tutaj

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ż FileCdlatego, że FileBzostała ponownie skompilowana, i to jest dość złe, ponieważ FileCnigdy nie używa FileAtutaj.

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.0gałę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ę:

  1. Dodaj HEADER_MAP_USES_VFSustawienie projektu ustawione natrue
  2. Wyłącz Find implicit dependenciesze swojego programu
  3. 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.

wprowadź opis zdjęcia tutaj

Próbowałem jeszcze, ale zaktualizuje ten post po zakończeniu. Wygląda obiecująco.

apouche
źródło
1
Ciekawy! Zastanawiam się, czy jest to po prostu brakująca optymalizacja, czy potrzeba parsowania tak wielu plików, ponieważ nie ma plików interfejsu.
zaph
2
Miałem podobne problemy i ostatecznie zdałem sobie sprawę, że to z powodu niestandardowych operatorów używanych w klasach jednostek do deserializacji z JSON. Jeśli używasz któregoś z nich, sugeruję, abyś spróbował przejść do normalnej funkcji jeden po drugim i sprawdzić, czy coś się zmieni.
Antonio
4
Kompilacja stała się miażdżąco powolna w moim projekcie od XCode 6 beta 6. Gdzie nie jestem pewien, czy jest to spowodowane zmianami w wersji beta, czy z powodu mojego kodu. Ale mój projekt nie jest jeszcze duży (~ 40-50 plików Swift).
BadmintonCat
2
Kompilacja stała się nieznośnie powolna w miarę rozwoju mojego projektu. Zależy mi również na kilku zasobnikach, co z pewnością irytuje problem. Korzysta z najnowszej wersji innej niż beta.
Andy
2
Kompilacja przyrostowa jest nadal wykonywana w „konserwatywnej analizie zależności, więc nadal może być więcej odbudowań plików, niż jest to absolutnie konieczne”. Mamy nadzieję, że z czasem ulegnie poprawie.
nmdias

Odpowiedzi:

70

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:

Wpisz opis zdjęcia tutaj

Przydaje się to, aby wiedzieć, który z twoich plików trwa tak długo. W moim przypadku był to ten fragment kodu:

var dic = super.json().mutableCopy() as NSMutableDictionary
dic.addEntriesFromDictionary([
        "url" : self.url?.absoluteString ?? "",
        "title" : self.title ?? ""
        ])

return dic.copy() as NSDictionary

ponieważ właściwość titlebyła typu, var title:String?a nie typu NSString. Kompilator zwariował, dodając go do NSMutableDictionary.

Zmiana na:

var dic = super.json().mutableCopy() as NSMutableDictionary
dic.addEntriesFromDictionary([
        "url" : self.url?.absoluteString ?? "",
        "title" : NSString(string: self.title ?? "")
        ])

return dic.copy() as NSDictionary

skróciło kompilację z 10/15 sekund (może nawet więcej) do jednej sekundy ... niesamowicie.

apouche
źródło
3
Dziękuję za odpowiedź. Może to być bardzo przydatne dla innych ścigających tam silnik wnioskowania typu, który utknął podczas kompilacji.
Rob Napier
1
Gdzie dotarłeś do tego widoku @apouche? Nie widzę tego w xcode
Eric
2
Musisz otworzyć asystenta debugowania (CMD + 8) i kliknąć bieżącą kompilację
apouche
1
tak, jestem pewien, że Apple zoptymalizuje to później, w przeciwnym razie robienie projektów w świecie rzeczywistym w szybkim tempie jest skazane tu i tam.
apouche
1
Jak przejść do tego narzędzia, które pokazuje, które pliki są kompilowane?
jgvb 27.04.16
42

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:

  1. Włącz „optymalizację całego modułu” w ustawieniach kompilacji „Swift Compiler - Code Generation”. Wybierz'Fast, Whole Module Optimization'

wprowadź opis zdjęcia tutaj

  1. W „Swift Compiler - Flagi niestandardowe” dodaj kompilacje programistyczne '-Onone'

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

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.

Sam Stow
źródło
4
Bardzo dziękuję za tę radę! Po prostu nie rozumiem, dlaczego nie ma czegoś takiego w oficjalnych źródłach (przynajmniej łatwo je znaleźć), na przykład artykuł, o którym wspominasz powinien (musi!) Mieć uwagę na ten temat -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
Ilya Puchka
Przetestowałem również tę metodę i jest to tylko wspomniana metoda, która zawiera -Onone i znacznie skraca czas kompilacji.
Vlad
Pracuj też z moim. Korzystanie z -Ononepomocy w celu skrócenia czasu kompilacji. Wielkie dzięki kolego!
nahung89
34

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:

let pipeResult =
seq |> filter~~ { $0 % 2 == 0 }
  |> sorted~~ { $1 < $0 }
  |> map~~ { $0.description }
  |> joinedWithCommas

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.

Rob Napier
źródło
Nie jestem pewien, czy widziałeś mój komentarz dotyczący tej CompileSwiftfazy. 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.
apouche
10
Możesz skompilować poszczególne pliki, używając, swiftcaby zobaczyć, jak długo one zajmują.
Rob Napier
Przepraszam, że nie dałem ci nagrody, ponieważ z początku jej nie wierzę. Próbowałem też kompilować pliki jeden po drugim, ale było to uciążliwe (musiałem odpowiednio dawać frameworki i deps za każdym razem), więc się poddałem. Proszę zobaczyć moją ostatnią odpowiedź na ten post, aby uzyskać pełne wyjaśnienie
apouche
Nie sądzę, że opiera się na wielkości projektu. Mój projekt ma tylko 4 szybkie pliki i nagle zaczął się kompilować niewiarygodnie wolno. Wczoraj szybko się paliło. Nie mogę położyć palca na czymkolwiek, co zrobiłem w moim projekcie, z wyjątkiem dodawania ikony i uruchamiania obrazów.
Travis M.,
33

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

...Compile EntityObserver.swift (1623 ms)Compile Session.swift (1526 ms)Compile SearchComposer.swift (1556 ms)
...

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ć, xcodebuildale wyjście jest niezwykle szczegółowe i trudne do spożycia.

Andrea Sprega
źródło
1
Upewnij się tylko, że opcja Optymalizacja całego modułu jest ustawiona na fałsz, w przeciwnym razie nie rozdzieli pojedynczych szybkich plików.
sabes
1
Zobacz Swift CompilerOptimization LeveldlaFast, Whole Module Optimization [-O -whole-module-optimization]
Matt
26

W moim przypadku Xcode 7 nie różnił się wcale. Miałem wiele funkcji, których kompilacja wymagała kilku sekund.

Przykład

// Build time: 5238.3ms
return CGSize(width: size.width + (rightView?.bounds.width ?? 0) + (leftView?.bounds.width ?? 0) + 22, height: bounds.height)

Po rozpakowaniu opcjonalnych czas kompilacji spadł o 99,4% .

// Build time: 32.4ms
var padding: CGFloat = 22
if let rightView = rightView {
    padding += rightView.bounds.width
}

if let leftView = leftView {
    padding += leftView.bounds.width
}
return CGSizeMake(size.width + padding, bounds.height)

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.

wizerunek

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.

Robert Gummesson
źródło
Wspaniale, mam nadzieję, że dam ci więcej niż +1. jesteś prawdą, a Twoja wtyczka jest również świetna. Użyłem tego, a mój czas kompilacji jest coraz krótszy, co oznacza super szybki rozwój, ponieważ te opcje są czasem koszmarem i powodują spowolnienie kompilatora.
hardikdevios
Fantastycznie! Twoje narzędzie bardzo mi pomaga. Dzięki
Phil
Świetna wtyczka - naprawdę przydatna! Dzięki
365SplendidSuns,
@Robert Gummesson, czy mamy jakieś narzędzie do kodu Objective-C?
Ashok
19

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:

xcodebuild -workspace App.xcworkspace -scheme App clean build OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" | grep [1-9].[0-9]ms | sort -nr > culprits.txt

Niesamowity Brian Irace napisał o tym świetny artykuł Profilowanie czasów kompilacji Swift .

Valentin Shergin
źródło
2
Dla tych, którzy alias grep='noglob grep'najpierw używają Zsh, inaczej grep nie zadziała
Jaime Agudo
16

Rozwiązaniem jest odlewanie.

Miałem ogromny wachlarz ton słowników, takich jak ten:

["title" : "someTitle", "textFile" : "someTextFile"],
["title" : "someTitle", "textFile" : "someTextFile"],
["title" : "someTitle", "textFile" : "someTextFile"],
["title" : "someTitle", "textFile" : "someTextFile"],
.....

Kompilacja zajęła około 40 minut. Aż rzuciłem takie słowniki:

["title" : "someTitle", "textFile" : "someTextFile"] as [String : String],
["title" : "someTitle", "textFile" : "someTextFile"] as [String : String],
["title" : "someTitle", "textFile" : "someTextFile"] as [String : String],
....

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.

YichenBman
źródło
6
Cóż, tak, jest to część optymalizacji, które robisz, aby skrócić czas kompilacji, ale nadal głównym problemem w obecnym szybkim kompilatorze jest to, że wciąż rekompiluje wszystko pojedynczy plik za każdym razem, gdy dokonujesz najmniejszej modyfikacji.
apouche
4
Byłoby zabawnie, gdyby nie było tak smutno.
Tom Andersen
15

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 .

marcprux
źródło
To dla mnie najlepsza odpowiedź (patrz link). Mogłem ŁATWO znaleźć dwie różne linie, które stanowiły problem, i rozwiązać je, rozkładając moje linie na mniejsze linie.
Nico,
To bardzo przydatna odpowiedź, ponieważ pokazuje, jak znaleźć kompilator. W moim przypadku było to: „curScore [curPlayer% 2] + curScore [2 + curPlayer% 2] == 3 && maker% 2 == curPlayer% 2” Gdy tylko przeniosłem go z „if” na „let” ”, spowodowało to, że„ ekspresja była zbyt złożona, aby można ją było rozwiązać w rozsądnym czasie; rozważ podzielenie wyrażenia na wyraźne podwyrażenia ”
Dmitry
Jest to zdecydowanie najbardziej pomocny sposób rozwiązania tego problemu.
Richard Venable,
9

Upewnij się również, że podczas kompilacji w celu debugowania (Swift lub Objective-C) ustawiono opcję Buduj tylko architekturę aktywną:

wprowadź opis zdjęcia tutaj

Rivera
źródło
6

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

Jerzy
źródło
„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”. fajna obserwacja, nie myślałam o tym wcześniej.
chakrit
1
2017 i wciąż powoli
Pedro Paulo Amorim
2017 z Xcode 9 i nowym systemem kompilacji i wciąż powolny
pableiros
2018 z Xcode 9 mam projekt z ponad 50 szybkimi plikami, jeśli wykonam czystą kompilację, minęło już 5 minut, a moja kompilacja jeszcze się nie zakończyła.
Chen Li Yong,
5

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:

SWIFT_WHOLE_MODULE_OPTIMIZATION = YES

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.

Chris
źródło
2
Gdzie są „ustawienia projektu”?
Raniys,
@Raniys Kliknij niebieską ikonę na poziomie katalogu głównego w lewym okienku w Xcode.
Chris
Zauważyłem, że od Xcode 8.3 (nie beta) to już nie działa w moim przypadku :(
Chris
4

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.

vorterixe
źródło
4

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,

var a = ["a": "b",
         "c": "d",
         "e": "f",
         "g": "h",
         "i": "j",
         "k": "l",
         "m": "n",
         "o": "p",
         "q": "r",
         "s": "t",
         "u": "v",
         "x": "z"]

prawdopodobnie będzie przyczyną, dla której powinno to naprawić:

var a = NSMutableDictionary()
a["a"] = "b"
a["c"] = "d"
... and so on
Marcelo Ribeiro
źródło
4

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,

  1. W ustawieniach kompilacji projektu wyszukaj „Optymalizacja” Zmień debugowanie w „Najszybszy [-O3]” lub wyżej.
  2. Zestaw kompilacji dla aktywnej architektury: TAK
  3. Format informacji debugowania: DWARF
  4. Optymalizacja całego modułu: NIE

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

Mahesh
źródło
4
Mogę się mylić, ale czy ustawienie wyższych poziomów optymalizacji nie wydłużyłoby czasu kompilacji? Poziomy optymalizacji poprawią wydajność środowiska wykonawczego.
Michael Waterfall
1
Set Build for Active Architecture: YESdało mi około 45% skrócenie czasu kompilacji. Wielkie dzięki.
Jean Le Moignan
4

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

let sum = [1,2,3].map({String($0)}).flatMap({Float($0)}).reduce(0, combine: +)

Następnie kompilator potrzebuje trochę czasu, aby dowiedzieć się, jaki sumpowinien być typ . Dodanie typu pomaga. Pomaga również wyciągnięcie przerywanych kroków do osobnych zmiennych.

let numbers: [Int] = [1,2,3]
let strings: [String] = sum.map({String($0)})
let floats: [Float] = strings.flatMap({Float($0)})
let sum: Float = floats.reduce(0, combine: +)

Specjalnie dla typów liczbowych CGFloat, Intmoże bardzo pomóc. Literalna liczba 2moż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żyj var a: [Foo]z append().

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.

orkoden
źródło
4

W przypadku projektów, które mieszają Objective-C i kod SWIFT, możemy ustawić -enable-bridging-pchw Other 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.

iHS
źródło
2

Ponowne uruchomienie mojego komputera Mac spowodowało cuda dla tego problemu. Po ponownym uruchomieniu przeszedłem z 15-minutowych wersji do 30-sekundowych wersji.

Sigma4Life
źródło
1

Szybki czas kompilacji został poprawiony w nowym Xcode 6.3

Ulepszenia kompilatora

Kompilator Swift 1.2 został zaprojektowany tak, aby był bardziej stabilny i poprawiał wydajność pod każdym względem. Zmiany te zapewniają również lepsze wrażenia podczas pracy z Swift w Xcode. Niektóre z najbardziej widocznych ulepszeń obejmują:

Kompilacje przyrostowe

Pliki źródłowe, które nie uległy zmianie, nie będą już ponownie kompilowane domyślnie, co znacznie skróci czas kompilacji w większości typowych przypadków. Większe zmiany strukturalne w kodzie mogą nadal wymagać odbudowania wielu plików.

Szybsze pliki wykonywalne

Kompilacje debugowania produkują pliki binarne, które działają znacznie szybciej, a nowe optymalizacje zapewniają jeszcze lepszą wydajność kompilacji wersji.

Lepsza diagnostyka kompilatora

Wyraźniejsze komunikaty o błędach i ostrzeżeniach oraz nowa poprawka ułatwiają pisanie właściwego kodu Swift 1.2.

Poprawa stabilności

Naprawiono najczęstsze awarie kompilatora. Powinieneś także zobaczyć mniej ostrzeżeń SourceKit w edytorze Xcode.

Vojtech Vrbka
źródło
0

Oto kolejny przypadek, który może powodować ogromne spowolnienia z wnioskami o typ. Operatorzy koalescencyjni .

Zmiana linii takich jak:

abs(some_optional_variable ?? 0)

do

abs((some_optional_variable ?? 0) as VARIABLE_TYPE)

pomogło mi skrócić czas kompilacji z lat 70. do 13

Harry Mexican
źródło
0

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.

hris.to
źródło
0

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 Optimizationpodczas dodawania -Ononew Other Swift Flags.

Używam Swift 3na Xcode 8.3/Xcode 8.2 .

Fałszować
źródło
0

Mieszanie literału całkowitego i zmiennoprzecinkowego w jednym wyrażeniu powoduje również długi czas kompilacji.

1.0 + (1.0 + (1  * (1.0 + 1.0))) // 3429ms

1.0 + (1.0 + (1.0  * (1.0 + 1.0))) // 5ms

Wiele wyrażeń czasu kompilacji 1000 + ms jest zmniejszonych do 10 ~ 100 ms po tym, .0jak wstawiłem literał po liczbie całkowitej.

Chen OT
źródło