Swift与Python性能比较

3
我可以帮您进行翻译。以下是需要翻译的内容:

我有一些Python代码,用于构建一个包含大约250K个字符串(单词)作为键的字典,每个值都有一个字符串数组。Python版本在0.5秒内运行。我需要将其移植到Swift,但我的Swift移植版运行时间为10.1秒,比Python慢了20倍。

以下是Python代码:

wordsDictionary = defaultdict(list)
for word in words:
    wordsDictionary[sort_string(word)].append(word)

以下是 Swift 代码:

var wordsDictionary : Dictionary<String, [String]> = Dictionary()
for word in words {
    let sortedWord : String = String(word.characters.sort())
    if wordsDictionary[sortedWord] == nil {
        wordsDictionary[sortedWord] = []
    }
    wordsDictionary[sortedWord]?.append(word)
}

有没有办法加快Swift版本的速度,或者说Swift字典仍然比Python慢得多?

1
你实际上进行了分析以查看哪个部分运行缓慢吗?这不仅仅是基本的字典操作。这里涉及到很多事情... - nhgrif
1
我猜字符串排序是不同之处。 - Nick Bailey
谢谢。我之前没有在循环内进行分析,但是现在做了之后发现字符串排序大约需要5s,数组分配大约需要3s,字典附加大约需要2秒钟,总计10秒钟左右。所以这不仅仅是一个字典问题。Python 代码也会完成所有这些操作,包括字符串排序,只需要0.5秒。因此,Swift 要慢得多或者我做错了什么。 - Jeff Zacharias
1
Swift 代码是在 Playground 中运行还是应用程序中运行? - vikingosegundo
3
我的猜测是:Python中的sort_string函数对UTF-8字节数组进行超级愚蠢的排序,而Swift中的word.characters将字符串分解为图形群,并且.sort()为每个图形群创建了许多小对象的新集合,这有效地降低了性能,与Python相比。我怀疑你可能严重误解了"字符"的含义- Swift 正确但速度慢,而 Python 非常不正确但快速。因为在英语/ ASCII 中没有区别,所以你很难理解为什么会出现问题。 - hamstergene
显示剩余6条评论
2个回答

1

在更改字典的字符串排序和数组分配后,这是我的最终代码,执行时间为1.4秒。

var wordsDictionary : Dictionary<String, [String]> = Dictionary()
for word in words {
    let sortedWordUTF8 = word.utf8.sort()
    let sortedWord : String = NSString(bytes: sortedWordUTF8, length: sortedWordUTF8.count, encoding: NSUTF8StringEncoding) as! String
    if wordsDictionary[sortedWord] == nil {
        wordsDictionary[sortedWord] = [word]
    }
    else {
        wordsDictionary[sortedWord]!.append(word)
    }
}

2
你的代码仍然包含一个冗余的检查 (?.append 而不是 .append)。此外,对 UTF8 字节进行排序通常不会得到有效的 UTF8 序列,所以除了 ASCII 字符串之外,你的代码是有问题的。你必须对字符进行排序。 - Konrad Rudolph
我编辑了相关代码并删除了冗余检查。我的意思是,对于只处理ASCII字符串的应用程序,这种字符串排序方法将起作用。 - Jeff Zacharias
如果您的应用程序仅处理ASCII字符串,则“Strings”是错误的类型 - 请改用字节数组。这样可以避免很多非常烦人的错误。 - Konrad Rudolph

0

你的Python代码和Swift代码之间的主要区别似乎是:

正如在问题的评论中已经提到的,Swift的内部字符串表示与Python的相当不同,并且字符串比较是基于字形级别而不是编码级别进行的。由于您已经排除了这一点,我就不再详细说明了。

接下来,在Swift中,你每次迭代都会两次询问字典查找你的键-字符串。即使对于哈希查找,这也可能需要一些时间,因为我怀疑第二次访问不会被优化掉。你可以通过使用indexForKey方法请求DictionaryIndex并重用它来避免这种情况。使用此索引访问字典应该更快。

最后,你检查了一个不需要检查的可选项。Swift代码中的else分支肯定会找到一个非nil的字典条目。


2
那是错误的。Python中的list实际上是一个向量,而不是链表。 - Konrad Rudolph
1
这就是downvote的作用。现在你已经纠正了它,downvote也会被移除。 - Konrad Rudolph
值得一提的是,将Python改为使用与Swift相同的两个查找(即使不符合习惯用法)也不会改变时间。将Python更改为对字形进行排序(使用“正则表达式”来提取字形),循环时间大约增加了一倍。这仍然让它们之间存在一个数量级的差距。 - Veedrac
@Jazzmaniac 我本来想提供一个基准测试,但事实证明我也没有进行归一化,这是我没有意识到 Swift 在做的。没关系,它仍然不像 Swift 那样慢,但我无法弄清楚 Swift 认为什么是“归一化”。例如,"e\u{305}\u{31b}" != "e\u{31b}\u{305}",尽管它们是 NFDNFKDNFCNFKC 等效的,但 "e\u{301}" == "\u{e9}",因此发生了某些归一化。文档非常模糊。 - Veedrac
@Jazzmaniac 啊,据说是NFD,除了它似乎有问题。我已经发了一个相关问题:https://dev59.com/RpPfa4cB1Zd3GeqPFJjP - Veedrac
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接