7

Integer をオブジェクトの配列 (キーとして int、値として配列を持つ辞書) にマップするデータ構造を Swift で構築しようとしています。オブジェクトは非常に小さく、UIColor と Int をラップするだけです。Dictionary の値の型として Swift 配列を使用する実装と、NSMutableArray を値の型として使用する実装の 2 つがあります。Objective-C のコードは非常に高速に実行されますが、Swift のコードは非常に遅く実行されます。理想的には、NSMutableArray を使用したくなく、Swift 配列として保持したいと考えています。この理由は、私がアルゴリズムを作成していて、パフォーマンスが重要であるためです。objC_msgSend のオーバーヘッドに気付きました。Swift コードの最適化を手伝ってくれる人はいますか? 私は何か間違ったことをしていますか、それとも配列を値型として迅速に扱うことの副産物ですか? もしそれが、この場合、なぜ値型のパフォーマンスが非常に遅いのか、どのようなオプションがあるのか​​、このシナリオをどのように拡張できるのかを理解したいと思います。以下に、コード スニペットと結果のベンチマークを掲載しました。

スウィフト配列コード:

let numColors = colorCount(filter: filter, colorInfoCount: colorInfo.count)
var colorCountsArray: [Int] = [Int]()
var countToColorMap: [Int:[CountedColor]] = [Int:[CountedColor]](minimumCapacity: capacity)
var topColors = [CountedColor]()

var startTime = CACurrentMediaTime()
for (color, colorCount) in colorInfo {
    colorCountsArray.append(colorCount)
    if countToColorMap[colorCount] != nil {
        countToColorMap[colorCount]?.append(CountedColor(color: color, colorCount: colorCount))
    } else {
        countToColorMap[colorCount] = [CountedColor(color: color, colorCount: colorCount)]
    }
}
var endTime = CACurrentMediaTime()
print("Time after mapping: \(endTime - startTime)")

迅速なパフォーマンス:

Time after mapping: 45.0881789259997

NSMutableArray コード:

let numColors = colorCount(filter: filter, colorInfoCount: colorInfo.count)
var colorCountsArray: [Int] = [Int]()
var countToColorMap: [Int:NSMutableArray] = [Int:NSMutableArray](minimumCapacity: capacity)
var topColors = [CountedColor]()


var startTime = CACurrentMediaTime()
for (color, colorCount) in colorInfo {
    colorCountsArray.append(colorCount)
    if countToColorMap[colorCount] != nil {
        countToColorMap[colorCount]?.add(CountedColor(color: color, colorCount: colorCount))
    } else {
        countToColorMap[colorCount] = NSMutableArray(object: CountedColor(color: color, colorCount: colorCount))
    }
}
var endTime = CACurrentMediaTime()
print("Time after mapping: \(endTime - startTime)")

NSMutableArray のパフォーマンス:

Time after mapping: 0.367132211999888

colorInfo オブジェクトは、UIColor オブジェクトをカウントを表す整数値にマッピングする辞書です。コードは本質的にこれを逆にマッピングし、整数を UIColor 配列にマッピングします (複数の色が同じカウントを持つ可能性があるため、配列です)。colorInfo には、UIColor と Int のキー値のペアが 60,000 個含まれています。

4

2 に答える 2

16

コピー オン ライトは難しい作業であり、変更しようとしている構造を共有しているものがいくつあるかを慎重に検討する必要があります。犯人はここにいます。

countToColorMap[colorCount]?.append(CountedColor(color: color as! UIColor, colorCount: colorCount))

これは、変更されてディクショナリに戻される一時的な値を生成しています。2 つの「もの」が同じ基礎となるデータ構造 (ディクショナリとappend) を見ているため、コピー オン ライトが強制されます。

これを修正する秘訣は、変更時にコピーが 1 つだけであることを確認することです。どのように?辞書から取り出してください。これを置き換えます:

if countToColorMap[colorCount] != nil {
    countToColorMap[colorCount]?.append(CountedColor(color: color as! UIColor, colorCount: colorCount))
} else {
    countToColorMap[colorCount] = [CountedColor(color: color as! UIColor, colorCount: colorCount)]
}

ランタイムは次のとおりです。

Elapsed Time: 74.2517465990022
53217

これとともに:

var countForColor = countToColorMap.removeValue(forKey: colorCount) ?? []
countForColor.append(CountedColor(color: color as! UIColor, colorCount: colorCount))
countToColorMap[colorCount] = countForColor

ランタイムは次のとおりです。

Elapsed Time: 0.370953808000195
53217
于 2016-12-15T01:20:32.873 に答える