首先,让我们看一下为什么这么大.
每个都有32个字节.这意味着以二进制形式存储在例如字节或字节数组对象的存储中大约需要32个字节.到现在为止还挺好.
但是所有Python对象都有标头,通常为24-64个字节.通过快速检查,看起来字节对象在32位(可能加上对齐填充)上占用了额外的36个字节,在64位上占用了48个字节(至少在我检查的两个CPython版本上).
因此,您如何摆脱那150%的额外存储空间?将字节打包成一个巨型数组,例如字节或字节数组.然后,您总共有48个字节加上每个哈希32个,而不是每个哈希48 32个.当您需要访问散列时,如果您有索引,则它只是切片[index * 32:(index 1)* 32].
另外,根据创建字节的方式,可能会有一些溢出斜率.您可以检查-sys.getsizeof(s)-sys.getsizeof(b”)>镜头,您需要对所有对象进行切片以创建新副本,而无需额外填充.
无论如何,现在您有8M的额外索引.如果这些是瞬态的,那很好,但是如果您将它们作为int存储在dict值槽中,那么它们中的每个也都有一个标头.通过快速测试,在实际存储的4个字节之上(对于小于1≤31的int),在32位和64位中都有一个24字节的标头(尽管非常小的int显然可以塞入标头).因此,所有这些操作将您的48个字节的浪费减少到28个字节,这不是很好.
您可以使用某种形式的打包存储,例如array模块.我的数组类型每个整数仅使用4个字节.但是然后您需要将索引插入数组,这就是您刚解决的相同问题.
但是,您甚至根本不需要索引-如果将密钥本身存储在数组中,则任何密钥的索引已经是字节字符串(除以32)中哈希的索引,对吗?
仅当您可以将密钥存储在某种紧凑数组中时,此方法才有效.如果它们大小都相同,则可以再次使用相同的“ giantbytestring”技巧来实现.在您的情况下,它们就是-密钥也是32字节的哈希.因此,您只需要使两个巨型字节字符串都按键值排序即可(请参见bisect模块,因此您不必自己编写该代码).
当然,使用二进制搜索算法而不是哈希意味着您要进行查找并插入对数而不是常量.而且,虽然log(8M)仅为16左右,比8M好很多,但仍然是1的16倍.但这实际上是从理想调优的关系数据库中获得的结果,除了不需要进行任何调整,并且全部都在内存中,并且没有任何额外的开销,因此必须对您到目前为止进行的尝试进行改进.
您当然可以在Python中使用两个巨型字节数组作为存储,并使用两个array(‘I’)作为索引来构建自定义哈希表.但这还有很多工作要做,所以我首先尝试简单的方法.