我首先将单个大文件拆分为 65536 个较小的文件,这样如果哈希以0000
它在文件中00/00data.txt
开头,如果哈希以0001
它在文件中开头00/01data.txt
,等等。如果完整文件是 12 GiB,那么每个较小的文件(平均)为 208 KiB。
接下来,将哈希与字符串分开;这样您就有 65536 个“哈希文件”和 65536 个“字符串文件”。每个散列文件将包含散列的其余部分(仅最后 12 位,因为不再需要前 4 位)和相应字符串文件中字符串的偏移量。这意味着(而不是平均每个 208 KiB 的 65536 个文件)您将拥有 65536 个哈希文件,每个文件可能 120 KiB 和 65536 个字符串文件,每个文件可能 100 KiB。
接下来,哈希文件应该是二进制格式。12 个十六进制数字需要 48 位(不是 12*8=96 位)。仅此一项就会将哈希文件的大小减半。如果字符串在字符串文件中的 4 字节边界上对齐,那么 16 位“字符串偏移量 / 4”就可以了(只要字符串文件小于 256 KiB)。哈希文件中的条目应按顺序排序,对应的字符串文件应按相同顺序排列。
在所有这些变化之后;您将使用哈希的最高 16 位来找到正确的哈希文件,加载哈希文件并进行二进制搜索。然后(如果找到)您将从哈希文件中的条目中获取字符串开头的偏移量(在字符串文件中),并从哈希文件中的下一个条目中获取下一个字符串的偏移量。然后,您将从字符串文件中加载数据,从正确字符串的开头开始,到下一个字符串的开头结束。
最后,您将在内存中实现“哈希文件缓存”。如果您的应用程序可以分配 1.5 GiB 的 RAM,那么这足以缓存一半的哈希文件。在这种情况下(缓存了一半的哈希文件),您预计一半时间您需要从磁盘加载的唯一内容是字符串本身(例如可能小于 20 个字节),而另一半时间您需要需要先将哈希文件加载到缓存中(例如 60 KiB);所以平均每次查找你会从磁盘加载大约 30 KiB。当然内存越多越好(越少越好);如果您可以分配超过 3 GiB 的 RAM,您可以缓存所有哈希文件并开始考虑缓存一些字符串。
一种更快的方法是使用可逆编码,这样您就可以将字符串转换为整数,然后将整数转换回原始字符串,而无需进行任何类型的查找。举个例子;如果您的所有字符串都使用小写 ASCII 字母并且是最大值。13 个字符长,那么它们都可以转换为 64 位整数并返回(如 26^13 < 2^63)。这可能会导致一种不同的方法——例如,在可能的情况下使用可逆编码(整数/散列的第 64 位清除);并且仅对无法以可逆方式编码的字符串使用某种查找(使用整数/哈希集的第 64 位)。只需一点知识(例如,为您的字符串仔细选择最佳可逆编码),这可以将您的 13 GiB 文件的大小缩减到“小到足以轻松放入 RAM”