首页 > 解决方案 > 使用带范围的 for-in 循环时的性能问题

问题描述

Swift 提供了一个 for-in 风格的 for 循环来替代 C 风格的 for 循环。我们也可以将这种 for-in 循环与Rangeand一起使用ClosedRange

for i in 0..<100 {
  // do something.
}

但是,在使用带范围的 for-in 循环时会出现一些性能问题,尤其是当存在嵌套的 for 循环时。

我使用 XCTestmeasure来测试三种循环:

final class LeetcodeSolutionsTests: XCTestCase {
    
    var count: Int = 1000
    
    func testForInClosedRange() {
        // Time: 1.016 sec
        measure {
            for i in 1...count {
                for j in 1...count {
                    let _ = i + j
                }
            }
        }
    }
    
    func testForInRange() {
        // Time: 0.542 sec
        measure {
            for i in 1...count {
                for j in 1..<(count + 1) {
                    let _ = i + j
                }
            }
        }
    }
    
    func testWhile() {
        // Time: 0.015 sec
        measure {
            for i in 1...count {
                var j = 1
                while j < count + 1 {
                    let _ = i + j
                    j += 1
                }
            }
        }
    }
}

从示例中,for-in 循环 withRange比 for-in 循环 with 快 2 倍ClosedRange。而while循环比它们都快得多。

我猜 for-in 循环会做一些额外的工作,而且会有一些细微的性能差异是有道理的。我没想到的是性能差异如此之大。

标签: swift

解决方案


您的问题仅出现在 Debug 配置中,因为编译器会生成大量访问检查代码。

如果你

  • 以使用变量的方式修改代码(以防止编译器丢弃所有未使用的代码)
  • 然后在发布模式下运行它

对于每种循环,您都会得到完全相同的结果:

func measure(txt:String = #function, c:()->()) {
    let start=Date()
    c()
    let end = Date()
    let duration = end.timeIntervalSinceReferenceDate-start.timeIntervalSinceReferenceDate
    print ("\(txt) - Duration: \(duration)")
}

final class LeetcodeSolutionsTests {
    
    var count: Int = 1000
    
    func testForInClosedRange() {
        // Time: 0.001672 sec
        var result:Int = 0
        measure {
            for i in 1...count {
                for j in 1...count {
                    result += i + j
                }
            }
        }
        print (result)
    }

    func testForInRange() {
        // Time: 0.001671
        var result:Int = 0
        measure {
            for i in 1...count {
                for j in 1..<(count + 1) {
                    result += i + j
                }
            }
        }
        print (result)
    }
    
    func testWhile() {
        // Time: 0.001670 sec
        var result:Int = 0
        measure {
            for i in 1...count {
                var j = 1
                while j < count + 1 {
                    result += i + j
                    j += 1
                }
            }
        }
        print (result)
    }
}

let t = LeetcodeSolutionsTests()
t.testForInClosedRange()
t.testForInRange()
t.testWhile()

然后给你:

testForInClosedRange() - Duration: 0.0016720294952392578
1001000000
testForInRange() - Duration: 0.0016709566116333008
1001000000
testWhile() - Duration: 0.0016709566116333008
1001000000

推荐阅读