Самый быстрый способ создать 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 (и варианты), см. Другие ответы.

Другие вопросы по тегам