Почему first.split(разделитель: » «).map { Int(строка($ 0)))!} лучше, чем first.split(разделитель: » «).map { Int($0)!}?

#string #performance #integer #swift5

#строка #Производительность #целое #swift5

Вопрос:

 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 => Int($ 0)!

производительность = 0.000843048095703125 => Int(строка($ 0))!

Я использовал map для преобразования в Int. Но это заняло меньше времени, когда я преобразовал его в «Int(String ($ 0))!». Интересно, почему.

Ответ №1:

split Возвращает не строки, а подстроки, поэтому Int($0) исполнение преобразует подстроки в целые числа и Int(String($0)) преобразует подстроку в строку, а затем в целое число.

Глядя на разборку для этих двух версий, они вызывают разные Int.init? методы. Но в сборке многое происходит, поэтому немного сложно сказать, единственное ли это отличие или некоторые другие вещи, происходящие в фоновом режиме, также могут оказать некоторое скромное влияние.

Но можно с уверенностью сказать, что преобразование String в Int более эффективно, чем работа с подстроками. Как уже было сказано, я бы придерживался более чистого кода и не беспокоился об этой незначительной разнице, если только эта разница, измеряемая в десятитысячные доли секунды, не будет существенной (т. Е. Вы делаете это миллионы раз, что в другом месте кода нет других вещей, которые могли бы иметь больше возможностей для оптимизации материала и т.д.). Как сказал Дональд Кнут:

Мы должны забыть о небольшой эффективности, скажем, в 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 Исполнение было в два раза быстрее:

введите описание изображения здесь