在swift中,哪个循环更快?为什么?

在swift中,哪个循环更快?为什么?,swift,Swift,当我必须非常清楚遍历大型数组所需的时间时,我应该使用哪个循环。for(带计数器)只是递增一个计数器。非常快。中的for使用迭代器(调用对象以传递下一个元素)。这要慢得多。但最后,您希望在这两种情况下都能访问该元素,而这两种情况最终不会产生任何影响。有句名言:“我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源”Donald Knuth。您似乎不太可能在%3中 专注于手头的更大问题。在它工作之后,如果它需要性能提升,那么就要担心for循环。但我向你保证,最终,更大的结构低效或糟糕的算法选择

当我必须非常清楚遍历大型数组所需的时间时,我应该使用哪个循环。

for(带计数器)只是递增一个计数器。非常快。中的for使用迭代器(调用对象以传递下一个元素)。这要慢得多。但最后,您希望在这两种情况下都能访问该元素,而这两种情况最终不会产生任何影响。

有句名言:“我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源”Donald Knuth。您似乎不太可能在%3中

专注于手头的更大问题。在它工作之后,如果它需要性能提升,那么就要担心
for
循环。但我向你保证,最终,更大的结构低效或糟糕的算法选择将是性能问题,而不是
for
循环

为循环担心
,真是太令人担忧了。

简短回答 不要像这样进行微优化——任何差异都可能被您在循环中执行的操作的速度远远抵消。如果您真的认为这个循环是一个性能瓶颈,那么使用类似于的东西可能会更好——但前提是分析表明您的努力确实值得

不要与语言对抗。使用
for…in
除非您想要实现的目标无法用
for…in
表达。这些病例很少见。在
中,
对于……的好处是,很难出错。这更重要。优先考虑正确性而不是速度。清晰是很重要的。您甚至可能希望完全跳过for循环,使用
map
reduce

更长的答案 对于数组,如果您在没有最快的编译器优化的情况下尝试它们,它们的性能是相同的,因为它们基本上做相同的事情

大概是您的
for循环如下所示:

var sum = 0
for var i = 0; i < a.count; ++i {
    sum += a[i]
}
for x in a {
    sum += x
}
让我们在
中为…重写
,以显示封面下的真实情况:

var g = a.generate()
while let x = g.next() {
    sum += x
}
然后,让我们重写
a.generate()
返回的内容,以及类似
let
正在做的事情:

 var g = IndexingGenerator<[Int]>(a)
 var wrapped_x = g.next()
 while wrapped_x != nil {
     let x = wrapped_x!
     sum += x
     wrapped_x = g.next()
 }
哇,代码太多了,它的执行速度肯定比常规的
for循环

没有。因为虽然这可能是它在逻辑上所做的,但编译器有很多空间进行优化。例如,请注意,
indexingGeneratorArrayFint
是一个
struct
而不是
类。这意味着它没有直接声明两个成员变量的开销。这也意味着编译器可以将代码内联到
generate
——这里没有进行间接寻址,没有重载的方法和vtables或objc_MsgSend。只是一些简单的指针算法和延迟。如果去掉结构和方法调用的所有语法,您会发现
代码中的
for…最终与
for;;几乎完全相同循环正在执行

for…in
有助于避免性能错误 另一方面,如果对于开头给出的代码,您将编译器优化切换到更快的设置,
for…in
中的
for离开。在一些非科学测试中,我使用XCTestCase.measureBlock对大量随机数进行求和,速度快了一个数量级

为什么??由于使用了
计数

for var i = 0; i < a.count; ++i {
                // ^-- calling a.count every time...
    sum += a[i]
}
“哦,我肯定每次都会这么做,所以没关系。”。我是说,真的吗?你确定吗?我打赌你有时会忘记

但是你想要更好的消息吗?使用
reduce
进行相同的求和(在我的测试中,同样不是很科学)甚至比for循环更快:

let sum = a.reduce(0,+)
但它也更具表达性和可读性(IMO),允许您使用
let
声明结果。考虑到这应该是你的首要目标,速度是一个额外的奖励。但希望这场演出能激励你不顾一切地去做

这只是针对数组,但其他集合呢?当然,这取决于实现,但有充分的理由相信,对于其他集合(如字典、自定义用户定义的集合),它会更快

我这样做的原因是,集合的作者可以实现一个优化版本的
generate
,因为他们确切地知道集合是如何使用的。假设下标查找涉及一些计算(例如数组中的指针算法-必须将索引乘以值大小,然后将其添加到基指针)。在generate的情况下,您知道要做的是按顺序遍历集合,因此可以对此进行优化(例如,在数组的情况下,在每次调用
next
时,保持一个指向下一个元素的指针,该指针递增)。这同样适用于
reduce
map
的专门成员版本


这甚至可能是
reduce
在数组上表现如此出色的原因——谁知道呢(如果您想尝试找出答案,可以在传入的函数上粘贴一个断点)。但这只是使用您应该使用的语言构造的另一个理由。

FWIW,一个基本的游乐场测试显示map()比枚举快10倍左右:

class SomeClass1 {
  let value: UInt32 = arc4random_uniform(100)
}
class SomeClass2 {
  let value: UInt32
  init(value: UInt32) {
    self.value = value
  }
}
var someClass1s = [SomeClass1]()
for _ in 0..<1000 {
  someClass1s.append(SomeClass1())
}

var someClass2s = [SomeClass2]()
let startTimeInterval1 = CFAbsoluteTimeGetCurrent()
someClass1s.map { someClass2s.append(SomeClass2(value: $0.value)) }
println("Time1: \(CFAbsoluteTimeGetCurrent() - startTimeInterval1)") // "Time1: 0.489435970783234"
var someMoreClass2s = [SomeClass2]()
let startTimeInterval2 = CFAbsoluteTimeGetCurrent()
for item in someClass1s { someMoreClass2s.append(SomeClass2(value: item.value)) }
println("Time2: \(CFAbsoluteTimeGetCurrent() - startTimeInterval2)") // "Time2 : 4.81457495689392"
class SomeClass1{
let值:UInt32=arc4random_均匀(100)
}
第二类{
let值:UInt32
初始值(值:UInt32){
自我价值=价值
}
}
var someClass1s=[SomeClass1]()

对于uu0..你试过测量它吗?就像这样回答:@MartinR不,不太可能。如果你知道,请写下答案。我一定会尝试去衡量它。我获得奖项有那么容易吗?1我没有否决投票,
let sum = a.reduce(0,+)
class SomeClass1 {
  let value: UInt32 = arc4random_uniform(100)
}
class SomeClass2 {
  let value: UInt32
  init(value: UInt32) {
    self.value = value
  }
}
var someClass1s = [SomeClass1]()
for _ in 0..<1000 {
  someClass1s.append(SomeClass1())
}

var someClass2s = [SomeClass2]()
let startTimeInterval1 = CFAbsoluteTimeGetCurrent()
someClass1s.map { someClass2s.append(SomeClass2(value: $0.value)) }
println("Time1: \(CFAbsoluteTimeGetCurrent() - startTimeInterval1)") // "Time1: 0.489435970783234"
var someMoreClass2s = [SomeClass2]()
let startTimeInterval2 = CFAbsoluteTimeGetCurrent()
for item in someClass1s { someMoreClass2s.append(SomeClass2(value: item.value)) }
println("Time2: \(CFAbsoluteTimeGetCurrent() - startTimeInterval2)") // "Time2 : 4.81457495689392"