【发布时间】:2018-12-09 02:34:48
【问题描述】:
我阅读了著名的Why is it faster to process a sorted array than an unsorted array?,并决定尝试使用其他语言,例如 Swift。我对 2 个非常相似的 sn-ps 代码之间的运行时间差异感到惊讶。
在 Swift 中,可以直接访问数组中的元素,也可以在 for-in 循环中使用下标访问元素。例如这段代码:
for i in 0..<size {
sum += data[i]
}
可以写成:
for element in data {
sum += element
}
size 具有 data 长度和 data 可求和元素数组。
所以,我刚刚在 Swift(下面的代码)中实现了与我在第一段中提到的问题相同的算法,令我惊讶的是,第一种方法比第二种方法快大约 5 倍。
我不太了解后台下标的实现,但我认为直接访问 Swift for-in 循环中的元素只是语法糖。
问题
我的问题是这两种for-in 语法有什么区别,为什么使用下标更快?
这里是计时器的详细信息。我在带有 Commande Line 项目的 2015 年初 MacBook Air 上使用 Xcode 9.4.1 和 Swift 4.1。
// Using Direct Element Access
Elapsed Time: 8.506288427
Sum: 1051901000
对
// Using Subscript
Elapsed Time: 1.483967902
Sum: 1070388000
额外问题:为什么 Swift 的执行速度比 C++ 慢 100 倍(两者都在同一个 Mac 上的 n Xcode 项目中执行)?例如,C++ 中 100,000 次重复所花费的时间几乎与 Swift 中 1,000 次重复所花费的时间相同。我的第一个猜测是 Swift 是一种比 C++ 更高级的语言,例如 Swift 会进行更多的安全检查。
这是我使用的 Swift 代码,我只修改了第二个嵌套循环:
import Foundation
import GameplayKit
let size = 32_768
var data = [Int]()
var sum = 0
var rand = GKRandomDistribution(lowestValue: 0, highestValue: 255)
for _ in 0..<size {
data.append(rand.nextInt())
}
// data.sort()
let start = DispatchTime.now()
for _ in 0..<1_000 {
// Only the following for-in loop changes
for i in 0..<size {
if data[i] <= 128 {
sum += data[i]
}
}
}
let stop = DispatchTime.now()
let nanoTime = stop.uptimeNanoseconds - start.uptimeNanoseconds
let elapsed = Double(nanoTime) / 1_000_000_000
print("Elapsed Time: \(elapsed)")
print("Sum: \(sum)")
【问题讨论】:
-
您是在 Swift Playground 中进行测试还是在编译后的应用中进行测试?
-
我正在使用已编译的应用程序(命令行项目)。
-
我怀疑您没有进行优化编译。使用
-O,我最多只能看到大约 10% 的成本,而不是 10 倍。如果要与 C++ 进行比较,还需要与-Ounchecked进行比较。 -
除非您使用
-0unchecked,否则每个基本算术运算都会执行一个分支(如果检查溢出,并崩溃而不是允许使用溢出的结果) -
@LouisLac 性能测试毫无意义,除非您进行优化构建。默认设置是为了方便开发人员(快速编译时间、调试符号)而不是运行时性能。 for 循环中的迭代涉及多个函数调用 (
Sequence.makeIterator(), IteratorProtocol.next()),如果它们没有被优化出来(它们在-O中是这样)会减慢速度
标签: swift performance optimization branch-prediction