How to beat NSMutableArray performance with Swift

2019-03-30 07:45发布

问题:

In Swift, I'm trying to build a large collection of items. When creating elements in CoreData on-the-go, this is very speedy. However when trying to keep an index to those items, creating an array Swift has a large performance impact. The code below is a benchmark between NSMutableArray and Swift's Array. When ran in the iOS Simulator, the Swift Array is around 8x slower. Why is this, can it be improved, or should it improve over the as Apple releases new builds of Xcode/Swift?

Code:

var start: NSDate
var time: NSTimeInterval
var batch = 1000000
var rate: Double

var oArr = NSMutableArray(capacity: batch)
start = NSDate()
for i in 1..batch {
    oArr.addObject(i)
}
time = -start.timeIntervalSinceNow
rate = Double(batch) / Double(time)
println("NSMutableArray \(batch) appends in \(time) sec: \(rate)/sec")

var sArr = Int[]()
start = NSDate()
for i in 1..batch {
    sArr += i
}
time = -start.timeIntervalSinceNow
rate = Double(batch) / Double(time)
println("Array<Int>     \(batch) appends in \(time) sec: \(rate)/sec")

Output in simulator (beta 2) 794%:

NSMutableArray 1000000 appends in 1.17320102453232 sec: 852368.843096295/sec
Array<Int>     1000000 appends in 9.31138801574707 sec: 107395.374170729/sec

Output in simulator (beta 3):

NSMutableArray 1000000 appends in 0.71416300535202 sec: 1400240.55083487/sec
Array<Int>     1000000 appends in 5.00839000940323 sec: 199664.961818569/sec

Output on iPhone 5 (beta 3 on iOS 7.1):

NSMutableArray 1000000 appends in 8.79256194829941 sec: 113732.494110367/sec
Array<Int>     1000000 appends in 55.6573320031166 sec: 17967.084730975/sec

回答1:

Update:

with Xcode beta-3, with a console program on a MacBook Pro 2 GHz Intel Core i7, I get these numbers with the original test (also fixed syntax due to minor grammar changes):

Debug (-O0):

NSMutableArray 1000000 appends in 0.782136023044586 sec: 1278549.9843203/sec
Array<Int>     1000000 appends in 5.56463801860809 sec: 179706.208500177/sec

Release (-Os):

NSMutableArray 1000000 appends in 0.14977502822876 sec: 6676680.43081684/sec
Array<Int>     1000000 appends in 0.124498963356018 sec: 8032195.3937913/sec

Release (-Ofast):

NSMutableArray 1000000 appends in 0.151567995548248 sec: 6597698.91646863/sec
Array<Int>     1000000 appends in 0.122538030147552 sec: 8160731.80543105/sec


回答2:

It doesn't seem fair that you're telling the NSMutableArray how much space it needs to allocate right up front but making the Swift array re-allocate itself with every single append. This makes the Swift version stupid fast, although it's kind of unfair in the other direction:

var sArr = Array<Int>(count: batch, repeatedValue: 0)
start = NSDate()
for i in 0..batch {
    sArr[i] = i
}

Edit: It looks like NSMutableArray doesn't actually use the capacity you provide to speed itself up, so maybe never mind?



回答3:

The Swift optimizer is very much a work in progress. Even between b1 and b3 I've seen a little benchmark program go from taking 3s (compiled at -O) to taking 0.2s. Remember to try -O and -Ofast, keep trying it with each beta, and file bugs about unusually or unexpectedly slow things. It should get a lot better over time :)



回答4:

I've done some tests on my old iPhone 4 with XCode 6.3.1 and found that Array is much faster than NSMutableArray.

Debug mode

NSMutableArray  1000000 appends in 8.11677902936935 sec: 123201.579885525/sec
Array1<Int>     1000000 appends in 3.9488559961319 sec: 253237.900034732/sec
Array2<Int>     1000000 appends in 2.59551799297333 sec: 385279.548324162/sec

Release mode

NSMutableArray  1000000 appends in 5.76798897981644 sec: 173370.650238625/sec
Array1<Int>     1000000 appends in 0.0752439498901367 sec: 13290105.0710402/sec
Array2<Int>     1000000 appends in 0.0505729913711548 sec: 19773400.2456174/sec

Where Array1 = [Int] (), Array2 = [Int?](count: batch, repeatedValue: nil)

And for Array1 I used array.append(i) instead of "+=" which can make it much faster.