DSP для отображения ветра
Мы в приложении Windy.app показываем карту скорости ветра. На примере этой задачи рассмотрим как можно на iPhone делать быстрые операции на матрицах.
Скорость ветра удобно представить в виде сетки, в узлах которой записаны значения.
Для сетки с шагом 0.25º данные в виде Float-ов займут 360 / 0.25 * 180 / 0. 25 * 4 = 4 Мб
.
Если подумать то такая высокая точность для представления ветра нам не нужна. И должно было бы точности с размерностью UInt8, тогда общее кол-во данных можно было сократитить до 1 Мб. Для этого мы должны представить матрицу Float-ов в матрицу UInt8, для этого мы найдем min / max и просто равномерно переведем в UInt8. А на клиенте мы захотим перевести данные обратно, про это и будет статья.
Не будем обсуждать как именно мы получили данные, главное что у на клиенте теперь есть массив UInt8 и min
/ max
оригинальных данных.
Чтобы на клиенте получить Float-ы из UInt8 нужно сделать следующие операции:
- посчитать шаг округления
step = (max - min) / 256
- перевести UInt8 → Float
- умножить значения на
step
- прибавить
min
Для примера рассмотрим матрицу 1000 x 1000, это 1млн точек. Наивный алгоритм преобразования будет выглядеть следующим образом:
func floatArray(intValues: [UInt8],
min: Float,
max: Float) -> [Float] {
let step = (max - min) / 256
let result = intValues.map { (v: UInt8) -> Float in
return Float(v) * step + min
}
return result
}
Время выполнения этого кода для матрицы 1000x1000 на iPhone 6s Plus - 160 мс.
Казалось, что можно было сделать это быстрее.
Apple предоставляет несколько механизмов для работы с массивами / матрицами чисел. Один из них - vDSP во фреймворке Accelerate.
Вот код для такого же преобразования, но c использованием библиотеки vDSP:
func floatArray(intValues: [UInt8],
min: Float,
max: Float) -> [Float] {
var step = (max - min) / 256
let count = intValues.count
let stride = vDSP_Stride(1)
let n = vDSP_Length(count)
var fltArray = [Float](repeating: 0, count: count)
vDSP_vfltu8(intValues, stride,
&fltArray, stride,
n)
var mulArray = [Float](repeating: 0, count: count)
vDSP_vsmul(fltArray, stride,
&step,
&mulArray, stride,
n)
var mMin = min
var result = [Float](repeating: 0, count: count)
vDSP_vsadd(mulArray, stride,
&mMin,
&result, stride,
n)
return result
}
Ниже таблица с разницей скорости работы методов
Девайс | Сетка | Array, sec | vDSP, sec | Прирост |
---|---|---|---|---|
iPhone 6 | 1000x1000 | 0.269 | 0.012 | x22.41 |
iPhone 6s plus | 1000x1000 | 0.18 | 0.0099 | x18.18 |
iPhone X | 1000x1000 | 0.202 | 0.0129 | x15.65 |
iPhone 6 | 2000x2000 | 1.014 | 0.051 | x19.88 |
iPhone 6s plus | 2000x2000 | 0.705 | 0.0442 | x15.95 |
iPhone X | 2000x2000 | 0.789 | 0.0473 | x16.68 |
Refs
Документция Apple по vDSP
Кейс для обработки сигналов для BLE