【问题标题】:Python: fastest strategy for remove a lot of keys from dictPython:从字典中删除大量键的最快策略
【发布时间】:2014-05-05 07:15:35
【问题描述】:

据我所知,Python 字典是一个 HashTable,如果表的大小超过当前表最大大小 (Objects\dictnotes.txt) 的 2/3,它会调整大小。

我需要删除很多字典项目(数千个),例如,每小时一次,基于简单的标准 - if key

我了解用于创建新 dict 以及在迭代时调整 dict 大小的 dict 理解。

# dict comprehension
new_d = {key: value for key, value in d.iteritems() if key >= guard_condition }

# resize while iterating
for key in d:
    if key < guard_condition:
        del d[key]

还有其他方法可以达到这个目的吗? 哪个更快?

【问题讨论】:

  • 您的两种方法不会产生相同的输出。第一个将保留所有等于guard_condition 的键,第二个将丢弃它们。
  • “几千个键,每小时一次”对于计算机来说听起来并不多。实际需要多长时间?你需要多长时间?字典有多大,你是删除大部分还是保留大部分?是什么样的键值/守卫条件测试?
  • 这个 sn-p 实际上是您应用程序的瓶颈吗?你知道:“过早的优化......”
  • 用几千个键对它进行 cProfiling 并识别可能的瓶颈如何?
  • 好的@viach 我也这么想。你如何确定要保留什么?门槛是什么意思?您可能会更好地使用 LRU(最近最少使用)队列来确定要删除的内容,并在添加新项目时即时删除,删除其他项目,而不是经常这样做。这将随着时间的推移摊销性能损失。

标签: python performance python-2.7 dictionary


【解决方案1】:

在我上面的评论之外 - 使用 LRU(最近最少使用)或 LFU(最不常用)缓存:

http://en.wikipedia.org/wiki/Cache_algorithms

当新项目进入时,使用适当的策略确定要清除的项目。这可以在应用程序的整个生命周期内分摊删除项目的成本,而不是在您有选择地删除项目的偶尔爆发中。

我并不认为有一种更快的方法可以使用 del[key] 从字典中删除,但是有更好的方法来实现您(我猜)想要做的事情。 LRU 和 LFU 是非常流行和常用的解决方案。

【讨论】:

    【解决方案2】:

    这取决于您的字典大小以及您必须删除多少元素:如果您删除了少于 80% 的字典键,那么与“字典理解”相比,“在迭代时调整大小”更快。如果你删除超过 80% 的字典键,那么“字典理解”会更快。自己试试这段代码

    import cProfile, pstats, StringIO
    pr = cProfile.Profile()
    pr.enable()
    
    guard_condition = int(raw_input("Enter guard_condition: "))
    
    d = {item: item for item in xrange(10000000)};
    
    new_d = {key: value for key, value in d.iteritems() if key >= guard_condition }
    
    def del_iter(d, guard_condition):
        for key in d.keys():
            if key < guard_condition:
                del d[key]
    
    del_iter(d, guard_condition)
    
    pr.disable()
    s = StringIO.StringIO()
    sortby = 'cumulative'
    ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
    ps.print_stats()
    print s.getvalue()
    

    对于guard_condition = 7000000,输出为

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    2.794    2.794    2.794    2.794 {raw_input}
         1    1.263    1.263    1.263    1.263 dictDel1.py:7(<dictcomp>)
         1    1.030    1.030    1.030    1.030 dictDel1.py:9(<dictcomp>) <-- dict comprehension
         1    0.892    0.892    0.976    0.976 dictDel1.py:11(del_iter) <-- resize while iterating
         1    0.085    0.085    0.085    0.085 {method 'keys' of 'dict' objects}
         1    0.000    0.000    0.000    0.000 {method 'iteritems' of 'dict' objects}
         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    

    当guard_condition = 8500000时,输出为

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    3.316    3.316    3.316    3.316 {raw_input}
         1    1.247    1.247    1.247    1.247 dictDel1.py:7(<dictcomp>)
         1    0.937    0.937    1.052    1.052 dictDel1.py:11(del_iter) <-- resize while iterating
         1    0.787    0.787    0.787    0.787 dictDel1.py:9(<dictcomp>) <-- dict comprehension
         1    0.115    0.115    0.115    0.115 {method 'keys' of 'dict' objects}
         1    0.000    0.000    0.000    0.000 {method 'iteritems' of 'dict' objects}
         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    

    【讨论】:

      【解决方案3】:

      从纯粹的性能角度来看,无论您是使用 Ruby 还是 Python 编程,while-loop 总是比列表解析更快。但是由于您使用 Python 进行编程,因此您可能希望使用列表解析,因为如果速度是最重要的,您就不会使用 Python 进行编程。

      【讨论】:

      • Python 的列表解析是用 C 实现的,因此通常比 for 循环更快。但是在这种情况下,循环做了一些完全不同的事情——字典推导构建了一个全新的字典,而 for 循环从旧字典中删除了元素。没有笼统的陈述会有所帮助。
      • 同意,理解速度很快。在这种情况下,问题还在于构建一个新字典(使用字典理解)是否比编辑现有字典更快。
      • 列表理解速度很快,但 viach 的测试显示普通的 for 循环更快。或者您可以四处搜索以查看性能差异。
      • @RemcoGerlich: dict comprehension is 10 times slower in this case 但这些例子做了不同的事情。尽管即使在列表推导和显式 for 循环具有相同效果的情况下;显式 for 循环可能更快。我不会说“总是”,但根据我的经验,它通常更快。
      • “如果速度是最重要的,你就不会用 Python 编程” 似乎没有必要。在很多普通程序中,只需要加速 1% 的代码即可获得时间性能提升。通常,这 1% 已经在从 Python 调用的 C 代码(数据库代码、数字运算代码)中进行了优化,或者代码受到 I/O 限制,因此应用程序代码的速度无关紧要。
      【解决方案4】:

      我尝试过 IPython,结果如下:

      In [140]: d = {item: item for item in xrange(10000)};
      
      In [142]: guard_condition = 9000;
      
      In [144]: %timeit new_d = {key: value for key, value in d.iteritems() if key >=
      100 loops, best of 3: 2.54 ms per loop
      
      In [140]: d = {item: item for item in xrange(10000)};
      
      In [149]: def del_iter(d, guard_condition):
         .....:     for key in d.keys():
         .....:         if key < guard_condition:
         .....:             del d[key]
         .....:
      
      In [150]: %timeit del_iter(d, guard_condition)
      1000 loops, best of 3: 232 us per loop
      

      差异大约是 100 个循环 * 2.54 毫秒 = 254000 us 与 1000 个循环 * 232 us = 232000 us,对于我的情况来说可以忽略不计。

      我将使用字典理解,因为可读性很重要并且

      正如我所见,执行时间是小菜一碟,我同意 @Hyperboreus 关于过早优化的意见。

      【讨论】:

      • 您的时间显示 dict 理解比 del_iter()10 倍。巨大的差异可能是由于del_iter() 修改了d 字典就地(1 次迭代删除键,999 次迭代而不删除任何内容)。
      猜你喜欢
      • 1970-01-01
      • 2019-11-18
      • 2014-09-02
      • 2012-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多