【问题标题】:Slow Swift Arrays and Strings performance缓慢的 Swift 数组和字符串性能
【发布时间】:2015-01-15 09:50:17
【问题描述】:

这是两个非常相似的Levenshtein Distance algorithms

Swift 实现: https://gist.github.com/bgreenlee/52d93a1d8fa1b8c1f38b

Objective-C 实现: https://gist.github.com/boratlibre/1593632

swift 的实现比ObjC 慢得多 我已经发送了几个小时以使其更快但是...似乎Swift 数组和Strings 操作不如objC 快。

在 2000 年 random Strings 计算 Swift 实现比 ObjC 慢大约 100(!!!) 倍。

老实说,我不知道可能出了什么问题,因为即使是 swift 的这一部分

func levenshtein(aStr: String, bStr: String) -> Int {
// create character arrays
let a = Array(aStr)
let b = Array(bStr)
...

Objective C中的整个算法慢几倍

有人知道如何加快swift 的计算吗?

提前谢谢你!

追加

毕竟建议的改进 swift 代码看起来像这样。 在发布配置中它比 ObjC 慢 4 倍

import Foundation
class Array2D {
    var cols:Int, rows:Int
    var matrix:UnsafeMutablePointer<Int>


    init(cols:Int, rows:Int) {
        self.cols = cols
        self.rows = rows
        matrix = UnsafeMutablePointer<Int>(malloc(UInt(cols * rows) * UInt(sizeof(Int))))
        for i in 0...cols*rows {
            matrix[i] = 0
        }

    }

    subscript(col:Int, row:Int) -> Int {
        get {
            return matrix[cols * row + col] as Int
        }
        set {
            matrix[cols*row+col] = newValue
        }
    }

    func colCount() -> Int {
        return self.cols
    }

    func rowCount() -> Int {
        return self.rows
    }
}

extension String {
    func levenshteinDistanceFromStringSwift(comparingString: NSString) -> Int {
        let aStr = self
        let bStr = comparingString

//        let a = Array(aStr.unicodeScalars)
//        let b = Array(bStr.unicodeScalars)

        let a:NSString = aStr
        let b:NSString = bStr

        var dist = Array2D(cols: a.length + 1, rows: b.length + 1)



        for i in 1...a.length {
            dist[i, 0] = i
        }

        for j in 1...b.length {
            dist[0, j] = j
        }

        for i in 1...a.length {
            for j in 1...b.length {
                if a.characterAtIndex(i-1) == b.characterAtIndex(j-1) {
                    dist[i, j] = dist[i-1, j-1]  // noop
                } else {
                    dist[i, j] = min(
                        dist[i-1, j] + 1,  // deletion
                        dist[i, j-1] + 1,  // insertion
                        dist[i-1, j-1] + 1  // substitution
                    )
                }
            }
        }

        return dist[a.length, b.length]

    }
    func levenshteinDistanceFromStringObjC(comparingString: String) -> Int {
        let aStr = self
        let bStr = comparingString
        //It is really strange, but I should link Objective-C coz dramatic slow swift performance
        return aStr.compareWithWord(bStr, matchGain: 0, missingCost: 1)

    }

}

malloc?? NSString??最后速度降低了 4 倍?有人需要swift吗?

【问题讨论】:

  • 其他用户确认 Swift 数组真的很慢stackoverflow.com/questions/24567429/… 现在 Swift 不是 Beta。所以对我来说这看起来很奇怪。
  • 2年了,这段代码还是很慢,没办法修复?

标签: objective-c arrays string performance swift


【解决方案1】:

Swift 代码比 Objective-C 代码慢的原因有很多。 我通过比较两个固定字符串 100 次做了一个非常简单的测试用例。

  • Objective-C 代码:0.026 秒
  • Swift 代码:3.14 秒

第一个原因是 Swift Character 代表一个“扩展字形簇”, 它可以包含多个 Unicode 代码点(例如“标志”)。这使得 将字符串分解为字符很慢。另一方面,Objective-C NSString 将字符串存储为 UTF-16 代码点序列。

如果你替换

let a = Array(aStr)
let b = Array(bStr)

通过

let a = Array(aStr.utf16)
let b = Array(bStr.utf16)

这样 Swift 代码也可以在 UTF-16 序列上工作,然后时间就会减少 到 1.88 秒。

二维数组的分配也很慢。分配速度更快 单个一维数组。我在这里找到了一个简单的Array2D 类: http://blog.trolieb.com/trouble-multidimensional-arrays-swift/

class Array2D {
    var cols:Int, rows:Int
    var matrix: [Int]


    init(cols:Int, rows:Int) {
        self.cols = cols
        self.rows = rows
        matrix = Array(count:cols*rows, repeatedValue:0)
    }

    subscript(col:Int, row:Int) -> Int {
        get {
            return matrix[cols * row + col]
        }
        set {
            matrix[cols*row+col] = newValue
        }
    }

    func colCount() -> Int {
        return self.cols
    }

    func rowCount() -> Int {
        return self.rows
    }
}

在您的代码中使用该类

func levenshtein(aStr: String, bStr: String) -> Int {
    let a = Array(aStr.utf16)
    let b = Array(bStr.utf16)

    var dist = Array2D(cols: a.count + 1, rows: b.count + 1)

    for i in 1...a.count {
        dist[i, 0] = i
    }

    for j in 1...b.count {
        dist[0, j] = j
    }

    for i in 1...a.count {
        for j in 1...b.count {
            if a[i-1] == b[j-1] {
                dist[i, j] = dist[i-1, j-1]  // noop
            } else {
                dist[i, j] = min(
                    dist[i-1, j] + 1,  // deletion
                    dist[i, j-1] + 1,  // insertion
                    dist[i-1, j-1] + 1  // substitution
                )
            }
        }
    }

    return dist[a.count, b.count]
}

测试用例中的时间减少到 0.84 秒。

我在 Swift 代码中发现的最后一个瓶颈是 min() 函数。 Swift 库有一个更快的内置min() 函数。所以只是删除 Swift 代码中的自定义函数减少了测试用例的时间 0.04 秒,几乎和 Objective-C 版本一样好。

附录: 使用 Unicode 标量似乎更快:

let a = Array(aStr.unicodeScalars)
let b = Array(bStr.unicodeScalars)

并且它的优点是它可以与代理对一起正常工作,例如 作为表情符号。

【讨论】:

  • 感谢您如此详细的评论!不幸的是,毕竟建议的修改我看不到你提到的改进。我的数字是: 10000 个城市的 Swift Levenstaining 速率 589/秒 ObjC 10000 个城市的速率 106281/秒的 Levenstaining 我正在使用 Swift 命令行项目模板进行测试。可能我需要做一些额外的设置吗?
  • 顺便说一句,为了得到这个数字,我已经将 Array2D 恢复为使用 NSMutableArray,它比 Swift Arrays 快得多。
  • @Alexey:您是否将构建配置从“调试”切换到“发布”以获得优化的代码? – 您也可以尝试使用 var dist = UnsafeMutablePointer&lt;Int&gt;(malloc((a.count + 1) * (b.count + 1) * UInt(sizeof(Int)))) 的普通 malloc 数组(如在 ObjC 代码中)。
  • 发布配置后,我有 1000 个城市的 Swift Levenstaining 速率 4259/秒 ObjC Levenstaining 1000 个城市的速率 142328/秒
  • mallocing Array2D 后的数字是 Swift Levenstaining 的 10000 个城市速率 28468/秒 ObjC Levenstaining 的 10000 个城市速率 130751/秒 它慢了 7 倍。无论如何它不是 100 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 2011-12-14
  • 2017-01-08
相关资源
最近更新 更多