gpt4 book ai didi

swift - 在哪个循环中 `for`或 `for-in`更快?为什么?

转载 作者:可可西里 更新时间:2023-10-31 23:54:15 24 4
gpt4 key购买 nike

当必须非常了解在大型数组上进行迭代所需的时间时,应该使用哪个循环。

最佳答案

简短答案
不要像这样进行微优化-您在循环内执行操作的速度可能会远远超过任何差异。如果您确实认为此循环是性能瓶颈,那么使用accelerate framework之类的方法可能会更好地为您服务-但前提是剖析显示您的努力确实值得。
而且不要与语言作斗争。除非不能用for…in表示想要实现的目标,否则请使用for…in。这些情况很少见。 for…in的好处在于,很难将其弄错。那更重要。优先考虑正确性而不是速度。清晰度很重要。您甚至可能希望完全跳过for循环并使用mapreduce
更长的答案
对于数组,如果您在没有最快的编译器优化的情况下尝试使用它们,则它们的性能相同,因为它们本质上是相同的。
大概您的for ;;循环如下所示:

var sum = 0
for var i = 0; i < a.count; ++i {
sum += a[i]
}
和你的 for…in循环像这样:
for x in a {
sum += x
}
让我们重写 for…in以显示幕后的真实情况:
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()
}
这是 IndexingGenerator<[Int]>的实现可能如下所示:
struct IndexingGeneratorArrayOfInt {
private let _seq: [Int]
var _idx: Int = 0

init(_ seq: [Int]) {
_seq = seq
}

mutating func generate() -> Int? {
if _idx != _seq.endIndex {
return _seq[_idx++]
}
else {
return nil
}
}
}
哇,好多代码,肯定比常规的 for ;;循环慢!
不。因为尽管这可能是逻辑上的工作,但是编译器有很大的自由度可以优化。例如,请注意 IndexingGeneratorArrayOfIntstruct而不是 class。这意味着直接声明两个成员变量没有开销。这也意味着编译器可能可以内联 generate中的代码–这里没有间接操作,没有重载的方法和vtables或objc_MsgSend。只是一些简单的指针算法和延迟。如果剥离结构和方法调用的所有语法,您会发现 for…in代码的最终结果与 for ;;循环的操作几乎完全相同。 for…in有助于避免性能错误
另一方面,如果对于开头给出的代码,将编译器优化切换到更快的设置,则 for…in似乎使 for ;;消失了。在一些非科学测试中,我使用 XCTestCase.measureBlock进行了运算,将大量随机数相加,结果快了一个数量级。
为什么?由于使用了 count:
for var i = 0; i < a.count; ++i {
// ^-- calling a.count every time...
sum += a[i]
}
也许优化程序可以为您解决此问题,但在这种情况下,还没有解决。如果将不变式拉出,就速度而言,它可以与 for…in相同:
let count = a.count
for var i = 0; i < count; ++i {
sum += a[i]
}
“哦,我绝对会每次都这样做,所以没关系”。我要说的真的吗?你确定吗?打赌有时会忘记。
但是您想要更好的消息吗?用 reduce进行相同的求和(在我的测试中,再一次不是很科学),甚至比for循环还快:
let sum = a.reduce(0,+)
但是它也更具表达性和可读性(IMO),并且允许您使用 let声明结果。鉴于无论如何这都是您的主要目标,因此速度是一个额外的好处。但是希望无论如何,这种性能都会激励您去做。
这仅用于数组,但是其他集合呢?当然,这取决于实现方式,但是有充分的理由相信,对于其他集合(如字典,自定义用户定义的集合)来说,这样做会更快。
我这样做的原因是,集合的作者可以实现 generate的优化版本,因为他们确切知道集合的使用方式。假设下标查找涉及一些计算(例如,对于数组,则使用指针算法-您必须将索引值乘以值大小,然后再将其添加到基本指针中)。对于generate,您知道要执行的操作是依次遍历该集合,因此您可以对此进行优化(例如,对于数组,请保持指向下一个元素的指针,该指针在每次 next时都会增加叫做)。 reducemap的特殊成员版本也是如此。
这甚至可能就是 reduce在数组上表现如此出色的原因-谁知道(如果您想尝试找出问题,可以在传入的函数上添加断点)。但这只是使用您可能应该使用的语言构造的另一种理由。

关于swift - 在哪个循环中 `for`或 `for-in`更快?为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27876623/

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