一尘不染

Swift数组和字符串性能缓慢

swift

这是两个非常相似的地方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执行速度比慢约100(!!!)倍ObjC

老实说,我不知道可能出什么问题了,因为这很快

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

比整个算法慢几倍 Objective C

有谁知道如何加快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 ?? 并在最后四倍速度下降?有人需要迅速了吗?


阅读 294

收藏
2020-07-07

共1个答案

一尘不染

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

  • Objective-C代码:0.026秒
  • 迅捷代码: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-multiDimension-
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)

并具有可以与代理对(例如表情符号)一起正常使用的优点。

2020-07-07