Самый быстрый способ создать Swift Array<Float> с фиксированным количеством
Я заметил, что это:
let a = [Float](repeating: 0, count: len)
занимает значительно больше времени, чем просто
let p = UnsafeMutablePointer<Float>.allocate(capacity: len)
Тем не менее, небезопасный указатель не так удобен в использовании, и можно создать Array<Float>
перейти на другой код.
let a = Array(UnsafeBufferPointer(start: p, count: len))
Но это абсолютно убивает, и быстрее создать Array
с заполненными нулями.
Любая идея, как создать Array
быстрее и в то же время Array<Float>
удобно? В контексте моего проекта я, вероятно, могу внутренне разобраться с небезопасным указателем и обернуть его Array
только при необходимости вне модуля.
Быстрый тест на все ответы в этом посте:
let len = 10_000_000
benchmark(title: "array.create", num_trials: 10) {
let a = [Float](repeating: 0, count: len)
}
benchmark(title: "array.create faster", num_trials: 10) {
let p = UnsafeMutableBufferPointer<Float>.allocate(capacity: len)
}
benchmark(title: "Array.reserveCapacity ?", num_trials: 10) {
var a = [Float]()
a.reserveCapacity(len)
}
benchmark(title: "ContiguousArray ?", num_trials: 10) {
let a = ContiguousArray<Float>(repeating: 0, count: len)
}
benchmark(title: "ContiguousArray.reserveCapacity", num_trials: 10) {
var a = ContiguousArray<Float>()
a.reserveCapacity(len)
}
benchmark(title: "UnsafeMutableBufferPointer BaseMath", num_trials: 10) {
let p = UnsafeMutableBufferPointer<Float>(len) // Jeremy's BaseMath
print(p.count)
}
Результаты: (на 10 миллионов плаваний)
array.create: 9.256 ms
array.create быстрее: 0.004 мс
Array.reserveCapacity?: 0,264 мс
ContiguousArray?: 10,154 мс
ContiguousArray.reserveCapacity: 3,251 мс
UnsafeMutableBufferPointer BaseMath: 0,049 мс
Я делаю это adhocly запуска приложения на симуляторе iphone в режиме выпуска. Я знаю, что, вероятно, я должен делать это в командной строке / автономно, но так как я планирую написать это как часть приложения, это может быть хорошо.
Для того, что я пытался сделать, UnsafeMutableBufferPointer выглядел великолепно, но вы должны использовать BaseMath и все его соответствия. Если вам нужен более общий или иной контекст. Обязательно прочитайте все и решите, какой из них вам подходит.
2 ответа
Если вам нужна производительность и вы знаете нужный вам размер, вы можете использовать ReserveCapacity (_:), это будет предварительно выделять память, необходимую для содержимого массива. Согласно документации Apple:
Если вы добавляете в массив известное количество элементов, используйте этот метод, чтобы избежать множественных перераспределений. Этот метод гарантирует, что массив имеет уникальное, изменяемое, непрерывное хранилище с пространством, выделенным как минимум для запрошенного количества элементов.
Вызов метода reserveCapacity(_:) для массива с мостовым хранилищем инициирует копирование в смежное хранилище, даже если в существующем хранилище есть место для хранения элементов minimalCapacity.
По соображениям производительности размер вновь выделенного хранилища может быть больше, чем запрошенная емкость. Используйте свойство емкости массива, чтобы определить размер нового хранилища.
Это самое близкое к тому, что я хочу. Есть библиотека с именем BaseMath (созданная Джереми Ховардом), и есть новый вызов классов AlignedStorage и UnsafeMutableBufferPointer. Он наделен большой математикой и очень быстрым, так что это уменьшает количество управления указателями при манипулировании математическим алгоритмом.
Но это еще предстоит проверить, этот проект очень новый. Я оставлю этот вопрос открытым, чтобы узнать, может ли кто-нибудь предложить что-то лучшее.
Примечание: это самое быстрое в контексте того, что я делаю. Если вам действительно нужен хороший тип значения структуры Array (и варианты), см. Другие ответы.