gpt4 book ai didi

performance - +ing Swift 的 AnyObject 数组比 +ing 的 T 数组快得多

转载 作者:搜寻专家 更新时间:2023-10-30 21:57:20 25 4
gpt4 key购买 nike

给定以下三个简单函数:

func twice_Array_of_Int(a: [Int]) -> [Int] {
return a + a
}

func twice_Array_of_T<T>(a: [T]) -> [T] {
return a + a
}

func twice_Array_of_Any(a: [AnyObject]) -> [AnyObject] {
return a + a
}

假设发布版本 (-Os),您预计它们的性能如何比较?

我的期望是 [Int] -> [Int] 会比 [AnyObject] -> [AnyObject] 快得多...而且它是.. . 快几个数量级。

但是,我还期望 [T] -> [T] 的性能比 [AnyObject] -> [AnyObject] 好得多,而且几乎和 一样快>[Int] -> [Int]...对吗?

这里我被证明是错误的:甚至 [AnyObject] -> [AnyObject](甚至包括转换回 [Int])比 快 5 倍[T] -> [T]!这令人失望,尤其是因为泛型是 Swift 最有前途的特性之一。

在他们的一个 WWDC 视频中,Apple 工程师提到他们正在本地实现泛型,即使用它们不会导致代码膨胀。这是否解释了 [T] -> [T] 的糟糕表现?如果他们只是在编译时扩展泛型函数,[T] -> [T][Int] -> [Int] 的性能应该是一样的,对吧?

测试代码如下:

func testPerformance_twice_Array_of_Int() {
let a = Array(1...100_000)
self.measureBlock {
let twice_a = twice_Array_of_Int(a)
}
// average: 0.000, relative standard deviation: 76.227%
}

func testPerformance_twice_Array_of_T() {
let a = Array(1...100_000)
self.measureBlock {
let twice_a = twice_Array_of_T(a)
}
// measured [Time, seconds] average: 0.554, relative standard deviation: 7.846%
}

func testPerformance_twice_Array_of_Any() {
let a = Array(1...100_000)
self.measureBlock {
let twice_a = twice_Array_of_Any(a) as [Int]
}
// average: 0.115, relative standard deviation: 8.303%

// without the cast to [Int] = average: 0.039, relative standard deviation: 2.931%
}

我很想听听您的意见,以及您打算如何将其纳入您的代码设计。

编辑

我刚刚做了一个更简单的测量,结果更令人吃惊:

func ==(lhs: (Int, Int), rhs: (Int, Int)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1
}

相比于:

func ==<T: Equatable>(lhs: (T, T), rhs: (T, T)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1
}

结果:

func testPerformance_Equals_Tuple_Int() {
let a = (2, 3)
let b = (3, 2)
XCTAssertFalse(a == b)
let i = 1_000_000
self.measureBlock() {
for _ in 1...i {
let c = a == b
}
// average: 0.002, relative standard deviation: 9.781%
}
}

相比于:

func testPerformance_Equals_Tuple_T() {
let a = (2, 3)
let b = (3, 2)
XCTAssertFalse(a == b)
let i = 1_000_000
self.measureBlock() {
for _ in 1...i {
let c = a == b
}
// average: 2.080, relative standard deviation: 5.118%
}
}

中缀函数的通用版本慢了 1000 多倍!

编辑 2

8 月 21 日,Austin Zheng 在 Swift 语言用户组聚会上发表了关于“枚举、模式匹配和泛型”的演讲(Chris Lattner 是特邀嘉宾)。他说 Swift 会发出针对常见类型优化的代码,但会根据运行时的需要回退到针对其他类型的函数的原生通用版本。请参阅:http://realm.io/news/swift-enums-pattern-matching-generics/ (从 32:00 开始)。

编辑 3

随着 Swift 2 的发布,这更新早就该更新了(就在我喘口气的时候)......

最佳答案

I'd love to hear your opinion and how you plan to factor this into your code design.

您不应该将此考虑到您的代码设计中。 Swift 编译器正在快速发展,优化器也在不断地、激进地发展。根据优化器早期版本的微基准更改编码实践是“过早优化”的最糟糕形式。

为清晰起见的代码。正确性代码。当您看到性能问题时,请调查它们。没有比崩溃的程序慢的程序了。 [Int][T] 都比 [AnyObject](你必须经常投并验证)。选择应该不难。当您有一些实时代码表明 Instruments 中的 [T] 存在问题时,您应该研究其他选项(尽管我仍会将 [AnyObject] 放在底部;上面代码中的明显解决方案是编写一个特殊情况的重载来处理 [Int] 如果真的更快的话)。

由于您有一个有趣的测试用例,展示了通用和 native 之间的惊人差异,因此打开雷达 (bugreport.apple.com) 是合适的。这样,当问题得到解决时,您清晰、正确的代码将免费获得速度提升。


编辑:我还没有查看汇编器输出(你应该),但我确实有几个理论可以解释为什么这是真的(如果它确实是真的;我也没有复制它)。 [AnyObject] 可以在这里替换为 NSArray,它有 radically different performance characteristics来自 数组。这就是你永远不应该认为“[AnyObject] 更快”的关键原因是基于一些不是你的真实代码的微基准测试。 a+a 的性能可能与其他一些操作的性能完全无关。

关于 [Int][T],你可能会误解 Swift 是如何处理泛型的。 Swift 不会为每种类型的每个函数都创建一个全新的版本。它创建了一个通用版本。该版本可能不会像特定类型版本那样优化所有内容。例如,在这种情况下,[T] 版本可能会进行内存管理,而 [Int] 版本不会(我完全是在猜测)。优化器可以生成一个优化版本(这就是为什么你不应该尝试事后猜测它),但它可能不会(这就是为什么你有时可能不得不帮助它处理一个特殊的过载)。 Swift Yeti has a nice article explaining further .

同样,您永远不要假设您知道优化器将要做什么而不对至少与您关心的代码相似的实时代码进行测试(并且直到你有理由相信这是一个性能瓶颈)。很容易写出“疯狂的代码,因为它更快”,实际上速度要慢得多,但仍然很疯狂。

优化器知识就是力量,但如果您不知道何时使用它,它就是一种危险的力量。

关于performance - +ing Swift 的 AnyObject 数组比 +ing 的 T 数组快得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25959857/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com