Swift类型检查在非常短的函数上耗费太长时间

25

我已经将Swift编译器标志-warn-long-function-bodies设置为90毫秒,以查看我的项目中哪些函数需要太长时间来编译(由于类型检查)。

我有以下方法:

func someKey(_ sectionType: SectionType, row: Int) -> String {
    let suffix = row == 0 ? "top" : "content"
    return "\(sectionType)_\(suffix)"
}

(SectionType 是一个基于字符串的枚举)

如上所述,在 2017 年款 MacBook Pro 上花费了 96 毫秒。我尝试的第一件事是绕过字符串插值,改用 \(sectionType.rawValue) 而不是 \(sectionType),但现在它给出了 106 毫秒。走错了路...

接下来,我做了以下更改:

let suffix = row == 0 ? "top" : "content"

to:

let suffix = "top"

警告消失了,因此是三目运算符引起了问题。
我尝试使用以下方法代替:
let suffix: String = { // Note the type annotation!
    if row == 0 {
        return "top"
    }
    return "content"
}()

...但现在是闭包需要 97 毫秒 (整个函数,101 毫秒)。

我甚至尝试了更明确的写法:

    let suffix: String = {
        if row == 0 {
            return String("top")
        } else {
            return String("content")
        }
    }()

...然后我得到了闭包: 94ms; 函数: 98ms。

发生了什么?

我的90毫秒限制太低了吗?我知道字典字面量存在(或者说仍然存在)类型检查错误,但这似乎完全不同...?

我的环境是Xcode 8.3.2 (8E2002),Swift: Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42)


但等等!还有更多...

我尝试了这个函数体:

func someKey(_ sectionType: SectionType, row: Int) -> String {
    if row == 0 {
        return "\(sectionType.rawValue)_top"
    } else {
        return "\(sectionType.rawValue)_content"
    }
}

...并且它需要97ms~112ms!?


补充:我将函数和枚举移植到一个干净、最小的项目(Single View Application)中设置了相同的警告,但没有发生。我确定整个项目在某种程度上影响了这个方法,但还不能确定具体是如何影响的...


补充2:我测试了我的函数的静态版本:无论row的值为何,都使用固定后缀“top”(这需要少于90毫秒,并且不会触发任何警告),但是添加了以下if块:

func someKey(_ sectionType: SectionType, row: Int) -> String {
    if row == 0 {
        print("zero")
    } else {
        print("non-zero")
    }

    let suffix: String = "top"
    return "\(sectionType)_\(suffix)"
}

这让我想起了96~98毫秒!所以,当将行与零进行比较时,问题就出现了?
解决方法:我一直在尝试我的代码,不知怎么的,我发现如果我用switch语句替换if块,问题就解决了。
func someKey(_ sectionType: SectionType, row: Int) -> String {
    let suffix: String = {
        switch row {
        case 0:
            return "top"
        default:
            return "content"
        }
    }()
    return "\(sectionType)_\(suffix)"
}

我不会回答自己的问题,因为我认为这并不是真正发生的事情的解释。


2
回答你的问题“90毫秒太低了吗?”,不是。在我看来,这应该很容易编译。这可能是一种类型推断的问题?如果你在第一个例子中添加类型let suffix: String = ...会发生什么? - Fogmeister
1
作为我的上面评论的附带说明。有一部非常好的视频(我会找到链接)关于类型检查与类型推断。前者容易,而后者非常困难。 - Fogmeister
1
看看这个视频 :D https://www.youtube.com/watch?v=IbjoA5xVUq0不过很奇怪,你仍然需要很长的编译时间 :( 如果你将第一个示例的return行更改为静态语句,比如return“Hello, world!”,会发生什么呢?只是为了检查它是第一行还是最后一行的插值。 - Fogmeister
1
没有比较的情况下编译时间是多少?(将“长时间”限制设置为5毫秒以再次触发警告)。 - Fogmeister
2
从我的测试中发现:
  1. 字符串插值可能需要时间 "(sectionType)_(suffix)"
  2. 字符串构造器可能需要时间:String("top")(可能是因为构造器重载的原因)
- Robert
显示剩余6条评论
1个回答

5

我认为这是三目运算符。

我在 Xcode 11 中也有类似的结果(约93毫秒),但编译时间可通过以下方式降至约23毫秒:

func someKey(_ sectionType: SectionType, row: Int) -> String {

    var suffix = "top"

    if row != 0 {
        suffix = "content"
    }

    return "\(sectionType)_\(suffix)"
}

通过更改这行代码的逻辑,我认为我们可以证明这是三值逻辑,因为该方法的执行时间降至 ~1ms。我只是将“row”变成了布尔型。

func someKey(_ sectionType: SectionType, row: Bool) -> String {
    let suffix = row ? "top" : "content"
    return "\(sectionType)_\(suffix)"
}

同样(没有双关语),将三元逻辑改为let suffix = row != 0 ? "top" : "content"可将编译时间减半,与我第一个代码块相当。对于Swift来说,!===更易理解和快速。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接