【问题标题】:Can `del` make Python faster?`del` 可以让 Python 更快吗?
【发布时间】:2019-04-28 16:36:51
【问题描述】:

作为一名程序员,我通常会尽量避免使用 del 语句,因为它通常是 Python 程序不需要的额外复杂功能。但是,在浏览标准库(threadingos 等...)和伪标准库(numpyscipy 等...)时,我发现它使用了非零数量的时候,我想更好地理解什么时候是/不适合 del 声明。

具体来说,我很好奇Pythondel语句与Python程序效率的关系。在我看来,del 可以通过减少需要筛选的杂乱查找指令的数量来帮助程序运行得更快。但是,我也可以看到额外指令占用的时间多于节省的时间。

我的问题是:有没有人有任何有趣的代码 sn-ps 来演示 del 显着改变程序速度的情况?我对del 提高程序执行速度的情况最感兴趣,尽管del 确实会造成伤害的非平凡情况也很有趣。

【问题讨论】:

  • “在我看来,del 可以通过减少需要筛选的杂乱查找指令的数量来帮助程序运行得更快”——这不是 Python 中变量查找的工作方式,而且几乎可以肯定不是在您看到的使用del 的代码中经过作者的头脑。
  • 查看您给出的示例之一 (threading),该模块使用 del 来避免全局命名空间混乱、删除 dict 条目并避免回溯引用循环。

标签: python


【解决方案1】:

具体来说,我很好奇Pythondel语句与Python程序效率的关系。

就性能而言,del(不包括像del x[i] 这样的索引删除)主要用于 GC 目的。如果您有一个指向某个不再需要的大对象的变量,deling 该变量将(假设没有其他对它的引用)释放该对象(对于 CPython,这会立即发生,因为它使用引用计数)。如果您要填满 RAM/缓存,这可能会使程序更快;唯一知道的方法是实际对其进行基准测试。

在我看来,del 可能会通过减少需要筛选的杂乱查找指令的数量来帮助程序运行得更快。

除非您使用数千个变量(您不应该使用),否则使用 del 删除变量不太可能对性能产生任何明显影响。

【讨论】:

  • Python 无论如何都会在它们超出范围时删除这些引用,除非像闭包这样的情况需要它们,而且在大多数情况下它会比使用 del 更快。
  • @user2357112 当然可以,但在某些情况下,您肯定不想在释放它指向的对象之前等待变量超出范围。
  • @arshajii 这个问题是我真正想要解决的问题。特别是在大型、难以重构、单一功能的情况下。在这种情况下,del 是否能够给我们带来有意义的速度提升。我想另一种说法是:命名空间整理对程序速度有多大影响?
  • @Erotemic 任何性能提升都不会来自“命名空间整理”,而是来自使用更少的内存(如果您正在填充 RAM/缓存,这可能会很明显)。
  • 所以也许这个问题可以作为系统硬件的功能来回答。在某些时候,如果命名空间太大,哈希表将不再适合缓存并因此导致执行时间变慢是否正确?在这种情况下,我有兴趣查看衡量减速与 dict 大小(wrt CPU 缓存大小)的基准。
【解决方案2】:

标准 Python 库使用del 的主要原因不是速度,而是命名空间整理(“避免命名空间污染”是我相信我见过的另一个术语为了这)。如user2357112 noted in a comment,也可以用来打破回溯循环。

让我们举个具体的例子:line 58 of types.py in the cpython implementation 读作:

del sys, _f, _g, _C, _c, # Not for export

如果我们看上面,我们会发现:

def _f(): pass
FunctionType = type(_f)
LambdaType = type(lambda: None)         # Same as FunctionType
CodeType = type(_f.__code__)
MappingProxyType = type(type.__dict__)
SimpleNamespace = type(sys.implementation)

def _g():
    yield 1
GeneratorType = type(_g())

_f_gdeled 中的两个名称;正如评论所说,它们“不适合出口”。1

您可能认为这是通过以下方式涵盖的:

__all__ = [n for n in globals() if n[:1] != '_']

(在同一文件的末尾附近),但正如What's the python __all__ module level variable for?(和链接的Can someone explain __all__ in Python?)所指出的,这些会影响通过from types import * 导出的名称,而不是通过import types; dir(types) 可见的名称。

没有必要清理你的模块命名空间,但这样做可以防止人们潜入其中并使用未定义的项目。所以它有几个用途。


1好像有人忘记更新此内容以包含 _ag。不幸的是,_GeneratorWrapper 更难隐藏。

【讨论】:

  • 命名空间整理对程序速度有影响吗?我的想法是,一个大的命名空间(又名哈希表)可能会降低速度(由于哈希冲突、将页面加载到内存中、缓存效率等),或者 CPython 的哈希表这么快没关系?
  • 确实没有。 CPython 对其中的大多数使用自己的字典实现(我认为有一些特殊情况下有插槽,但对于那些del 完全不相关),它的查找效率非常好,并且很大程度上独立于 dict-size。请参阅 stackoverflow.com/questions/513882/… 了解一些性能统计信息。
  • 相关:stackoverflow.com/questions/31442906/…(但是,dict 实现在 Python 的生命周期中至少改变了两次,所以不确定这里的答案是否可以指望)。不过,您需要让更大和更小的字典正好穿过收缩/扩展点才能产生任何效果。
猜你喜欢
  • 1970-01-01
  • 2017-11-06
  • 2013-10-30
  • 2011-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多