При запуске цикла for для массива в Swift, работающего на iOS, я бы хотел использовать весь процессор, так как считаю, что это теоретически должно ускорить вычисления.Однако мой результат противоположен, что при выполнении всего на одном DispatchQueue
вместо кратного DispatchQueue
s, на самом деле работает быстрее.Я приведу пример и хотел бы знать, почему однопотоковый подход является более быстрым, и, если возможно, я мог бы еще сократить время, необходимое для расчета, правильно используя несколько ядер ЦП?
Для тех, кто просто хочет увидеть кодмоего намерения может быть пропущено, поскольку в следующем разделе описываются только мои намерения и подход .
Мое намерение в представленном примере:
Я определяю, находится ли линия на карте (след автомобильной поездки, широта и долгота за каждую секунду) в пределах определенного заранее определенного региона, многоугольника на карте (широты и долготы, которые проходят вокруг всего региона),Для этого у меня есть функция, которая вычисляет, если одна точка находится в пределах многоугольника.Я использую цикл for, чтобы перебрать каждое место в отслеживаемой линии автомобильной поездки, и с помощью этой функции проверить, находится ли точка в пределах многоугольника.Если каждое отслеживаемое местоположение находится внутри многоугольника, то вся поездка отслеживаемого автомобиля происходила в указанном регионе.
Я использую iPhone X в целях разработки и для использования всего ЦП, чтобы ускорить этот расчет.
Мой подход:
В представленных примерах у меня есть 3 варианта, которые приводят к следующему времени, необходимому для расчета (в секундах):
Time elapsed for single thread variant: 6.490409970283508 s.
Time elapsed for multi thread v1 variant: 24.076722025871277 s.
Time elapsed for multi thread v2 variant: 23.922222018241882 s.
Первый подход является самым простым, в котором не используются множественные значения DispatchQueue
.
Во втором подходе используется DispatchQueue.concurrentPerform(iterations: Int)
.Мне казалось, что это может быть оптимальным решением для моих нужд, поскольку оно уже реализовано и, похоже, сделано именно для моей цели.
Третий подход - мой, и он планирует примерно равные части массива дляЦиклы for, работающие на DispatchQueue
s, основанные на количестве активных ядер ЦП, сообщаемых ОС.
Я также пробовал варианты, в которых используются параметры inout
(вызов по ссылке), но безрезультатно.,Время осталось прежним, поэтому я не предоставляю больше кода, чтобы загромождать вопрос.
Я также знаю, что могу вернуть функцию, как только найду одну точку не внутри многоугольника, ноэто не является частью этого вопроса.
Мой код:
/**
Function that calculates wether or not a
single coordinate is within a polygon described
as a pointlist.
This function is used by all others to do the work.
*/
private static func contains(coordinate: CLLocationCoordinate2D, with pointList: [CLLocationCoordinate2D]) -> Bool {
var isContained = false
var j = pointList.count - 1
let lat = coordinate.latitude
let lon = coordinate.longitude
for i in 0 ..< pointList.count {
if (pointList[i].latitude > lat) != (pointList[j].latitude > lat) &&
(lon < (pointList[j].longitude - pointList[i].longitude) * (lat - pointList[i].latitude) / (pointList[j].latitude - pointList[i].latitude) + pointList[i].longitude) {
isContained.toggle()
}
j = i
}
return isContained
}
///Runs all three variants as are described in the question
static func testAllVariants(coordinates: [CLLocationCoordinate2D], areInside pointList: [CLLocationCoordinate2D]) {
var startTime = CFAbsoluteTimeGetCurrent()
var bool = contains_singleThread(coordinates: coordinates, with: pointList)
var timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for single thread variant: \(timeElapsed) s.")
startTime = CFAbsoluteTimeGetCurrent()
bool = contains_multiThread_v1(coordinates: coordinates, with: pointList)
timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for multi thread v1 variant: \(timeElapsed) s.")
startTime = CFAbsoluteTimeGetCurrent()
bool = contains_multiThread_v2(coordinates: coordinates, with: pointList)
timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for multi thread v2 variant: \(timeElapsed) s.")
}
private static func contains_singleThread(coordinates: [CLLocationCoordinate2D], with pointList: [CLLocationCoordinate2D]) -> Bool {
var bContainsAllPoints = true
for coordinate in coordinates {
if !contains(coordinate: coordinate, with: pointList) {
bContainsAllPoints = false
}
}
return bContainsAllPoints
}
private static func contains_multiThread_v1(coordinates: [CLLocationCoordinate2D], with pointList: [CLLocationCoordinate2D]) -> Bool {
let numOfCoordinates = coordinates.count
var booleanArray = Array(repeating: true, count: numOfCoordinates)
DispatchQueue.concurrentPerform(iterations: numOfCoordinates) { (index) in
if !contains(coordinate: coordinates[index], with: pointList) {
booleanArray[index] = false
}
}
return !booleanArray.contains(false)
}
private static func contains_multiThread_v2(coordinates: [CLLocationCoordinate2D], with pointList: [CLLocationCoordinate2D]) -> Bool {
let numOfCoordinates = coordinates.count
let coreCount = ProcessInfo().activeProcessorCount
func chunk<T>(array: [T], into size: Int) -> [[T]] {
return stride(from: 0, to: array.count, by: size).map {
Array(array[$0 ..< Swift.min($0 + size, array.count)])
}
}
let segments = chunk(array: coordinates, into: numOfCoordinates/coreCount)
let dg = DispatchGroup()
for i in 0..<segments.count {
dg.enter()
}
var booleanArray = Array(repeating: true, count: segments.count)
for (index, segment) in segments.enumerated() {
DispatchQueue.global(qos: .userInitiated).async {
for coordinate in segment {
if !contains(coordinate: coordinate, with: pointList) {
booleanArray[index] = false
}
}
dg.leave()
}
}
dg.wait()
return !booleanArray.contains(false)
}
Пример данных
Я загрузил дваJSON-файлы для тех, кто хочет иметь данные для запуска тестов.Это тот же самый ввод, который привел к моим зарегистрированным временам.
Отслеживаемая поездка на автомобиле: Ссылка на файл json Регион / область: Ссылка на файл json