【问题标题】:Performance issue when using for-in loops with ranges使用带范围的 for-in 循环时的性能问题
【发布时间】:2020-10-11 18:56:32
【问题描述】:

Swift 提供了一种 for-in 风格的 for 循环来替代 C 风格的 for 循环。我们也可以在RangeClosedRange 中使用这种for-in 循环。

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

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

我用XCTestmeasure测试了三种循环:

  • for-in 循环与ClosedRange
  • for-in 循环与Range
  • while 循环
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
                }
            }
        }
    }
}

从示例中,Range 的 for-in 循环比 ClosedRange 的 for-in 循环快 2 倍。并且while循环比它们都快很多。

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

【问题讨论】:

    标签: swift


    【解决方案1】:

    您的问题仅出现在 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
    

    【讨论】:

    • 谢谢。实际上,我正在 leetcode.com 上学习算法。经过一番摸索,很确定问题是他们的执行环境造成的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2011-10-21
    • 2018-05-11
    • 1970-01-01
    相关资源
    最近更新 更多