有啥比 dict() 更快的吗?

Posted

技术标签:

【中文标题】有啥比 dict() 更快的吗?【英文标题】:Is there anything faster than dict()?有什么比 dict() 更快的吗? 【发布时间】:2017-04-03 07:56:45 【问题描述】:

我需要一种更快的方式来存储和访问大约 3GB 的 k:v 对。其中k 是字符串或整数,vnp.array(),可以有不同的形状。

在存储和访问这样的表时,有没有比标准 python dict 更快的对象?例如,pandas.DataFrame?

据我所知,python dict 是一个非常快的哈希表实现。对于我的具体情况,还有什么比这更好的吗?

【问题讨论】:

如果你使用 Python 3.5 或更低版本,那么the dictionary built in in Python 3.6 is said to be 20-25% faster than the old dictionary builtin in Python 3.5。因此,使用最新的稳定版 Python 可能会获得更好的性能。 除非您的代码不执行任何其他操作,否则如果字典访问是您的瓶颈,我会感到非常惊讶。您是否有分析信息表明这是问题所在? 我认为 dict 相当快。与其寻找替代方案,不如考虑优化其余代码:) 如果您的用例涉及交换 - 如果您的数据结构大于可用 RAM - 那么会有更好的答案,但不清楚是否是这种情况。 @alec_djinn:如果你的代码只在字典上循环,很容易让它更快——删除循环!但是如果你的代码在循环inside中做了一些事情(比如打印,或者找到值的最大值,或者pass以外的任何东西),那么如果这比字典访问花费的时间更长(而且几乎当然会),改进 dict 访问根本不会提高您的网络性能。在这一点上,如果你想要任何真正的建议,你将不得不展示一些代码。 【参考方案1】:

不,对于这个任务,没有什么比字典更快的了,这是因为它的索引(获取和设置项目)甚至成员检查的复杂性平均为 O(1)。 (检查 Python 文档 https://wiki.python.org/moin/TimeComplexity 上其余功能的复杂性)

一旦您将项目保存在字典中,您就可以在恒定时间内访问它们,这意味着您的性能问题不太可能与字典索引有关。话虽如此,您仍然可以通过对对象及其类型进行一些更改来稍微加快这个过程,这可能会导致一些底层操作的优化。

例如如果您的字符串(键)不是很大,您可以实习查找键和字典的键。实习是将对象缓存在内存中——或者在 Python 中,“实习”字符串表——而不是将它们创建为单独的对象。

Python 在 sys 模块中提供了一个 intern() 函数,您可以使用它。

在“interned”字符串表中输入 string 并返回 interned 字符串 - 它是字符串本身或副本。实习字符串有助于在 字典查找...

上获得一点性能

还有……

如果字典中的键是实习的并且查找键是实习的,则键比较(在散列之后)可以通过指针比较来完成,而不是比较字符串值本身,从而减少对对象的访问时间。

这是一个例子:

In [49]: d = 'mystr'.format(i): i for i in range(30)

In [50]: %timeit d['mystr25']
10000000 loops, best of 3: 46.9 ns per loop

In [51]: d = sys.intern('mystr'.format(i)): i for i in range(30)

In [52]: %timeit d['mystr25']
10000000 loops, best of 3: 38.8 ns per loop

【讨论】:

虽然我认为 OP 正朝着错误的方向前进,但性能不仅仅是 big-O。 这就是我一直在寻找的技巧!它确实加快了我的代码百分之几。谢谢! 不错的答案,但我认为如果这些值也是实习字符串,而不是整数,这个例子会更清楚(但我想它可以工作,因为整数作为一个实现细节被实习在这个范围内)跨度> @Chris_Rands 确实如此。它们小于 256 ;-) 没有什么比 dict 查找更快的原因不是 dict 查找大约是 O(1)。原因是 Python 中的 dict 查找在 C 中有效实现,并且多年来一直在优化。这并不排除可能有更有效的实现。它也不排除使用散列以外的其他方法可能会获得更好的性能,尤其是在键是整数的情况下。【参考方案2】:

不,我认为没有比dict 更快的了。其索引检查的时间复杂度为O(1)

-------------------------------------------------------
Operation    |  Average Case  | Amortized Worst Case  |
-------------------------------------------------------
Copy[2]      |    O(n)        |       O(n)            | 
Get Item     |    O(1)        |       O(n)            | 
Set Item[1]  |    O(1)        |       O(n)            | 
Delete Item  |    O(1)        |       O(n)            | 
Iteration[2] |    O(n)        |       O(n)            | 
-------------------------------------------------------

PShttps://wiki.python.org/moin/TimeComplexity

【讨论】:

【参考方案3】:

numpy.array[] 和简单的 dict = 比较:

import numpy
from timeit import default_timer as timer

my_array = numpy.ones([400,400])

def read_out_array_values():
    cumsum = 0
    for i in range(400):
        for j in range(400):
            cumsum += my_array[i,j]


start = timer()
read_out_array_values()
end = timer()
print("Time for array calculations:" + str(end - start))


my_dict = 
for i in range(400):
    for j in range(400):
        my_dict[i,j] = 1

def read_out_dict_values():
    cumsum = 0
    for i in range(400):
        for j in range(400):
            cumsum += my_dict[i,j]
    
start = timer()
read_out_dict_values()
end = timer()
print("Time for dict calculations:" + str(end - start))

打印:

Time for dict calculations:0.046898419999999996
Time for array calculations:0.07558204099999999
============= RESTART: C:/Users/user/Desktop/dict-vs-numpyarray.py =============
Time for array calculations:0.07849989000000002
Time for dict calculations:0.047769446000000104

【讨论】:

【参考方案4】:

人们会认为数组索引比哈希查找更快。

如果我们可以将这些数据存储在一个 numpy 数组中,并假设键不是字符串,而是数字,那会比 python 字典更快吗?

不幸的是,NumPy 是针对向量运算进行优化的,而不是针对单个值的查找。 熊猫的情况更糟。 在这里查看实验:https://nbviewer.jupyter.org/github/annotation/text-fabric/blob/master/test/pandas/pandas.ipynb

另一个候选对象可能是数组模块中的 Python 数组。但这不适用于可变大小的值。 为了使这项工作正常进行,您可能需要将其包装到一些纯 Python 代码中,这将阻碍数组提供的所有时间性能提升。

所以,即使对 OP 的要求放宽了,似乎仍然没有比字典更快的选择。

【讨论】:

【参考方案5】:

如果你的键是字符串,你可以考虑将它们存储在像 Trie 这样的数据结构中。即使要从 Trie 存储和检索,您也需要 O(N),其中 N 是密钥的最大长度。计算密钥哈希的哈希计算也是如此。 Hash用于在Hash Table中查找和存储。我们通常不考虑散列时间或计算。

你可以试试 Trie,这应该是几乎相同的性能,可能会快一点(如果哈希值的计算方式不同的话

HASH[i] = (HASH[i-1] + key[i-1]*256^i % BUCKET_SIZE ) % BUCKET_SIZE 

或类似的碰撞,我们需要使用 256^i。

您可以尝试将它们存储在 Trie 中,看看它是如何执行的。

【讨论】:

第一句话有点误导。 Big-O 符号通常与速度无关,它与输入大小变化时计算时间如何变化有关。因此,O(1) 和 O(n!) 一样可能非常慢。 是的,但这并不意味着它会fastfaster 比 O(anything) 甚至 最快 i> 在所有可能的Os 中。这是关于输入大小变化时工作量如何变化 ...也就是说:在现实世界中,常数因素很重要。与每个输入项花费 1ns 的 O(n) 过程相比,花费恒定时间 1000 毫秒的 O(1) 过程通常是更糟糕的选择。 O(1) 表示“恒定时间”,即“无论输入什么,计算时间都是一样的”。如果我们只检查所有可能性,数独求解恒定时间,但它比许多算法慢得多。

以上是关于有啥比 dict() 更快的吗?的主要内容,如果未能解决你的问题,请参考以下文章

这段代码有啥比 boost mutex 更快的吗?

有啥比 document.execCommand 更好的吗?

在 css 中有啥比 !important 更重要的吗?

有啥比 CORBA 更适合跨语言程序通信的吗?

从命令行向 python 程序传递参数,有啥比 sys.argv 更好的吗? [复制]

Python 3字典迭代中的性能:dict [key] vs. dict.items()