为什么 Swift 编译时间这么慢? [英] Why is Swift compile time so slow?

查看:55
本文介绍了为什么 Swift 编译时间这么慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 Xcode 6 Beta 6.

这件事一直困扰着我一段时间,但现在几乎无法使用.

我的项目开始有体面大小的 65 个 Swift 文件和一些桥接的 Objective-C 文件(这实际上不是问题的原因).

似乎对任何 Swift 文件的任何轻微修改(例如在应用中几乎不使用的类中添加一个简单的空格)都会导致指定目标的整个 Swift 文件被重新编译.

经过更深入的调查,我发现几乎 100% 的编译器时间是 CompileSwift 阶段,其中 Xcode 运行 swiftc 命令对所有目标的 Swift 文件.

我做了一些进一步的调查,如果我只使用默认控制器保留应用程序委托,编译速度会非常快,但是随着我添加越来越多的项目文件,编译时间开始变得非常缓慢.

现在只有 65 个源文件,每次编译大约需要 8/10 秒.一点也不迅速.

除了 使用此示例代码.

使用 Xcode 7 beta 5 和 Swift 2.0 更新

昨天 Apple 发布了 beta 5,我们可以在发行说明中看到:

<块引用>

Swift 语言 &编译器• 增量构建:仅更改函数主体不应再导致重新构建依赖文件.(15352929)

我已经试过了,我必须说它现在工作得很好(真的!).他们极大地优化了 swift 的增量构建.

我强烈建议您创建一个 swift2.0 分支,并使用 XCode 7 beta 5 使您的代码保持最新.您会对编译器的增强感到高兴(但是我会说XCode 7 的全局状态仍然很慢且有问题)

使用 Xcode 8.2 更新

距离我上次更新这个问题已经有一段时间了,所以现在更新了.

我们的应用程序现在大约有 2 万行几乎完全是 Swift 代码,虽然不错,但并不出色.它经历了 swift 2 和 swift 3 迁移.在 2014 年中期的 Macbook pro(2.5 GHz Intel Core i7)上编译大约需要 5/6m,这在干净的构建上是可以的.

然而,尽管 Apple 声称,增量构建仍然是一个笑话:

<块引用>

Xcode 不会在只发生小的更改时重建整个目标.(28892475)

显然,我认为我们中的许多人在看完这些废话后都笑了(向我的项目的任何文件添加一个私有(私有!)属性将重新编译整个事情......)

我想指出你们在​​ Apple 开发者论坛上的

已经试过了,但会在完成后更新这篇文章.不过看起来很有希望.

解决方案

好吧,事实证明 Rob Napier 是对的.是一个文件(实际上是一种方法)导致编译器崩溃.

现在不要误会我的意思.Swift 每次都会重新编译您的所有文件,但现在最棒的是 Apple 在它编译的文件上添加了实时编译反馈,因此 Xcode 6 GM 现在可以实时显示正在编译哪些 Swift 文件以及编译状态正如您在此屏幕截图中看到的:

因此,了解您的哪个文件花费了这么长时间非常方便.就我而言,这是一段代码:

var dic = super.json().mutableCopy() 作为 NSMutableDictionarydic.addEntriesFromDictionary(["url" : self.url?.absoluteString ??"",标题":self.title ??"])返回 dic.copy() 作为 NSDictionary

因为属性 title 的类型是 var title:String? 而不是 NSString.将它添加到 NSMutableDictionary 时,编译器疯了.

改为:

var dic = super.json().mutableCopy() 作为 NSMutableDictionarydic.addEntriesFromDictionary(["url" : self.url?.absoluteString ??"","title" : NSString(string: self.title ?? "")])返回 dic.copy() 作为 NSDictionary

使编译时间从 10/15 秒(可能甚至更多)缩短到一秒……太棒了.

I'm using Xcode 6 Beta 6.

This is something that's been bugging me for some time now, but it's reaching a point where it's barely usable now.

My project is starting to have a decent size of 65 Swift files and a few bridged Objective-C files (which are really not the cause of the problem).

It seems like any slight modification to any Swift file (like adding a simple white space in a class that's barely used in the app) will cause the entire Swift files for the specified target to be recompiled.

After a deeper investigation, I've found that what is taking pretty much 100% of the compiler time is the CompileSwift phase where Xcode runs the swiftc command on all Swift files of your target.

I did some further investigation, and if I only keep the app delegate with a default controller the compilation is very fast, but as I was adding more and more of my project files, the compile time was starting to get really slow.

Now with only 65 source files, it takes about 8/10 seconds to compile each time. Not very swift at all.

I haven't seen any post talking about this issue except this one, but it was an old version of Xcode 6. So I'm wondering if I'm the only one in that case.

UPDATE

I've checked a few Swift projects on GitHub like Alamofire, Euler and CryptoSwift, but none of them had enough Swift files to actually compare. The only project I found that was starting a have decent size was SwiftHN, and even though it only had a dozen source files I was still able to verify the same thing, one simple space and the whole project needed recompilation which was starting to take a little time (2/3 seconds).

Compared to Objective-C code where both analyzer and compilation are blazing fast, this really feels like Swift will never be able to handle big projects, but please tell me I'm wrong.

UPDATE With Xcode 6 Beta 7

Still no improvement whatsoever. This is starting to get ridiculous. With the lack of #import in Swift, I really don't see how Apple will ever be able to optimize this.

UPDATE With Xcode 6.3 and Swift 1.2

Apple has added incremental builds (and many other compiler optimizations). You have to migrate your code to Swift 1.2 to see those benefits, but Apple added a tool in Xcode 6.3 to help you do so:

HOWEVER

Don't rejoice too quickly as I did. The graph solver that they use to make the build incremental is not very well optimised yet.

Indeed first, it doesn't look at function signature changes so if you add a space in the block of one method, all files depending on that class will be recompiled.

Second, it seems to create the tree based on the files that were recompiled even if a change doesn't affect them. For example, if you move these three classes into different files

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

Now if you modify FileA, the compiler will obviously mark FileA to be recompiled. It will also recompile FileB (that would be OK based on the changes to FileA), but also FileC because FileB is recompiled, and that is pretty bad because FileC never uses FileA here.

So I hope they improve that dependency tree solver... I've opened a radar with this sample code.

UPDATE With Xcode 7 beta 5 and Swift 2.0

Yesterday Apple released the beta 5 and inside the release notes we could see:

Swift Language & Compiler • Incremental builds: changing just the body of a function should no longer cause dependent files to be rebuilt. (15352929)

I have given it a try and I must say it is working really (really !) well now. They greatly optimized the incremental builds in swift.

I highly recommend you create a swift2.0 branch and keep your code up to date using XCode 7 beta 5. You will be pleased by the enhancements of the compiler (however I'd say the global state of XCode 7 is still slow & buggy)

UPDATE With Xcode 8.2

It's been a while since my last update on this issue so here it is.

Our app is now about 20k lines of almost exclusively Swift code, which is decent but not outstanding. It underwent swift 2 and than swift 3 migration. It takes about 5/6m to compile on a mid 2014 Macbook pro (2.5 GHz Intel Core i7) which is okay on a clean build.

However the incremental build is still a joke despite Apple claiming that:

Xcode will not rebuild an entire target when only small changes have occurred. (28892475)

Obviously I think many of us just laughed after checking out this nonsense (adding one private (private!) property to any file of my project will recompile the whole thing...)

I would like to point you guys to this thread on Apple developer forums which has some more information about the issue (as well as appreciated Apple dev communication on the matter once in a while)

Basically people have come up with a few things to try to improve the incremental build:

  1. Add a HEADER_MAP_USES_VFS project setting set to true
  2. Disable Find implicit dependencies from your scheme
  3. Create a new project and move your files hierarchy to the new one.

I will try solution 3 but solution 1/2 didn't work for us.

What's ironically funny in this whole situation is that looking at the first post on this issue we were using Xcode 6 with I believe swift 1 or swift 1.1 code when we reached the first compilations slugginess and now about two years later despite actual improvements from Apple the situation is just as bad as it was with Xcode 6. How ironic.

I actually REALLY regret choosing Swift over Obj/C for our project because of the daily frustration it involves. (I even switch to AppCode but that's another story)

Anyways I see this SO post has 32k+ views and 143 ups as of this writing so I guess I'm not the only one. Hang in there guys despite being pessimistic over this situation there might be some light at the end of the tunnel.

If you have the time (and courage!) I guess Apple welcomes radar about this.

Til next time ! Cheers

UPDATE With Xcode 9

Stumble upon this today. Xcode quietly introduced a new build system to improve on the current awful performance. You have to enable it through the workspace settings.

Have given a try yet but will update this post after it's done. Looks promising though.

解决方案

Well, it turned out that Rob Napier was right. It was one single file (actually one method) that was causing the compiler to go berzek.

Now don't get me wrong. Swift does recompile all your files each time, but the great thing now, is that Apple added real-time compilation feedback over the files it compiles, so Xcode 6 GM now shows which Swift files are being compiled and the status of compilation in real time as you can see in this screenshot:

So this comes very handy to know which of your files is taking so long. In my case it was this piece of code:

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

return dic.copy() as NSDictionary

because the property title was of type var title:String? and not NSString. The compiler was going crazy when adding it to the NSMutableDictionary.

Changing it to:

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

return dic.copy() as NSDictionary

made the compilation go from 10/15 seconds (maybe even more) down to a single second... amazing.

这篇关于为什么 Swift 编译时间这么慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆