大多数内存有效的方式来存储8M + sha256哈希

时间:2014-01-10 19:19:09

标签: python dictionary hash

我一直使用dict来存储键值对,其中键和值都是sha256哈希摘要。我需要能够找出列表中是否存在密钥,并且还能够检索该字典的值。

目前我估计我将需要大约10Gb的内存来存储基于我的一些测试的8,000,000个哈希,其中存储的实际数据只有大约512MB(每个哈希32个字节,因此每个记录64个字节)< / p>

有没有人有任何建议?

更新,基于我认为应该更新的一些评论。我将哈希值存储为字节,而不是十六进制字符串。我使用sqlite数据库来永久存储数据,但是在大约1,000,000条记录之后插入带有索引的那么多记录变得太慢,并且没有索引检查存在的键也会以指数方式变慢。这就是为什么我想使用内存结构来进行查找。

更新2

这可行吗? atbr hashtable

我的解决方案:(我应该把它作为答案吗?) 我最终做的是从@abarnert那里获取大量建议,创建一个实现1024个[count, bytearray(8000 * 32), bytearray(8000 *32)]

列表的新类

我使用散列的前10位作为索引到哪个列表我应该存储哈希。然后我只是将密钥附加到下一个32byte插槽,并将值附加到另一个字节数组中的相同插槽。

我可以生成16,000,000个哈希值(一个用于键,一个用于值),并在大约30秒内将8,000,000个键值对插入到结构中。

搜索正好相反,我使用前10位查找列表然后我只是对哈希进行线性搜索,直到找到它为止。

从8,000,000中随机搜索200,000个哈希需要30秒,所以比写作慢40倍,但它应该足够快以满足我的需求。

这一切的好处,它现在只消耗519MB RAM用于所有8,000,000个哈希。

谢谢大家的帮助。

6 个答案:

答案 0 :(得分:3)

首先,让我们看一下为什么这是如此之大。

每个都有32个字节。这意味着以二进制形式存储大约32个字节,例如,bytesbytearray对象的存储。到目前为止,非常好。

但是所有Python对象都有标题,通常大小为24-64字节。通过快速检查,看起来bytes个对象在32位上占用额外的36个字节(可能加上对齐填充),在64位上占用48个,至少在我检查的两个CPython版本上。

那么,你怎么能摆脱150%的额外存储?将字节打包成一个巨大的数组,如bytesbytearray。然后你有48个字节加上每个哈希32个,而不是每个哈希48 + 32个。当你需要访问哈希时,如果你有索引,那就是切片[index*32:(index+1)*32]

此外,根据您创建bytes的方式,可能会出现一些溢出问题。您可以检查 - 如果sys.getsizeof(s) - sys.getsizeof(b'') > len(s),您需要将所有对象切片以创建没有额外填充的新副本。

无论如何,现在你有8M额外的索引。如果这些是短暂的,那很好,但是如果你将他们存储在dict值槽中的int,那么每个都有一个头。从快速测试开始,在实际存储的4个字节之上(对于1 <&lt;&lt; 31以下的int),32位和64位都有一个24字节的标题(虽然非常小的整数显然可以塞进标题)。所以,所有这一切都将你的48个字节的浪费减少到28个字节,这不是很好。

您可以使用某种形式的打包存储,例如array模块。数组类型I每个整数仅使用4个字节。但是你需要在数组中使用索引,这就是你刚解决的问题。

但你真的甚至不需要索引 - 如果你把密钥本身存储在一个数组中,任何键的索引都已经是字节串中哈希的索引(除以32),对吧?

仅当您可以将密钥存储在某种紧凑的数组中时,这才有效。如果它们都大小相同,你可以通过再次使用相同的“giantbytestring”技巧来做到这一点。在你的情况下,他们是 - 键 32字节哈希。所以你必须保持两个巨大的字节字符串按键值排序(参见bisect模块,这样你就不必自己编写代码了。)

当然使用二进制搜索算法而不是散列意味着您正在进行查找并插入对数而不是常量。而且,虽然log(8M)只有16左右,比8M好很多,但它仍然是16倍。但这实际上是你从一个理想调整的关系数据库得到的,除了你不需要做任何调整,它都在内存中,并且没有额外的开销,所以它必须比你迄今为止所做的改进。

您当然可以在Python中构建自定义哈希表,使用两个巨型字节数组作为存储,并使用两个array('I')作为索引。但这是更多的工作,所以我先尝试一下这个简单的方法。

答案 1 :(得分:1)

使用sqlite3 library将哈希值存储在数据库中。 sqlite嵌入式数据库将使用内存缓冲和磁盘存储来为您完成内存管理,以满足您的查询。

一个非常简单的表就足够了:

import sqlite3

connection = sqlite3.connect('/tmp/hashes.db')
connection.execute('CREATE TABLE hashes (key UNIQUE, value)')

然后使用:

with connection:
    cursor = connection.cursor()
    sql = 'INSERT INTO hashes VALUES (?, ?)'
    cursor.executemany(sql, ((key1, hash1), (key2, hash2), ....))

您可以使用以下方式查询数据库:

with connection:
    cursor = connection.cursor()
    sql = 'SELECT hash FROM hashes WHERE key=?'
    cursor.execute(sql, (key,))
    hash = cursor.fetchone()
    if hash is not None:
        hash = hash[0]

答案 2 :(得分:1)

我分别写了一个更复杂的答案,因为我不确定这是否适合你,但是:

dbm是一个键值数据库,其工作方式几乎与dict(其键和值为bytes个字符串)完全相同,除了它支持磁盘和页面值并根据需要出去。

它比SQL数据库简单得多,它有三大优势。

  • 您无需将代码从hash = hashes[thingy]更改为hash = db.execute('SELECT * FROM Hashes WHERE Thingy = ?', (thingy,)).fetchone()[0],只需继续使用hashes[thingy]

  • 没有正确的索引 - 密钥哈希是数据库中一个表的唯一索引 - 没有其他优化要做。

  • 数据库的一大块将被缓存在内存中,使其快速。


尽管称为“Unix数据库”,但每个平台中至少存在一个dbm个模块系列。有关详细信息,请参阅dbm: not just for Unix

答案 3 :(得分:1)

如果您不想或不能使用外部数据库,您可以创建一个内存数据库,该数据库在内存使用中更接近信息理论最小值,同时速度极快。您需要使用比Python对象更低级别的工具。

您可以使用array.arraybytearray来存储密钥和值,而无需任何开销,这意味着8M条目适合488 MiB。然后你可以写一个哈希表。这样做相当不方便,因此您可能希望使用像cffi这样的外部库来处理紧密压缩的C结构和数组。

具有线性探测功能的简单开放式寻址哈希表可以很好地处理您的数据(将密钥的最低N位作为哈希值)并且实现起来并不太难,如果您不需要删除则更加容易。只需保持负载系数合理,在一半到三分之二之间。如果你想节省空间(每个空条目浪费半个千字节),请将键值对紧密地打包到数组中,并且只在哈希表中存储指针/索引。

答案 4 :(得分:0)

由于它是一个哈希,你可以使用seek() / tell()在文件中实现一个字典....你需要预先创建给定大小的文件(10GB或其他) )...

然后它就像任何其他哈希表一样。你必须自己处理碰撞,显然它会慢一点,因为它在文件中......

offset = dict_hash(in_key)
dict_file.seek(offset)
hashval = dict_file.read(hash_len)

类似的东西(用于在C中做这样的事情,没有在python中完成它,但底层文件支持是相同的......)

答案 5 :(得分:-1)

根本不需要存储哈希数据。您需要做的就是正确索引哈希值。

以下是哈希的索引函数

首先我们有这个快速索引哈希的功能。

ZequebaHashB[bvc_, yvc_, avc_] :=
 {Drop[Flatten[Reap[
  Module[{a34 = bvc, a35 = yvc, rt2 = avc, z75, zler},
    z75 = 1;
    zler = Total[BCC[Re[Floor[Px[a34, a35^2]]], a35^3]];
    Label[Start5629];
    Sow[Denominator[zler/FiveItt[a35, z75]], yvc];
    z75 = z75 + 1;
    If[z75 >= rt2 + 1, Goto[end5629]];
    Goto[Start5629];
    Label[end5629];
    ];]], 1], Total[BCC[Floor[Re[Px[bvc, yvc^2]]], yvc^3]], bvc};

其次我们有这个函数用于获取散列的彩虹索引

RainbowHashXG[zf_, xz_, zd_, fd_] := 
Column[{Table[
 Flatten[Drop[ZequebaHashB[zf + xf, xz, zd], -2]], {xf, 0, 
  fd - 1}], zf}];

现在当你尝试这些功能时

Table[ZequebaHashB[Hash[xu, "SHA512"], 2, 5], {xu, 1, 10}]

{{{1, 2, 3, 4, 5}, 427, 

12579926171497332473039920596952835386489858401292624452730263741969 \ 1347390182282976402981790496477460666208142347425205936701161323553455 \ 43156774710409041}, {{1,1,1,1,5},396,   378544712​​15291391986149267401049113295567628473597440675968265868739 \ 3920246834469920751231286910611366704757913119360843344094113813460828 \ 6029275267369625}, {{1,1,1,2,5},378,   71668700870008575285238318023246235316098096074289026150051114683524 \ 8893999285271969471146596174190457020264703584540790263678736452792747 \ 5984118971455163}, {{1,2,3,4,5},377,   33095966240281217830184164668404219514626500609945265788213543056523 \ 6612792119604718913684957565086394439681603253709963629672412822522528 \ 4694992131191098}, {{1,2,1,4,5},363,   86087420302049294430262146818103852368792727362988712093781053088200 \ 5531339261473092981846995901587757487311471069416835834626804973821926 \ 684090578667825}, {{1,1,3,2,5},374,   18586086601485268646467765285794047467027639305129763019055665664163 \ 2819380637531124748570695025942793945139516664108034654512831533948189 \ 743738184270224}, {{1,1,3,1,1},380,   72109882448403363840259529414390721196358024901859951350044294221621 \ 3409708767088486766304397692430037767785681544787701437132358156239382 \ 5256452011168475}, {{1,2,3,4,5},397,   22760214977694020069971224118591466739483553732805530503408373418535 \ 1711847169063849360187954434350675389187296376543635586233555068331343 \ 3001046271103001}, {{1,2,1,4,5},369,   11906459655144790308170064541982556680120578173098014909650827827844 \ 2313493552143468785692756291539132782149145837942478466345517803751070 \ 21641806135272354}, {{1,1,3,2,5},382,   88155955858214177781767282869972903505820511583564376117417944351446 \ 8458315518532665921338085983977628624644833036888032312932654944528755 \

  1. 5410805140620789}}

    Table[RainbowHashXG[Hash[xu, "SHA512"], 2, 5, 5], {xu, 1, 10}]
    

    {{{{1,1,1,1,5},{1,2,3,4,5},{1,1,3,2,5},{1,2,1,4} , 1},{1,1,3,1,5}},
    12579926171497332473039920596952835386489858401292624452730263741969 \ 1347390182282976402981790496477460666208142347425205936701161323553455 \ 43156774710409041},{{{1,2,1,4,5},{1,1,3,2,5},{1,2,3, 4,1},{1,1,1,1,5},{1,2,3,4,5}},
    378544712​​15291391986149267401049113295567628473597440675968265868739 \ 3920246834469920751231286910611366704757913119360843344094113813460828 \ 6029275267369625},{{{1,2,3,4,5},{1,1,1,1,5},{1,2,3,4, 5},{1,1,3,2,5},{1,2,1,4,1}},
    71668700870008575285238318023246235316098096074289026150051114683524 \ 8893999285271969471146596174190457020264703584540790263678736452792747 \ 5984118971455163},{{{1,2,3,4,5},{1,1,1,1,5},{1,2,3,4, 5},{1,1,3,2,1},{1,2,1,4,5}},
    33095966240281217830184164668404219514626500609945265788213543056523 \ 6612792119604718913684957565086394439681603253709963629672412822522528 \ 4694992131191098},{{{1,2,3,4,1},{1,1,3,1,5},{1,2,1,4, 5},{1,1,3,2,5},{1,1,3,1,5}},
    86087420302049294430262146818103852368792727362988712093781053088200 \ 5531339261473092981846995901587757487311471069416835834626804973821926 \ 684090578667825},{{{1,1,1,1,5},{1,2,3,4,5},{1,1,3,2, 5},{1,2,1,4,1},{1,1,3,1,5}},
    18586086601485268646467765285794047467027639305129763019055665664163 \ 2819380637531124748570695025942793945139516664108034654512831533948189 \ 743738184270224},{{{1,2,3,4,1},{1,1,1,2,5},{1,2,3,4, 5},{1,2,3,4,5},{1,1,3,2,5}},
    72109882448403363840259529414390721196358024901859951350044294221621 \ 3409708767088486766304397692430037767785681544787701437132358156239382 \ 5256452011168475},{{{1,1,3,1,5},{1,2,3,4,1},{1,1,1,2, 5},{1,2,3,4,5},{1,1,3,1,5}},
    22760214977694020069971224118591466739483553732805530503408373418535 \ 1711847169063849360187954434350675389187296376543635586233555068331343 \ 3001046271103001},{{{1,1,1,2,5},{1,2,3,4,5},{1,1,3,1, 1},{1,2,1,4,5},{1,2,1,4,1}},
    11906459655144790308170064541982556680120578173098014909650827827844 \ 2313493552143468785692756291539132782149145837942478466345517803751070 \ 21641806135272354},{{{1,2,1,4,5},{1,1,3,1,1},{1,2,3, 4,5},{1,1,1,2,5},{1,2,3,4,5}},
    88155955858214177781767282869972903505820511583564376117417944351446 \ 8458315518532665921338085983977628624644833036888032312932654944528755 \ 5410805140620789}}

    FiveItt[x98_, cc5_] :=
    DifferenceRoot[
    Function[{\[FormalY], \[FormalN]}, {-cc5 -
    cc5 \[FormalY][\[FormalN]] + \[FormalY][1 + \[FormalN]] == 0, \[FormalY][1] == 1, \[FormalY][2] == cc5}]][x98];
    

    BCC [x55_,g77_]:= Drop [Flatten [Reap [Module [{x45 = x55,z7 = 0,z8] = 0,z9,g7 = g77,bell},

    z7 =如果[x45 / FiveItt [Length [IntegerDigits [x45,g7]],g7]&lt; = 1,If [x45 == 1,1,长度[IntegerDigits [x45,g7]] - 1],长度[IntegerDigits [x45,g7]]]; bell = FiveItt [z7 - 1,g7]; z9 = g7 ^(z7 - 1);

    标签[SPo的]; z8 =如果[IntegerQ [x45 / g7]&amp;&amp; x45&gt; g7,商数[x45 - bell - (1 /(2 * g7)),z9],if [x45&lt; = g7,x45,Quotient [x45 - bell, Z9]]];母猪[Z8]; x45 = x45 - (z8 *(z9)); z7 = z7 - 1; z9 = z9 / g7;钟 =贝尔 - z9;

    如果[z7&lt; 1,转到[EnD],转到[SPo]];

    标签[END];

    ]]],1];

    Px =编译[{{x1d,_Complex},{si1d,_Real}},模块[{x1c = x1d, si1c = si1d},x1c + 1/2(Floor [Re [( - 4 + si1c + Sqrt [( - 4 + si1c)^ 2

    • 8(-2 + si1c)( - 1 + x1d)])/(2(-2 + si1c))]] + Floor [Im [( - 4 + si1c + Sqrt [( - 4 + si1c)] ^ 2 + 8(-2 + si1c)( - 1 + x1d)])/(2(-2 + si1c))] I)( - 4 + si1c - (-2 + si1c)(Floor [Re [( - 4 + si1c + Sqrt [( - 4 + si1c)^ 2 + 8(-2 + si1c)( - 1 + x1c)])/(2(-2 + si1c))]] + 地板[Im [( - 4 + si1c + Sqrt [( - 4 + si1c)^ 2 + 8(-2 + si1c)( - 1 + x1c)])/(2(-2 + si1c))]] I))],CompilationTarget - &gt; &#34; C&#34 ;, &#34; RuntimeOptions&#34; - &GT; &#34;速度&#34;];