Warum ist die Kompilierungszeit von Swift so langsam?


209

Ich benutze Xcode 6 Beta 6.

Das nervt mich schon seit einiger Zeit, aber es erreicht einen Punkt, an dem es jetzt kaum noch verwendbar ist.

Mein Projekt hat eine anständige Größe von 65 Swift-Dateien und ein paar überbrückte Objective-C-Dateien (die wirklich nicht die Ursache des Problems sind).

Es scheint, als würde eine geringfügige Änderung an einer Swift-Datei (wie das Hinzufügen eines einfachen Leerraums in einer Klasse, die in der App kaum verwendet wird) dazu führen, dass die gesamten Swift-Dateien für das angegebene Ziel neu kompiliert werden.

Nach einer eingehenderen Untersuchung habe ich festgestellt, dass die CompileSwiftPhase, in der Xcode den swiftcBefehl für alle Swift-Dateien Ihres Ziels ausführt , fast 100% der Compilerzeit in Anspruch nimmt .

Ich habe einige weitere Untersuchungen durchgeführt, und wenn ich den App-Delegaten nur mit einem Standard-Controller belasse, ist die Kompilierung sehr schnell, aber da ich immer mehr meiner Projektdateien hinzufügte, wurde die Kompilierungszeit langsam.

Mit nur 65 Quelldateien dauert das Kompilieren jedes Mal etwa 8/10 Sekunden. Überhaupt nicht sehr schnell .

Ich habe keinen Beitrag gesehen, der über dieses Problem spricht, außer diesem , aber es war eine alte Version von Xcode 6. Ich frage mich also, ob ich in diesem Fall der einzige bin.

AKTUALISIEREN

Ich habe einige Swift-Projekte auf GitHub wie Alamofire , Euler und CryptoSwift überprüft , aber keines von ihnen hatte genug Swift-Dateien, um sie tatsächlich zu vergleichen. Das einzige Projekt, bei dem ich feststellte, dass es eine anständige Größe hatte, war SwiftHN , und obwohl es nur ein Dutzend Quelldateien hatte, konnte ich immer noch das Gleiche überprüfen, einen einfachen Speicherplatz und das gesamte Projekt musste neu kompiliert werden wenig Zeit (2/3 Sekunden).

Im Vergleich zu Objective-C-Code, bei dem sowohl der Analysator als auch die Kompilierung blitzschnell sind, scheint Swift wirklich nie in der Lage zu sein, große Projekte zu bearbeiten, aber bitte sagen Sie mir, dass ich falsch liege.

UPDATE Mit Xcode 6 Beta 7

Immer noch keine Verbesserung. Das wird langsam lächerlich. Aufgrund des Mangels #importan Swift sehe ich wirklich nicht, wie Apple dies jemals optimieren kann.

UPDATE Mit Xcode 6.3 und Swift 1.2

Apple hat inkrementelle Builds (und viele andere Compiler-Optimierungen) hinzugefügt . Sie müssen Ihren Code auf Swift 1.2 migrieren, um diese Vorteile zu sehen. Apple hat jedoch in Xcode 6.3 ein Tool hinzugefügt, das Ihnen dabei hilft:

Geben Sie hier die Bildbeschreibung ein

JEDOCH

Freue dich nicht zu schnell wie ich. Der Graph-Solver, mit dem der Build inkrementell gemacht wird, ist noch nicht sehr gut optimiert.

In der Tat werden zunächst keine Änderungen der Funktionssignaturen betrachtet. Wenn Sie also ein Leerzeichen im Block einer Methode hinzufügen, werden alle Dateien, die von dieser Klasse abhängen, neu kompiliert.

Zweitens scheint es, den Baum basierend auf den Dateien zu erstellen, die neu kompiliert wurden, auch wenn eine Änderung sie nicht beeinflusst. Wenn Sie diese drei Klassen beispielsweise in verschiedene Dateien verschieben

class FileA: NSObject {
    var foo:String?
}
class FileB: NSObject {
    var bar:FileA?
}
class FileC: NSObject {
    var baz:FileB?
}

Wenn Sie nun Änderungen vornehmen FileA, markiert der Compiler offensichtlich FileAdie Neukompilierung. Es wird auch neu kompiliert FileB(das wäre aufgrund der Änderungen an in Ordnung FileA), aber auch, FileCweil FileBes neu kompiliert wird, und das ist ziemlich schlecht, weil es hier FileCnie verwendet wird FileA.

Ich hoffe also, dass sie diesen Abhängigkeitsbaumlöser verbessern ... Ich habe mit diesem Beispielcode ein Radar geöffnet .

UPDATE Mit Xcode 7 Beta 5 und Swift 2.0

Gestern hat Apple die Beta 5 veröffentlicht und in den Versionshinweisen konnten wir sehen:

Swift Language & Compiler • Inkrementelle Builds: Wenn Sie nur den Hauptteil einer Funktion ändern, sollten abhängige Dateien nicht mehr neu erstellt werden. (15352929)

Ich habe es versucht und ich muss sagen, dass es jetzt wirklich (wirklich!) Gut funktioniert. Sie haben die inkrementellen Builds schnell optimiert.

Ich empfehle Ihnen dringend, einen swift2.0Zweig zu erstellen und Ihren Code mit XCode 7 Beta 5 auf dem neuesten Stand zu halten. Sie werden von den Verbesserungen des Compilers begeistert sein (ich würde jedoch sagen, dass der globale Status von XCode 7 immer noch langsam und fehlerhaft ist).

UPDATE mit Xcode 8.2

Es ist eine Weile her seit meinem letzten Update zu diesem Thema, also hier ist es.

Unsere App besteht jetzt aus etwa 20.000 Zeilen fast ausschließlich Swift-Code, was anständig, aber nicht herausragend ist. Es wurde schnell 2 und dann schnell 3 migriert. Das Kompilieren auf einem Macbook Pro Mitte 2014 (2,5 GHz Intel Core i7) dauert ca. 5 / 6m, was bei einem sauberen Build in Ordnung ist.

Der inkrementelle Build ist jedoch immer noch ein Witz, obwohl Apple behauptet:

Xcode erstellt nicht das gesamte Ziel neu, wenn nur kleine Änderungen vorgenommen wurden. (28892475)

Offensichtlich denke ich, dass viele von uns nur gelacht haben, nachdem sie diesen Unsinn überprüft haben (das Hinzufügen einer privaten (privaten!) Eigenschaft zu einer Datei meines Projekts wird das Ganze neu kompilieren ...)

Ich möchte Sie auf diesen Thread in den Apple-Entwicklerforen verweisen, der weitere Informationen zu diesem Problem enthält (und ab und zu auch die Kommunikation der Apple-Entwickler zu diesem Thema zu schätzen weiß).

Grundsätzlich haben sich die Leute ein paar Dinge ausgedacht, um den inkrementellen Build zu verbessern:

  1. Fügen Sie eine HEADER_MAP_USES_VFSProjekteinstellung hinzu, die auf festgelegt isttrue
  2. Deaktivieren Sie Find implicit dependenciesIhr Schema
  3. Erstellen Sie ein neues Projekt und verschieben Sie Ihre Dateihierarchie in das neue.

Ich werde Lösung 3 ausprobieren, aber Lösung 1/2 hat bei uns nicht funktioniert.

Was in dieser ganzen Situation ironischerweise lustig ist, ist, dass wir beim Betrachten des ersten Beitrags zu diesem Thema Xcode 6 mit schnellem 1 oder schnellem 1.1-Code verwendet haben, als wir die erste Kompilierung der Kompilierung erreichten und jetzt, ungefähr zwei Jahre später, trotz tatsächlicher Verbesserungen von Apple Die Situation ist genauso schlimm wie bei Xcode 6. Wie ironisch.

Ich bedaure es WIRKLICH , Swift für unser Projekt gegenüber Obj / C gewählt zu haben, da es täglich frustriert ist. (Ich wechsle sogar zu AppCode, aber das ist eine andere Geschichte)

Wie auch immer, ich sehe, dass dieser SO-Beitrag zum Zeitpunkt des Schreibens mehr als 32.000 Aufrufe und 143 Ups hat, also bin ich wohl nicht der einzige. Bleib dran, Leute, obwohl du in dieser Situation pessimistisch bist, könnte am Ende des Tunnels etwas Licht sein.

Wenn Sie die Zeit (und den Mut!) Haben, begrüßt Apple wohl das Radar.

Bis zum nächsten Mal! Prost

UPDATE Mit Xcode 9

Stolpert dieser heute. Xcode führte leise ein neues Build-System ein, um die derzeitige schreckliche Leistung zu verbessern. Sie müssen es über die Arbeitsbereichseinstellungen aktivieren.

Geben Sie hier die Bildbeschreibung ein

Habe es schon versucht, werde diesen Beitrag aber aktualisieren, nachdem er fertig ist. Sieht aber vielversprechend aus.


1
Interessant! Ich frage mich, ob es nur eine fehlende Optimierung ist oder die Notwendigkeit, so viele Dateien zu analysieren, da es keine Schnittstellendateien gibt.
Zaph

2
Hatte ähnliche Probleme, und am Ende wurde mir klar, dass dies an benutzerdefinierten Operatoren lag, die in Entitätsklassen zum Deserialisieren von JSON verwendet wurden. Wenn Sie eine verwenden, empfehle ich Ihnen, nacheinander zu versuchen, auf die normale Funktion umzusteigen und zu prüfen, ob sich etwas ändert.
Antonio

4
Die Kompilierung ist in meinem Projekt seit XCode 6 Beta 6 zahnbrechend langsam geworden. Ich bin mir nicht sicher, ob es an Änderungen in der Beta oder an meinem Code liegt. Aber mein Projekt ist noch nicht einmal groß (~ 40-50 Swift-Dateien).
BadmintonCat

2
Das Kompilieren ist unerträglich langsam geworden, als mein Projekt gewachsen ist. Ich bin auch auf mehrere Pods angewiesen, was das Problem sicher noch verschlimmert. Hierbei wird die aktuelle Nicht-Beta-Version verwendet.
Andy

2
Die inkrementelle Erstellung erfolgt weiterhin in einer "konservativen Abhängigkeitsanalyse, sodass möglicherweise immer noch mehr Dateien neu erstellt werden als unbedingt erforderlich". Hoffentlich wird es sich mit der Zeit verbessern.
nmdias

Antworten:


70

Nun, es stellte sich heraus, dass Rob Napier Recht hatte. Es war eine einzelne Datei (eigentlich eine Methode), die den Compiler dazu brachte, berzek zu werden.

Versteh mich jetzt nicht falsch. Swift kompiliert jedes Mal alle Ihre Dateien neu, aber das Tolle ist jetzt, dass Apple Echtzeit-Kompilierungsfeedback zu den kompilierten Dateien hinzugefügt hat, sodass Xcode 6 GM jetzt in Echtzeit anzeigt, welche Swift-Dateien kompiliert werden und welchen Status die Kompilierung aufweist Wie Sie in diesem Screenshot sehen können:

Geben Sie hier die Bildbeschreibung ein

Dies ist sehr praktisch, um zu wissen, welche Ihrer Dateien so lange dauert. In meinem Fall war es dieser Code:

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

return dic.copy() as NSDictionary

weil das Eigentum titlevom Typ war var title:String?und nicht NSString. Der Compiler wurde verrückt, als er es dem hinzufügte NSMutableDictionary.

Ändern zu:

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

return dic.copy() as NSDictionary

Die Zusammenstellung ging von 10/15 Sekunden (vielleicht sogar mehr) auf eine einzelne Sekunde zurück ... erstaunlich.


3
Vielen Dank für die Antwort. Dies kann für andere sehr nützlich sein, die dort nachjagen. Die Typ-Inferenz-Engine bleibt während der Kompilierung hängen.
Rob Napier

1
Woher kommst du zu dieser Ansicht @apouche? Ich sehe es nicht in xcode
Eric

2
Sie müssen den Debug-Assistenten (CMD + 8) öffnen und auf den aktuellen Build klicken
Apouche

1
Ja, ich bin mir sicher, dass Apple dies später optimieren wird, sonst ist es hier und da zum Scheitern verurteilt, Projekte in der realen Welt in Kürze durchzuführen.
Apouche

1
Wie komme ich zu diesem Tool, das anzeigt, welche Dateien kompiliert werden?
JGVB

41

Wir haben einige Dinge versucht, um dem entgegenzuwirken, da wir ungefähr 100.000 Zeilen Swift-Code und 300.000 Zeilen ObjC-Code haben.

Unser erster Schritt war die Optimierung aller Funktionen gemäß der Ausgabe der Funktionskompilierungszeiten (z. B. wie hier beschrieben https://thatthinginswift.com/debug-long-compile-times-swift/ ).

Als nächstes haben wir ein Skript geschrieben, um alle schnellen Dateien in einer Datei zusammenzuführen. Dadurch werden die Zugriffsebenen unterbrochen, aber unsere Kompilierungszeit wurde von 5-6 Minuten auf ~ 1 Minute erhöht.

Dies ist jetzt nicht mehr möglich, da wir Apple danach gefragt haben und sie geraten haben, Folgendes zu tun:

  1. Aktivieren Sie die Optimierung des gesamten Moduls in der Build-Einstellung "Swift Compiler - Code Generation". Wählen'Fast, Whole Module Optimization'

Geben Sie hier die Bildbeschreibung ein

  1. Fügen Sie in 'Swift Compiler - Benutzerdefinierte Flags' für Ihre Entwicklungsbuilds hinzu '-Onone'

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Wenn diese Flags gesetzt sind, kompiliert der Compiler alle Swift-Dateien in einem Schritt. Wir haben festgestellt, dass dies mit unserem Zusammenführungsskript viel schneller ist als das individuelle Kompilieren von Dateien. Ohne das -Onone'Überschreiben wird jedoch auch das gesamte Modul optimiert, was langsamer ist. Wenn wir das '-Onone'Flag in den anderen Swift-Flags setzen, wird die Optimierung gestoppt, aber nicht alle Swift-Dateien werden in einem Schritt kompiliert.

Weitere Informationen zur Optimierung des gesamten Moduls finden Sie in Apples Blog-Post hier - https://swift.org/blog/whole-module-optimizations/

Wir haben festgestellt, dass diese Einstellungen es unserem Swift-Code ermöglichen, in 30 Sekunden zu kompilieren :-) Ich habe keine Hinweise darauf, wie es bei anderen Projekten funktionieren würde, aber ich empfehle, es auszuprobieren, wenn die Swift-Kompilierungszeiten für Sie immer noch ein Problem darstellen.

Hinweis: Bei Ihren App Store-Builds sollten Sie das '-Onone'Flag weglassen, da die Optimierung für Produktions-Builds empfohlen wird.


4
Vielen Dank für diesen Rat! Ich verstehe einfach nicht, warum es in offiziellen Quellen nichts Vergleichbares gibt (zumindest leicht zu finden), zum Beispiel sollte (muss!) Der Artikel, den Sie erwähnen, die Bemerkung enthalten -Onone. Wir können derzeit nicht die Optimierung des gesamten Moduls verwenden, da dies zum Absturz des Compilers führt ... Aber Ihr Rat erhöht unsere Build-Geschwindigkeit um fast das 10-fache. Auf dem MacBook Air (jährlich 2013) wurden ungefähr 8 Minuten erstellt, jetzt sind es nur noch ungefähr 1 Minute und die Hälfte der Zeit, die für das Wechseln zwischen Zielen (wir haben App, Erweiterungen und wenige
interne

Ich habe diese Methode auch getestet und es ist nur die erwähnte Methode, die -Onone enthält und die Erstellungszeit erheblich verkürzt.
Vlad

Arbeite auch mit mir. Mithilfe der -OnoneHilfe können Sie die Erstellungszeiten verkürzen. Vielen Dank Kumpel!
nahung89

34

Es hat wahrscheinlich wenig mit der Größe Ihres Projekts zu tun. Es ist wahrscheinlich ein bestimmter Code, möglicherweise sogar nur eine Zeile. Sie können dies testen, indem Sie versuchen, jeweils eine Datei und nicht das gesamte Projekt zu kompilieren. Oder versuchen Sie, die Build-Protokolle zu überprüfen, um festzustellen, welche Datei so lange dauert.

Als Beispiel für die Arten von Code, die Probleme verursachen können, dauert die Kompilierung dieses 38-Zeilen-Kerns in Beta7 mehr als eine Minute. All dies wird durch diesen einen Block verursacht:

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

Vereinfachen Sie dies durch ein oder zwei Zeilen und es wird fast sofort kompiliert. Das Problem ist, dass dies ein exponentielles Wachstum (möglicherweise faktorielles Wachstum) im Compiler verursacht. Offensichtlich ist das nicht ideal, und wenn Sie solche Situationen isolieren können, sollten Sie Radargeräte öffnen, um diese Probleme zu beheben.


Ich bin mir nicht sicher, ob Sie meinen Kommentar zur CompileSwiftPhase gesehen haben. Es werden alle schnellen Dateien benötigt, auch wenn nur eine geändert wurde. Wenn es sich also um eine Datei handelt, die einige Zeit in Anspruch nimmt (was ich sehr bezweifle), wird der Compiler Ihnen niemals sagen, um welche es sich handelt.
Apouche

10
Sie können einzelne Dateien kompilieren, indem Sie swiftcsehen, wie lange sie dauern.
Rob Napier

Ich entschuldige mich dafür, dass ich Ihnen das Kopfgeld nicht gegeben habe, weil ich es zunächst nicht glaube. Ich habe auch versucht, Dateien einzeln zu kompilieren, aber es war zu umständlich (musste jedes Mal die richtigen Frameworks und Deps angeben), also habe ich aufgegeben. Bitte
lesen

Ich denke nicht, dass es auf der Projektgröße basiert. Mein Projekt hat nur 4 schnelle Dateien und hat plötzlich unglaublich langsam mit dem Kompilieren begonnen. Es war gestern schnell hell. Ich kann nichts sagen, was ich speziell an meinem Projekt getan habe, außer Symbole hinzufügen und Bilder starten.
Travis M.

33

Wenn Sie versuchen, bestimmte Dateien zu identifizieren, die Ihre Kompilierungszeit verlangsamen, können Sie versuchen, sie über xctool über Ihre Befehlszeile zu kompilieren. Dadurch erhalten Sie Datei für Datei Kompilierungszeiten.

Zu beachten ist, dass standardmäßig 2 Dateien gleichzeitig pro CPU-Kern erstellt werden und nicht die verstrichene "Netto" -Zeit, sondern die absolute "Benutzer" -Zeit angegeben wird. Auf diese Weise gleichen sich alle Timings zwischen parallelisierten Dateien aus und sehen sehr ähnlich aus.

Um dies zu überwinden, setzen Sie das -jobsFlag auf 1 , damit keine Dateierstellungen parallelisiert werden. Es wird länger dauern, aber am Ende haben Sie "Netto" -Kompilierungszeiten, die Sie Datei für Datei vergleichen können.

Dies ist ein Beispielbefehl, der den Trick ausführen sollte:

xctool -workspace <your_workspace> -scheme <your_scheme> -jobs 1 build

Die Ausgabe der Phase "Compile Swift-Dateien" wäre ungefähr so:

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

Anhand dieser Ausgabe können Sie schnell erkennen, welche Dateien länger als andere zum Kompilieren benötigen. Darüber hinaus können Sie mit hoher Genauigkeit feststellen, ob Ihre Refactorings (explizite Casts, Typhinweise usw.) die Kompilierungszeiten für bestimmte Dateien verkürzen oder nicht.

HINWEIS: Technisch gesehen können Sie dies auch tun, xcodebuildaber die Ausgabe ist unglaublich ausführlich und schwer zu konsumieren.


1
Stellen Sie einfach sicher, dass die Gesamtmoduloptimierung Ihres Projekts auf false gesetzt ist, da sonst die einzelnen schnellen Dateien nicht getrennt werden.
Sabes

1
Siehe Swift CompilerOptimization LevelfürFast, Whole Module Optimization [-O -whole-module-optimization]
Matt

26

In meinem Fall hat Xcode 7 überhaupt keinen Unterschied gemacht. Ich hatte mehrere Funktionen, deren Kompilierung einige Sekunden dauerte.

Beispiel

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

Nach dem Auspacken der Optionen verringerte sich die Erstellungszeit um 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)

Weitere Beispiele finden Sie in diesem Beitrag und in diesem Beitrag .

Build Time Analyzer für Xcode

Ich habe ein Xcode-Plug-In entwickelt, das für alle nützlich sein kann, bei denen diese Probleme auftreten.

Bild

In Swift 3 scheint es Verbesserungen zu geben. Hoffentlich wird unser Swift-Code dann schneller kompiliert.


Super, hoffe ich kann dir mehr als +1 geben. Sie sind wahr und Ihr Plugin ist auch großartig. Ich habe das benutzt und meine Build-Zeit wird immer kürzer, was eine superschnelle Entwicklung bedeutet, da diese Optionen manchmal ein Albtraum sind und den Compiler verlangsamen.
Hardikdevios

Fantastisch ! Ihr Werkzeug hilft mir sehr. Vielen Dank
Phil

Tolles Plugin - Wirklich nützlich! Danke
365SplendidSuns

@ Robert Gummesson, haben wir ein Tool für Objective-C-Code?
Ashok

19

Wahrscheinlich können wir den Swift-Compiler nicht reparieren, aber etwas, das wir reparieren können, ist unser Code!

Im Swift-Compiler gibt es eine versteckte Option, die die genauen Zeitintervalle ausgibt, die der Compiler benötigt, um jede einzelne Funktion zu kompilieren : -Xfrontend -debug-time-function-bodies. Dadurch können wir Engpässe in unserem Code finden und die Kompilierungszeit erheblich verbessern.

Führen Sie im Terminal einfach Folgendes aus und analysieren Sie die Ergebnisse:

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

Der großartige Brian Irace hat einen brillanten Artikel darüber geschrieben. Profilieren Sie Ihre Swift-Kompilierungszeiten .


2
Für diejenigen mit zsh alias grep='noglob grep'zuerst wird othewise grep nicht funktionieren
Jaime Agudo

16

Die Lösung ist Gießen.

Ich hatte eine riesige Auswahl an Tonnen von Wörterbüchern, wie folgt:

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

Das Kompilieren dauerte ungefähr 40 Minuten. Bis ich die Wörterbücher so gegossen habe:

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

Dies funktionierte für fast jedes andere Problem, auf das ich in Bezug auf Datentypen stieß, die ich fest in meine Anwendung codiert hatte.


6
Nun ja, es ist Teil der Optimierungen, die Sie vornehmen, um die Kompilierungszeiten zu verbessern. Das Hauptproblem des aktuellen Swift-Compilers besteht jedoch darin, dass bei jeder geringfügigen Änderung alle einzelnen Swift-Dateien neu kompiliert werden.
Apouche

4
Das wäre lustig, wenn es nicht so traurig wäre.
Tom Andersen

15

Zu beachten ist, dass die Inferenz-Engine vom Typ Swift bei verschachtelten Typen sehr langsam sein kann. Sie können sich einen Überblick darüber verschaffen, was die Langsamkeit verursacht, indem Sie das Build-Protokoll nach einzelnen Kompilierungseinheiten durchsuchen, die lange dauern, den vollständigen Xcode-Spawn-Befehl kopieren und in ein Terminalfenster einfügen und dann STRG- \ drücken, um abzurufen einige Diagnosen. Ein vollständiges Beispiel finden Sie unter http://blog.impathic.com/post/99647568844/debugging-slow-swift-compile-times .


Das ist für mich die beste Antwort (siehe Link). Ich konnte EINFACH die zwei verschiedenen Zeilen finden, die ein Problem waren, und es lösen, indem ich meine Zeilen in kleinere Zeilen zerlegte.
Nico

Das ist eine sehr nützliche Antwort, denn sie zeigt, wie man herausfindet, wo der Compiler verrückt geworden ist. In meinem Fall war es das Folgende: 'curScore [curPlayer% 2] + curScore [2 + curPlayer% 2] == 3 && maker% 2 == curPlayer% 2' Sobald ich es von 'if' nach 'let verschoben habe ', es führte zu "Ausdruck war zu komplex, um in angemessener Zeit gelöst zu werden; erwägen Sie, den Ausdruck in verschiedene Unterausdrücke aufzuteilen"
Dmitry

Dies ist definitiv der hilfreichste Weg, um dieses Problem zu beheben.
Richard Venable

9

Stellen Sie außerdem sicher, dass Sie beim Kompilieren für das Debuggen (entweder Swift oder Objective-C) die Option Nur aktive Architektur erstellen festlegen:

Geben Sie hier die Bildbeschreibung ein


6

Da all diese Dinge in der Beta sind und der Swift-Compiler (zumindest ab heute) nicht geöffnet ist, gibt es wohl keine wirkliche Antwort auf Ihre Frage.

Zunächst einmal ist der Vergleich von Objective-C mit dem Swift-Compiler irgendwie grausam. Swift befindet sich noch in der Beta-Phase, und ich bin sicher, dass Apple mehr an der Bereitstellung von Funktionen und der Behebung von Fehlern als an der Bereitstellung von Blitzgeschwindigkeit arbeitet (Sie beginnen nicht mit dem Bau eines Hauses, indem Sie die Möbel kaufen). Ich denke, Apple wird den Compiler zu gegebener Zeit optimieren.

Wenn aus irgendeinem Grund alle Quelldateien vollständig kompiliert werden müssen, besteht möglicherweise die Möglichkeit, separate Module / Bibliotheken zu erstellen. Diese Option ist jedoch noch nicht möglich, da Swift Bibliotheken erst zulassen kann, wenn die Sprache stabil ist.

Ich vermute, dass sie den Compiler optimieren werden. Aus dem gleichen Grund, aus dem wir keine vorkompilierten Module erstellen können, kann es durchaus sein, dass der Compiler alles von Grund auf neu kompilieren muss. Sobald die Sprache eine stabile Version erreicht und sich das Format der Binärdateien nicht mehr ändert, können wir unsere Bibliotheken erstellen, und möglicherweise (?) Kann der Compiler auch seine Arbeit optimieren.

Nur raten, denn nur Apple weiß ...


"Aus dem gleichen Grund, aus dem wir keine vorkompilierten Module erstellen können, kann es durchaus sein, dass der Compiler alles von Grund auf neu kompilieren muss." nette Beobachtung, habe noch nie so darüber nachgedacht.
Chakrit

1
2017 und es ist immer noch langsam
Pedro Paulo Amorim

2017 mit Xcode 9 und neuem Build-System und immer noch langsam
Pableiros

2018 mit Xcode 9 habe ich ein Projekt mit mehr als 50 schnellen Dateien. Wenn ich sauber baue, hat es jetzt die 5-Minuten-Marke überschritten und meine Kompilierung ist noch nicht abgeschlossen.
Chen Li Yong

5

Gehen Sie für Xcode 8 zu Projekteinstellungen, dann zu Editor> Build-Einstellung hinzufügen> Benutzerdefinierte Einstellung hinzufügen und fügen Sie Folgendes hinzu:

SWIFT_WHOLE_MODULE_OPTIMIZATION = YES

Durch Hinzufügen dieses Flags wurden unsere Clean-Build-Kompilierungszeiten für ein schnelles 40KLOC-Projekt auf wundersame Weise von 7 Minuten auf 65 Sekunden gesenkt. Kann auch bestätigen, dass 2 Freunde ähnliche Verbesserungen bei Unternehmensprojekten gesehen haben.

Ich kann nur annehmen, dass dies eine Art Fehler in Xcode 8.0 ist

EDIT: Es scheint in Xcode 8.3 für einige Leute nicht mehr zu funktionieren.


2
Wo sind bitte die "Projekteinstellungen"?
Raniys

@Raniys Klicken Sie in Xcode auf das blaue Symbol auf der Stammebene im linken Bereich.
Chris

Ich stelle fest, dass dies ab Xcode 8.3 (ohne Beta) in meinem Fall nicht mehr funktioniert :(
Chris

4

Leider ist der Swift-Compiler immer noch nicht für eine schnelle und inkrementelle Kompilierung optimiert (ab Xcode 6.3 Beta). In der Zwischenzeit können Sie einige der folgenden Techniken verwenden, um die Kompilierungszeit von Swift zu verbessern:

  • Teilen Sie die App in Frameworks auf, um die Auswirkungen auf die Neukompilierung zu verringern. Beachten Sie jedoch, dass Sie zyklische Abhängigkeiten in Ihrer App vermeiden müssen. Weitere Informationen zu diesem Thema finden Sie in diesem Beitrag: http://bits.citrusbyte.com/improving-swift-compile-time/

  • Verwenden Sie Swift für Teile Ihres Projekts, die ziemlich stabil sind und sich nicht oft ändern. Für andere Bereiche, in denen Sie sehr häufig Änderungen vornehmen müssen, oder Bereiche, in denen viele Kompilierungs- / Ausführungsiterationen abgeschlossen sein müssen (fast alle UI-bezogenen Dinge), verwenden Sie Objective-C besser mit einem Mix-and-Match-Ansatz.

  • Versuchen Sie die Laufzeitcode-Injektion mit 'Injection for Xcode'.

  • Verwenden Sie die Roopc-Methode: http://roopc.net/posts/2014/speeding-up-swift-builds/

  • Entlasten Sie die Inferenz-Engine vom schnellen Typ, indem Sie einige Hinweise mit expliziten Casts geben.


4

Die schnelle Erstellung von Arrays und Wörterbüchern scheint eine ziemlich beliebte Ursache dafür zu sein (speziell für Sie, die aus einem Ruby- Hintergrund stammen), d. H.

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"]

wird wahrscheinlich die Ursache sein, wo dies das Problem beheben sollte:

var a = NSMutableDictionary()
a["a"] = "b"
a["c"] = "d"
... and so on

4

Stellen Sie zum Debuggen und Testen sicher, dass Sie die folgenden Einstellungen verwenden, um die Kompilierungszeit von etwa 20 Minuten auf weniger als 2 Minuten zu verkürzen.

  1. Suchen Sie in den Einstellungen für die Projekterstellung nach "Optimierung". Schalten Sie das Debug auf "Schnellste [-O3]" oder höher.
  2. Set Build für aktive Architektur: JA
  3. Debug-Informationsformat: DWARF
  4. Optimierung des gesamten Moduls: NEIN

Ich habe unzählige Stunden darauf gewartet, dass das Projekt erstellt wird, nur um zu erkennen, dass ich diese eine kleine Änderung vornehmen und weitere 30 Minuten warten musste, um es zu testen. Dies sind die Einstellungen, die für mich funktioniert haben. (Ich experimentiere immer noch mit den Einstellungen)

Stellen Sie jedoch sicher, dass Sie mindestens "DWARF with dSYM" (wenn Sie Ihre Anwendung überwachen möchten) und Build Active Architecture auf "NO" setzen, damit Release / Archiving auf iTunes Connect übertragen werden kann (ich erinnere mich, dass ich auch hier einige Stunden verschwendet habe).


4
Ich kann mich irren, aber würde das Festlegen erhöhter Optimierungsstufen nicht tatsächlich die Bauzeit verlängern? Die Optimierungsstufen verbessern die Laufzeitleistung.
Michael Wasserfall

1
Set Build for Active Architecture: YESgab mir eine Reduzierung der Kompilierungszeit um ca. 45%. Vielen Dank.
Jean Le Moignan

4

Der Compiler verbringt viel Zeit damit, die Typen abzuleiten und zu überprüfen. Das Hinzufügen von Typanmerkungen hilft dem Compiler also sehr.

Wenn Sie viele verkettete Funktionsaufrufe haben wie

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

Dann braucht der Compiler eine Weile, um herauszufinden, welcher Typ sumsein sollte. Das Hinzufügen des Typs hilft. Was auch hilft, ist das Ziehen der intermittierenden Schritte in separate Variablen.

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: +)

Insbesondere bei numerischen Typen CGFloatkann Intdies sehr hilfreich sein. Eine Literalzahl wie 2kann viele verschiedene numerische Typen darstellen. Der Compiler muss also aus dem Kontext herausfinden, um welchen es sich handelt.

Funktionen, deren Nachschlagen viel Zeit in +Anspruch nimmt, sollten ebenfalls vermieden werden. Die Verwendung mehrerer +Arrays zum Verketten mehrerer Arrays ist langsam, da der Compiler herausfinden muss, welche Implementierung von +jedem für jedes aufgerufen werden soll +. Verwenden Sie stattdessen nach Möglichkeit ein var a: [Foo]mit append().

Sie können eine Warnung hinzufügen, um festzustellen, welche Funktionen in Xcode nur langsam kompiliert werden .

Suchen Sie in den Build-Einstellungen für Ihr Ziel nach anderen schnellen Flags und fügen Sie sie hinzu

-Xfrontend -warn-long-function-bodies=100

um für jede Funktion zu warnen, deren Kompilierung länger als 100 ms dauert.


4

Für die Projekte , die Objective-C und Swift Code mischen, können wir setzen -enable-bridging-pchin Other Swift Flags. Damit wird der Bridging-Header nur einmal analysiert und das Ergebnis (eine temporäre "vorkompilierte Header" - oder "PCH" -Datei) wird zwischengespeichert und für alle Swift-Dateien im Ziel wiederverwendet. Apple behauptete, es würde die Bauzeit um 30% verkürzen. Referenzlink:

HINWEIS: Dies funktioniert nur für Swift 3.1 und höher.


2

Der Neustart meines Mac hat bei diesem Problem Wunder bewirkt. Ich bin von 15-Minuten-Builds auf 30-Sekunden-Builds gewechselt, nur durch einen Neustart.


1

Die schnelle Kompilierungszeit wurde im neuen Xcode 6.3 verbessert

Compiler-Verbesserungen

Der Swift 1.2-Compiler wurde entwickelt, um stabiler zu sein und die Leistung in jeder Hinsicht zu verbessern. Diese Änderungen bieten auch eine bessere Erfahrung bei der Arbeit mit Swift in Xcode. Einige der sichtbarsten Verbesserungen sind:

Inkrementelle Builds

Nicht geänderte Quelldateien werden standardmäßig nicht mehr neu kompiliert, wodurch sich die Erstellungszeiten in den meisten Fällen erheblich verbessern. Bei größeren strukturellen Änderungen an Ihrem Code müssen möglicherweise noch mehrere Dateien neu erstellt werden.

Schnellere ausführbare Dateien

Debug-Builds erzeugen Binärdateien, die erheblich schneller ausgeführt werden, und neue Optimierungen bieten eine noch bessere Release-Build-Leistung.

Bessere Compilerdiagnose

Klarere Fehler- und Warnmeldungen sowie neue Fix-its erleichtern das Schreiben des richtigen Swift 1.2-Codes.

Stabilitätsverbesserungen

Die häufigsten Compiler-Abstürze wurden behoben. Außerdem sollten im Xcode-Editor weniger SourceKit-Warnungen angezeigt werden.


0

Hier ist ein weiterer Fall, der zu massiven Verlangsamungen mit Typinferenz führen kann. Koaleszenzoperatoren .

Ändern von Zeilen wie:

abs(some_optional_variable ?? 0)

zu

abs((some_optional_variable ?? 0) as VARIABLE_TYPE)

half mir, meine Kompilierungszeit von 70 auf 13 zu bringen


0

In Xcode 6.3.1 funktionierte nichts für mich - als ich rund 100 Swift-Dateien hinzufügte, hing Xcode zufällig beim Erstellen und / oder Indizieren. Ich habe eine modulare Option ohne Erfolg ausprobiert.

Die Installation und Verwendung von Xcode 6.4 Beta hat bei mir tatsächlich funktioniert.


0

Das hat für mich wie Zauberei funktioniert - Speed ​​Up Swift Compilation . Die Kompilierungszeit wurde von 10 Minuten auf 3 Minuten reduziert.

Er sagt , Sie drehen sollte auf die Whole Module Optimizationunter Zugabe von -Ononein Other Swift Flags.

Ich benutze Swift 3auf Xcode 8.3/Xcode 8.2 .


0

Das Mischen von Integer- Literal und Float- Literal in einem Ausdruck führt ebenfalls zu einer langen Kompilierungszeit.

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

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

Viele Ausdrücke zur Kompilierungszeit von 1000 + ms werden auf 10 bis 100 ms reduziert, nachdem ich ein .0Literal nach der Ganzzahl eingefügt habe .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.