Если вы профилируете (Cmd + I
) свой код, вы увидите, что большую часть времени он занимает различные функции «копировать в буфер». Это происходит, когда вы добавляете новый элемент в массив, но для него закончилось исходное выделенное пространство, поэтому его необходимо переместить в место в куче с большим объемом памяти. Мораль урока: выделение кучи происходит медленно, но неизбежно с массивами. Делайте это как можно меньше раз.
Попробуй это:
func convertWordToBytes2(fullW: [UInt32]) -> [[UInt8]] {
let subSize = 6
// We allocate the array only once per run since allocation is so slow
// There will only be assignment to it after
var combined48 = [UInt8](count: fullW.count * 4, repeatedValue: 0).splitBy(subSize)
var row = 0
var col = 0
for i in 0...16 {
for j in 24.stride(through: 0, by: -8) {
let value = UInt8(truncatingBitPattern: fullW[i] >> UInt32(j))
combined48[row][col] = value
col += 1
if col >= subSize {
row += 1
col = 0
}
}
}
return combined48
}
Код эталона:
let testCases = (0..<1_000_000).map { _ in
(0..<17).map { _ in arc4random() }
}
testCases.forEach {
convertWordToBytes($0)
convertWordToBytes2($0)
}
Результат (на моем iMac 2012 года)
Weight Self Weight Symbol Name
9.35 s 53.2% 412.00 ms specialized convertWordToBytes([UInt32]) -> [[UInt8]]
3.28 s 18.6% 344.00 ms specialized convertWordToBytes2([UInt32]) -> [[UInt8]]
Устранив множественные выделения, мы уже сократили время выполнения на 60%. Но каждый тестовый случай является независимым, что идеально подходит для параллельной обработки с помощью современных многоядерных процессоров. Модифицированный цикл...:
dispatch_apply(testCases.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { i in
convertWordToBytes2(testCases[i])
}
... сократит время стены примерно на 1 секунду при выполнении на моем четырехъядерном процессоре i7 с 8 потоками:
Weight Self Weight Symbol Name
2.28 s 6.4% 0 s _dispatch_worker_thread3 0x58467
2.24 s 6.3% 0 s _dispatch_worker_thread3 0x58463
2.22 s 6.2% 0 s _dispatch_worker_thread3 0x58464
2.21 s 6.2% 0 s _dispatch_worker_thread3 0x58466
2.21 s 6.2% 0 s _dispatch_worker_thread3 0x58465
2.21 s 6.2% 0 s _dispatch_worker_thread3 0x58461
2.18 s 6.1% 0 s _dispatch_worker_thread3 0x58462
Экономия времени не так велика, как я надеялся. По-видимому, при доступе к куче памяти возникает конфликт. Для чего-то еще более быстрого вам следует изучить решение на основе C.
person
Code Different
schedule
30.10.2016