Инициализация массива переменного значения


Моя цель в этом проекте-сделать то, что может—, как, быстро, эффективно, и эффективно, как Array(repeating: , count: )инициализировать Array переменного значения.

Примечание: Я использую Свифт 4.1 и Xcode 9.3


Моя Инициализация Массива:

extension Array {
    init (repeatingValues arr: Array, count: Int) {
        precondition(!arr.isEmpty, "Initialization values cannot be empty")
        precondition(count > 0, "Count cannot be less than 1")
        var newArr = Array<Element>()
        for i in 0..<count {
            newArr.append(arr[i % arr.count])
        }
        self = newArr
    }
}

Использование

Инициализ init(repeatingValues: , count: ):

let array = Array(repeatingValues: [true, false], count: 10)

print(array) //prints: [true, false, true, false, true, false, true, false, true, false]

Эталоном Сравнения

Сравнение: Array(repeating: , count: ) и Array(repeatingValues: , count: )

Ориентир Расширение Времени:

extension Date {
      func elapsedTime(to date: Date) -> String {

        let attoseconds100 = date.timeIntervalSince(self) * 10000000000000

        switch attoseconds100 {
        case 6048000000000000000...:
            let weeks : Int = Int(attoseconds100 / 6048000000000000000)
            return "\(weeks)w" + " " + "\(Int(attoseconds100 / 864000000000000000) - (weeks * 7))d"

        case 864000000000000000...:
            let days : Int = Int(attoseconds100 / 864000000000000000)
            return "\(days)d" + " " + "\(Int(attoseconds100 / 36000000000000000) - (days * 24))h"

        case 36000000000000000...:
            let hours : Int = Int(attoseconds100 / 36000000000000000)
            return "\(hours)h" + " " + "\(Int(attoseconds100 / 600000000000000) - (hours * 60))m"

        case 600000000000000...:
            let mins : Int = Int(attoseconds100 / 600000000000000)
            return "\(mins)m" + " " + "\(Int(attoseconds100 / 10000000000000) - (mins * 60))s"

        case 10000000000000...:
            let secs : Int = Int(attoseconds100 / 10000000000000)
            return "\(secs)s" + " " + "\(Int(attoseconds100 / 10000000000) - (secs * 1000))ms"

        case 10000000000...:
            let millisecs : Int = Int(attoseconds100 / 10000000000)
            return "\(millisecs)ms" + " " + "\(Int(attoseconds100 / 10000000) - (millisecs * 1000))μs"

        case 10000000...:
            let microsecs : Int = Int(attoseconds100 / 10000000)
            return "\(microsecs)μs" + " " + "\(Int(attoseconds100 / 10000) - (microsecs * 1000))ns"

        case 10000...:
            let nanosecs : Int = Int(attoseconds100 / 10000)
            return "\(nanosecs)ns" + " " + "\(Int(attoseconds100 / 10) - (nanosecs * 1000))ps"

        case 10...:
            let picosecs : Int = Int(attoseconds100 / 10)
            return "\(picosecs)ps" + " " + "\(Int(attoseconds100 / 0.01) - (picosecs * 1000))fs"

        case 0.01...:
            let femtosecs : Int = Int(attoseconds100 * 100)
            return "\(femtosecs)fs" + " " + "\((Int(attoseconds100 / 0.001) - (femtosecs * 10)) * 100)as"
        case 0.001...:
            return "\(Int(attoseconds100 * 100000))as"

        default:
            return "Less than 100 attoseconds"
        }
    }
}

Array(repeating: , count: ):

let start = Date()
let _ = Array(repeating: true, count: 1000000)
let end = Date()
print(start.elapsedTime(to: end)) //2ms 470μs

Время выполнения: 2МС 470µs

Array(repeatingValues: , count: ):

let start = Date()
let _ = Array(repeatingValues: [true, false], count: 1000000)
let end = Date()
print(start.elapsedTime(to: end)) //472ms 555μs

Время выполнения: 472ms 555µs

Результаты:

Array(repeatingValues: , count: ) 191.3 х медленнее, чем Array(repeating: , count: )


Как я могу сравнить эффективность и скорость от Apple?



Комментарии
2 ответа

На моем компьютере (1,2 ГГц процессор Core М5 Макбук) я измерил

Array(repeating:count:)        663μs 995ns
Array(repeatingValues:count:) 13ms 105μs

С код, скомпилированный в конфигурации Release, то есть с
оптимизаций. Это примерно в 20 раз между этими методами.

Похоже, что одним “виновником” является расчет остатка в

        newArr.append(arr[i % arr.count])

Если мы заменяем, что в дополнение с наматыванием теста

public init(repeatingValues arr: Array, count: Int) {
precondition(!arr.isEmpty, "Initialization values cannot be empty")
precondition(count > 0, "Count cannot be less than 1")

var newArr = Array<Element>()
var srcIndex = 0
for _ in 0..<count {
newArr.append(arr[srcIndex])
srcIndex += 1
if srcIndex == arr.count { srcIndex = 0 }
}
self = newArr
}

тогда производительность повышается в

Array(repeatingValues:count:)   3ms 315μs

который является “только” на 5 медленнее, чем Array(repeating:count).

Другая возможная проблема: границы массива проверить на каждом обращении.
Это может быть вызвано и как элемент хранения напрямую:

init (repeatingValues2 arr: Array, count: Int) {
precondition(!arr.isEmpty, "Initialization values cannot be empty")
precondition(count > 0, "Count cannot be less than 1")
var newArr = Array(repeating: arr.first!, count: count)
newArr.withUnsafeMutableBufferPointer { src in
arr.withUnsafeBufferPointer { dest in
var j = 0
for i in 0..<count {
src[i] = dest[j]
j += 1
if j == arr.count { j = 0 }
}
}
}
self = newArr
}

Это сокращает время выполнения

Array(repeatingValues:count:)   1ms 889μs

который сейчас медленнее примерно в 3, чем Array(repeating:count).

Другим, возможно, полезно, чтобы избежать перераспределения из
элемент массива хранения по телефону

    newArr.reserveCapacity(count)

Однако, это не сделало значительную разницу в моих тестах.

Еще одно замечание: требование графа назначения должны быть строго
позитивные кажется ненужные ограничения. Я хотел бы изменить это

init (repeatingValues arr: Array, count: Int) {
precondition(!arr.isEmpty, "Initialization values cannot be empty")
precondition(count >= 0, "Count cannot be negative")
if count == 0 {
self = []
return
}

// ...
}

4
ответ дан 16 апреля 2018 в 02:04 Источник Поделиться

Я бы принять совершенно иной подход от этого.

Я бы определил CycleSequenceи сопровождающих CycleIterator, который имеет несколько ключевых преимуществ:


  • Пользователи имеют свободу итерации CycleSequenceбез принуждения, чтобы преобразовать его в Array.


    • Конечно, если кто-то хочет хранить этот массив, они могут позвонить Array(CycleSequence(cycling: whatever).prefix(someLength)). В prefix(someLength) часть имеет решающее значение, в противном случае Array инициализатор будет бесконечный цикл, пытаются сделать копию бесконечно долго, никогда не заканчивается последовательность циклически элементы.


  • Это более эффективно памяти, потому что повторяющиеся элементы массива сохраняются только один раз.

  • Элементы могут быть взяты из любого Collection тип. Массивы, диапазоны, строки, гораздо более гибким!

Пример реализации:

struct CycleSequence<C: Collection>: Sequence {
let cycledElements: C

init(cycling cycledElements: C) {
self.cycledElements = cycledElements
}

public func makeIterator() -> CycleIterator<C> {
return CycleIterator(cycling: cycledElements)
}
}

struct CycleIterator<C: Collection>: IteratorProtocol {
let cycledElements: C
var cycledElementIterator: C.Iterator

init(cycling cycledElements: C) {
self.cycledElements = cycledElements
self.cycledElementIterator = cycledElements.makeIterator()
}

public mutating func next() -> C.Iterator.Element? {
if let next = cycledElementIterator.next() {
return next
} else {
self.cycledElementIterator = cycledElements.makeIterator() // Cycle back again
return cycledElementIterator.next()
}
}
}

CycleSequence(cycling: [true, false]).prefix(7).forEach{ print($0) }
print()
CycleSequence(cycling: 1...3).prefix(7).forEach{ print($0) }
print()
CycleSequence(cycling: "ABC").prefix(7).forEach{ print($0) }
CycleSequence(cycling: EmptyCollection()).prefix(7).forEach{ print($0) }

0
ответ дан 25 апреля 2018 в 06:04 Источник Поделиться