簡體   English   中英

為什么將鍵按順序插入到python dict中比為無序編寫更快

[英]Why inserting keys in order into a python dict is faster than doint it unordered

我一直在創建巨大的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精確x86_64中使用Python 2.7.3

我幾乎可以肯定這是正在發生的事情:當你第一次創建otest ,字符串按順序存儲在內存中。 創建utest ,字符串指向相同的內存緩沖區,但現在這些位置無序,這會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 在我的機器上,這反轉了兩個測試的運行時間。

檢查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位作為初始表索引是非常快的,並且對於由連續的整數范圍索引的dicts,根本沒有沖突。 當鍵是“連續”字符串時,大致相同。 因此,這在常見情況下提供了比隨機更好的行為,這是非常理想的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM