为什么first.splitseparator:“”.map {IntString$ 0!}优于first.splitseparator:“”.map {Int$ 0!}?

问题描述

import Foundation

let first = readLine()! // input : 10 20 30

processTime {
    first.split(separator: " ").map { Int($0)! }
}
processTime {
    first.split(separator: " ").map { Int(String($0))! }
}

func processTime(blockFunction: () -> ()) {
    let startTime = CFAbsoluteTimeGetCurrent()
    blockFunction()
    let processTime = CFAbsoluteTimeGetCurrent() - startTime
    print("performance = \(processTime)")
}

性能= 0.0012700557708740234 =>整数($ 0)!

performance = 0.000843048095703125 => Int(String($ 0))!

我使用map转换为Int。但是,将它转换为“ Int(String($ 0))!”所花的时间更少。我不知道为什么。

解决方法

split不返回字符串,而是子字符串,因此Int($0)表示将子字符串转换为整数,而Int(String($0))则将子字符串转换为字符串,然后转换为整数

查看这两种格式的反汇编,它们正在调用不同的Int.init?方法。但是组装中有很多事情要做,所以很难说这是否是唯一的区别,或者在后台进行的其他一些事情是否也会产生适度的影响。

但是,可以肯定地说,将String转换为Int比处理子字符串更有效。话虽这么说,我会坚持使用更简洁的代码,而不用担心这种微不足道的差异,除非这种差异(以千分之一秒为单位)将变得很重要(即,您正在执行数百万次,不是代码中其他地方可能具有更多实质性优化机会的其他内容等)。正如Donald Knuth所说:

我们应该忘记效率低下的问题,例如大约97%的时间:过早的优化是万恶之源。然而,我们不应该放弃我们那关键的3%的机会。


顺便说一句,在衡量效果时,而不是衡量单个呼叫时,要进行数百万次,并观察它们之间的比较。另外,在测试性能时,您可能还希望随机化测试顺序,重复执行几次,以确保执行顺序不会影响性能。最后,请确保您正在测试优化的发行版而不是调试版。

此外,单元测试还有一种measure方法来比较性能,重复测试十次:

class ConversionBenchmarkTests: XCTestCase {    
    let input = "10 20 30"
    let count = 100_000

    func testInt() throws {
        measure {
            for _ in 0 ..< count {
                let result = input.split(separator: " ").map { Int($0)! }
                XCTAssertEqual(result.count,3)
            }
        }
    }

    func testString() throws {
        measure {
            for _ in 0 ..< count {
                let result = input.split(separator: " ").map { Int(String($0))! }
                XCTAssertEqual(result.count,3)
            }
        }
    }
}

String再现速度快一倍:

enter image description here

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...