首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么可变字符串比不可变字符串慢?

为什么可变字符串比不可变字符串慢?
EN

Stack Overflow用户
提问于 2010-09-05 09:01:35
回答 2查看 1.8K关注 0票数 4

为什么可变字符串比不可变字符串慢?

编辑:

代码语言:javascript
复制
>>> import UserString
... def test():
...     s = UserString.MutableString('Python')
...     for i in range(3):
...         s[0] = 'a'
... 
... if __name__=='__main__':
...     from timeit import Timer
...     t = Timer("test()", "from __main__ import test")
...     print t.timeit()
13.5236170292



>>> import UserString
... def test():
...     s = UserString.MutableString('Python')
...     s = 'abcd'
...     for i in range(3):
...         s = 'a' + s[1:]
... 
... if __name__=='__main__':
...     from timeit import Timer
...     t = Timer("test()", "from __main__ import test")
...     print t.timeit()
6.24725079536


>>> import UserString
... def test():
...     s = UserString.MutableString('Python')
...     for i in range(3):
...         s = 'a' + s[1:]
... 
... if __name__=='__main__':
...     from timeit import Timer
...     t = Timer("test()", "from __main__ import test")
...     print t.timeit()
38.6385951042

我认为我将s= UserString.MutableString('Python')放在第二个测试中的原因是显而易见的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-09-05 09:24:52

在一种假设的语言中,同时提供了可变的和不可变的,否则是等价的,字符串类型(我真的不能随便想到一个--例如,Python和Java都只有不可变的字符串,以及通过突变来创建一个字符串的其他方法,这增加了间接性,因此当然可以放慢速度;-),没有任何性能差异的真正原因--例如,在C++中,交替使用std::stringconst std::string我希望不会造成任何性能差异(诚然,通过依靠不变性,编译器可能能够更好地优化使用后者的代码,但我不知道任何现实世界中确实可以执行这种理论上可能的优化;-)。

在Java和Python中,拥有不变的字符串可能并且实际上确实允许进行非常实质性的优化。例如,如果字符串被散列,则散列可以被缓存,并且永远不需要重新计算(因为字符串不能改变) --这在Python中尤其重要,因为Python大量使用散列字符串(用于集合和字典中的查找),甚至是“幕后”。在此期间,不需要“以防万一”创建新的副本--无论何时需要该字符串,都可以系统地分发对单个副本的引用。Python还大量地使用(某些)字符串的“内部”操作,这可能允许常量时间比较和许多其他类似的快速操作--可以将其视为另一种方法,可以肯定是一种更高级的方法,它利用字符串的不变性来缓存经常对其执行的操作的更多结果。

当然,这并不是说给定的编译器将利用所有可能的优化。例如,当请求一个字符串的片段时,实际上不需要创建一个新对象并复制数据--新的片段可能引用具有偏移量(和独立存储的长度)的旧片段,这对于从其中提取许多片段的大字符串来说可能是一个很好的优化。Python之所以不这样做,是因为除非特别注意内存管理,否则很容易导致“大”字符串在实际需要时全部保留在内存中--但这是一种权衡,不同的实现可能会选择执行(当然,还有额外的内存管理负担--更复杂、更难调试的编译器和运行时代码,用于所讨论的语言)。

我在这里只是触及了皮毛--如果在可变和不可变版本中都存在可互换的字符串类型,那么很难保持这些优点(我怀疑这就是为什么,至少就我目前所知,C++编译器实际上并不关心这样的优化,尽管通常非常注重性能)。但是,通过提供唯一的不可变字符串作为基本的原始数据类型(从而在您确实需要可变字符串时隐含地接受一些缺点;-),诸如Java和Python这样的语言显然可以获得各种优势--性能问题只是其中的一组(例如,Python选择只允许不可变的原始类型是hashable,并不是一个以性能为中心的设计决策--它更多的是关于集合和字典行为的清晰度和可预测性!)。

票数 25
EN

Stack Overflow用户

发布于 2010-09-05 09:11:27

有了一个不可变的字符串,python就可以把它放在内部,并通过它在内存中的地址在内部引用它。这意味着,要比较两个字符串,它只需比较它们在内存中的地址(除非其中一个未被实例化)。此外,请记住,并不是所有的字符串都会被拦截。我已经看到了构造字符串的例子,这些字符串并没有被内嵌。

对于可变字符串,字符串比较将涉及到逐个字符地比较它们,还需要将相同的字符串存储在不同的位置(malloc不是免费的),或者添加逻辑来跟踪给定字符串被引用的次数,并在存在多个引用的情况下为每个突变创建一个副本。

看起来python已经针对字符串比较进行了优化。这是有意义的,因为即使字符串操作在大多数情况下也涉及字符串比较,因此对于大多数用例来说,它是最小的公分母。

不可变字符串的另一个优点是,它使得它们可以是hashable的,这是将它们用于字典键的要求。想象一个场景,它们是可变的:

代码语言:javascript
复制
s = 'a'
d = {s : 1}
s = s + 'b'
d[s] = ?

我认为python可以跟踪哪些dict将哪些字符串作为键,并在字符串被修改时更新它们的所有哈希表,但这只是增加了dict插入的开销。可以毫不含糊地说,如果没有字典插入/查找,你就不能在python中执行任何操作,所以这将是非常非常糟糕的。它还增加了字符串操作的开销。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3644576

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档