为什么 first.split(separator: " ").map { Int(String($0))!} 比 first.split(separator: " ").map { Int($0)!} 好?
Why is first.split(separator: " ").map { Int(String($0))!} better than first.split(separator: " ").map { Int($0)!}?
import Foundation
let first = readLine()! // input : 10 20 30
processTime {
first.split(separator: " ").map { Int([=12=])! }
}
processTime {
first.split(separator: " ").map { Int(String([=12=]))! }
}
func processTime(blockFunction: () -> ()) {
let startTime = CFAbsoluteTimeGetCurrent()
blockFunction()
let processTime = CFAbsoluteTimeGetCurrent() - startTime
print("performance = \(processTime)")
}
性能 = 0.0012700557708740234 => Int($0)!
性能=0.000843048095703125 => Int(String($0))!
我是用map转Int的。但是当我将它转换为“Int(String($0))!”时花费的时间更少。我想知道为什么。
split
返回的不是字符串,而是子串,所以Int([=12=])
是把子串转成整数,Int(String([=13=]))
是把子串转成字符串,再转成一个整数。
查看这两个演绎版的反汇编,它们调用了不同的 Int.init?
方法。但是装配中发生了很多事情,所以很难说这是否是唯一的区别,或者在后台发生的其他一些事情是否也会产生一些适度的影响。
但是,可以肯定地说,将 String
转换为 Int
比处理子字符串更有效。话虽这么说,我会坚持使用更简洁的代码,而不用担心这种微不足道的差异,除非这种差异(以千分之十秒为单位)将是 material(即你正在做数百万次,代码中其他地方没有其他东西可能有更多 material 优化机会等)。正如 Donald Knuth 所说:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 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([=10=])! }
XCTAssertEqual(result.count, 3)
}
}
}
func testString() throws {
measure {
for _ in 0 ..< count {
let result = input.split(separator: " ").map { Int(String([=10=]))! }
XCTAssertEqual(result.count, 3)
}
}
}
}
String
再现速度是原来的两倍:
import Foundation
let first = readLine()! // input : 10 20 30
processTime {
first.split(separator: " ").map { Int([=12=])! }
}
processTime {
first.split(separator: " ").map { Int(String([=12=]))! }
}
func processTime(blockFunction: () -> ()) {
let startTime = CFAbsoluteTimeGetCurrent()
blockFunction()
let processTime = CFAbsoluteTimeGetCurrent() - startTime
print("performance = \(processTime)")
}
性能 = 0.0012700557708740234 => Int($0)!
性能=0.000843048095703125 => Int(String($0))!
我是用map转Int的。但是当我将它转换为“Int(String($0))!”时花费的时间更少。我想知道为什么。
split
返回的不是字符串,而是子串,所以Int([=12=])
是把子串转成整数,Int(String([=13=]))
是把子串转成字符串,再转成一个整数。
查看这两个演绎版的反汇编,它们调用了不同的 Int.init?
方法。但是装配中发生了很多事情,所以很难说这是否是唯一的区别,或者在后台发生的其他一些事情是否也会产生一些适度的影响。
但是,可以肯定地说,将 String
转换为 Int
比处理子字符串更有效。话虽这么说,我会坚持使用更简洁的代码,而不用担心这种微不足道的差异,除非这种差异(以千分之十秒为单位)将是 material(即你正在做数百万次,代码中其他地方没有其他东西可能有更多 material 优化机会等)。正如 Donald Knuth 所说:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 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([=10=])! }
XCTAssertEqual(result.count, 3)
}
}
}
func testString() throws {
measure {
for _ in 0 ..< count {
let result = input.split(separator: " ").map { Int(String([=10=]))! }
XCTAssertEqual(result.count, 3)
}
}
}
}
String
再现速度是原来的两倍: