У меня есть настройка модульного теста, чтобы доказать, что одновременное выполнение нескольких тяжелых задач быстрее, чем последовательное.
Теперь ... прежде чем все здесь сойдут с ума из-за того факта, что вышеприведенное утверждение не всегда верно, потому что многопоточность сопряжена со многими неопределенностями, позвольте мне объяснить.
Из прочтения документации Apple я знаю, что вы не можете гарантировать получение нескольких потоков при их запросе.ОС (iOS) назначит потоки так, как считает нужным.Например, если устройство имеет только одно ядро, оно назначит одно ядро, и последовательный интерфейс будет немного быстрее из-за того, что код инициализации параллельной работы займет некоторое дополнительное время, но не обеспечит повышение производительности, поскольку устройство имеет только одно ядро.
Однако: эта разница должна быть незначительной.Но в моей настройке POC разница огромна.В моем POC, одновременный медленнее примерно в 1/3 времени.
Если последовательное выполнение завершается через 6 секунд , одновременное выполнение завершается через 9 секунд .
Эта тенденция продолжается даже при более высоких нагрузках.если сериал завершается за 125 секунд , одновременный будет конкурировать за 215 секунд .Это также происходит не один раз, а каждый раз.
Интересно, допустил ли я ошибку при создании этого POC, и если да, то как мне доказать, что одновременное выполнение нескольких тяжелых задач действительно быстрее, чем последовательное?
Мой POC в быстрых юнит-тестах:
func performHeavyTask(_ completion: (() -> Void)?) {
var counter = 0
while counter < 50000 {
print(counter)
counter = counter.advanced(by: 1)
}
completion?()
}
// MARK: - Serial
func testSerial () {
let start = DispatchTime.now()
let _ = DispatchQueue.global(qos: .userInitiated)
let mainDPG = DispatchGroup()
mainDPG.enter()
DispatchQueue.global(qos: .userInitiated).async {[weak self] in
guard let self = self else { return }
for _ in 0...10 {
self.performHeavyTask(nil)
}
mainDPG.leave()
}
mainDPG.wait()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
print("NanoTime: \(nanoTime / 1_000_000_000)")
}
// MARK: - Concurrent
func testConcurrent() {
let start = DispatchTime.now()
let _ = DispatchQueue.global(qos: .userInitiated)
let mainDPG = DispatchGroup()
mainDPG.enter()
DispatchQueue.global(qos: .userInitiated).async {
let dispatchGroup = DispatchGroup()
let _ = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.concurrentPerform(iterations: 10) { index in
dispatchGroup.enter()
self.performHeavyTask({
dispatchGroup.leave()
})
}
dispatchGroup.wait()
mainDPG.leave()
}
mainDPG.wait()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
print("NanoTime: \(nanoTime / 1_000_000_000)")
}
Подробности:
ОС: macOS High Sierra
Название модели: MacBook Pro
Идентификатор модели: MacBookPro11,4
Имя процессора: Intel Core i7
Скорость процессора: 2,2 ГГц
Количество процессоров: 1
Общее количество ядер: 4
Обатесты проводились на симуляторе iPhone XS Max.Оба теста были выполнены сразу после перезагрузки всего Mac (чтобы избежать того, что Mac занят приложениями, отличными от запуска этого модульного теста, размытие результатов)
Кроме того, оба модульных теста заключены в асинхронный DispatcherWorkItemпоскольку тестовый сценарий предназначен для того, чтобы основная (UI) очередь не блокировалась, предотвращается преимущество последовательного тестового сценария в этой части, поскольку он использует основную очередь вместо фоновой очереди, как это делает параллельный тестовый случай.
Я также приму ответ, который показывает POC, надежно проверяющий это.Он не обязательно должен показывать, что параллельный всегда быстрее, чем последовательный (см. Выше объяснение, почему нет).Но хотя бы какое-то время