繁体   English   中英

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

[英]Why are mutable strings slower than immutable strings?

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

编辑:

>>> 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')。

在一种假设的语言中,它提供了可变的和不可变的,或者是等价的字符串类型(我不能真正想到一个 - 例如,Python和Java都只有不可变的字符串,以及其他通过突变来增加间接性的方法因此当然可以减慢一些事情;-),没有任何性能差异的真正原因 - 例如,在C ++中,可互换地使用std::stringconst std::string我会期望不会性能差异(当然编译器可能能够使用在不变性计数后者更好地优化代码,但我不知道任何真实世界的那些执行这样理论上是可行的优化;-)。

拥有不可变字符串可能并确实允许在Java和Python中进行非常大量的优化。 例如,如果字符串被散列,则哈希值可以被缓存,并且永远不必重新计算(因为字符串不能更改) - 这在使用散列字符串的Python中尤其重要(对于集合中的查找)和词典)如此奢华甚至“幕后”。 新鲜的副本永远不需要“以防万一”,前一个副本在此期间发生了变化 - 只要需要该字符串,就可以系统地分发对单个副本的引用。 Python还大量使用(某些)字符串的“实习”,可能允许进行常数时间比较和许多其他类似的快速操作 - 将其视为一种更多方式,一种更先进的方法,以利用字符串的不变性缓存更多经常对它们执行的操作的结果。

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

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

我不知道它们是否真的慢得多,但是他们在很多时候都会考虑编程更容易,因为对象/字符串的状态不能改变。 这是对我来说不变的最重要的财产。

此外,您可能会认为不可变字符串更快,因为它们具有较少的状态(可以更改),这可能意味着较低的内存消耗,CPU周期。

我在google搜索时也发现了这篇有趣的文章,我想引用一下:

知道字符串是不可变的,这使得在构建时很容易将其布局 - 固定且不变的存储要求

使用不可变的字符串,python可以实习它并在内部通过它在内存中的地址引用它。 这意味着要比较两个字符串,它只需要比较它们在内存中的地址(除非其中一个没有实现)。 另外,请记住并非所有字符串都被实现。 我已经看到了没有实习的构造字符串的例子。

对于可变字符串,字符串比较将涉及逐个字符地比较它们,并且还需要在不同位置存储相同的字符串(malloc不是空闲的)或添加逻辑以跟踪给定字符串被引用的次数并制作副本对于每个突变,如果有多个推荐人。

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

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

s = 'a'
d = {s : 1}
s = s + 'b'
d[s] = ?

我想python可以跟踪哪些字符串有哪些字符串作为键,并在修改字符串时更新所有哈希表,但这只是增加了dict插入的开销。 如果没有dict插入/查找,你就不能在python中做任何事情 ,这是非常不合适的,所以这将是非常非常糟糕的。 它还增加了字符串操作的开销。

你问题的明显答案是普通字符串是用C实现的,而MutableString是用Python实现的。

不仅可变字符串上的每个操作都有通过一个或多个Python函数调用的开销,但实现本质上是一个不可变字符串的包装器 - 当你修改字符串时,它创建一个新的不可变字符串并抛出旧字符串远。 您可以在Python lib目录中的UserString.py文件中读取源代码。

引用Python文档:

注意:

此模块中的此UserString类仅可用于向后兼容。 如果您编写的代码不需要使用早于Python 2.2的Python版本,请考虑直接从内置str类型继承子类而不是使用UserString(没有内置的等效于MutableString)。

该模块定义了一个类,它充当字符串对象的包装器。 它是您自己的类字符串类的有用基类,它可以从它们继承并覆盖现有方法或添加新方法。 通过这种方式,可以向字符串添加新行为。

应该注意的是,与真正的字符串或Unicode对象相比,这些类的效率非常低; 对于MutableString尤其如此。

(重点补充)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM