我一直在创建巨大的dicts(数百万条目),我注意到如果我用密钥创建它们的速度要快得多。
我认为它与哈希函数的冲突有关,但有人可以解释它为什么会发生以及它是否在python版本中是一致的?
这里有一个人为的例子:
import timeit
import random
def get_test_data(num, size):
olist, ulist = [], []
for _ in range(num):
otest = [str(i) for i in range(size)]
utest = list(otest)
random.shuffle(utest)
olist.append(otest)
ulist.append(utest)
return olist, ulist
NUM_TESTS = 20
# Precalculate the test data so we only measure dict creation time
ordered, unordered = get_test_data(NUM_TESTS, 1000000)
def test_ordered():
dict((k, k) for k in ordered.pop())
def test_unordered():
dict((k, k) for k in unordered.pop())
print "unordered: ",
print timeit.timeit("test_unordered()",
setup="from __main__ import test_unordered, test_ordered",
number=NUM_TESTS)
print "ordered: ",
print timeit.timeit("test_ordered()",
setup="from __main__ import test_unordered, test_ordered",
number=NUM_TESTS)
我机器的输出始终如下:
(X)$ python /tmp/test.py
unordered: 8.60760807991
ordered: 5.1214389801
我在ubuntu中使用Python 2.7.3精确x86_64
答案 0 :(得分:8)
我几乎可以肯定这是正在发生的事情:当你第一次创建otest
时,字符串将按顺序存储在内存中。创建utest
时,字符串指向相同的内存缓冲区,但现在这些位置无序,这会破坏无序测试用例的缓存性能。
这是证据。我用这个版本替换了你的get_test_data
函数:
def get_test_data(num, size):
olist, ulist = [], []
for _ in range(num):
nums = range(size)
random.shuffle(nums)
utest = [str(i) for i in nums]
otest = list(utest)
otest.sort(key=lambda x: int(x))
olist.append(otest)
ulist.append(utest)
return olist, ulist
我的想法是,我现在在内存中连续ulist
构建字符串,然后通过使用适当的键对这些字符串进行排序来构建olist
。在我的机器上,这会逆转两次测试的运行时间。
答案 1 :(得分:2)
检查source code of the python dict您可以看到连续的字符串或整数会减少冲突。这与@skishore关于更好的缓存区域性的评论结合起来可能就是答案。
未来的主要微妙之处:大多数哈希计划依赖于“好” 哈希函数,在模拟随机性的意义上。 Python没有: 它最重要的哈希函数(对于字符串和整数)非常有用 常见的常见情况:
>>> map(hash, (0, 1, 2, 3)) [0, 1, 2, 3] >>> map(hash, ("namea", "nameb", "namec", "named")) [-1658398457, -1658398460, -1658398459, -1658398462] >>>
这不一定是坏事!相反,在一张2 ** i的表格中, 将低位i位作为初始表索引是极其重要的 快速,并且对于由a索引的dicts完全没有冲突 连续的整数范围。键是大致相同的 “连续”字符串。所以这给出了比随机更好的行为 常见的情况,这是非常可取的。