My goal in this project is to make something that can—as, quickly, effectively, and efficiently as Array(repeating: , count: )
—initialize an Array
of alternating values.
Note: I am using Swift 4.1 and Xcode 9.3
My Array Initialization:
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
}
}
Usage
Initialization - init(repeatingValues: , count: )
:
let array = Array(repeatingValues: [true, false], count: 10)
print(array) //prints: [true, false, true, false, true, false, true, false, true, false]
Benchmark Comparison
Comparing: Array(repeating: , count: )
vs. Array(repeatingValues: , count: )
Benchmark Time Extension:
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
Execution Time: 2ms 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
Execution Time: 472ms 555μs
Results:
Array(repeatingValues: , count: )
191.3x slower than Array(repeating: , count: )
How can I match Apple's efficiency and speed?
self.measure{}
. I was able to shave off 0.03 seconds by assigningarr.count
to a variable, as it's cheaper then lookup against thearr
object. That being said, Apple is very likely initializing the array in a low-level function, so you aren't going to be reaching their level of performance unless you do the same. \$\endgroup\$(0..<count).flatMap { [true, false] }
\$\endgroup\$