marisa trie后缀压缩?

时间:2017-07-03 23:46:47

标签: python c++ trie

我使用this marisa trie库的自定义Cython包装器作为键值多图。

我的条目似乎是key 0xff data1 0xff data2,以便将key映射到元组(data1, data2)data1是一个可变长度的字符串,但data2始终是一个4字节的无符号整数。 0xff是分隔符字节。

从理论角度来看,我知道trie不是最优的数据结构,但是各种实际考虑因素使它成为最佳选择。

在这个用例中,我有大约1000万到2000万个密钥,每个密钥平均有10个数据点。对于许多条目,data2是多余的(在某些情况下,data2对于给定密钥的所有数据点始终是相同的),因此我想到了最频繁的data2条目并为每个密钥添加("", base_data2)个数据点。

由于MARISA trie,据我所知,没有后缀压缩,并且对于给定的密钥,每个data1都是唯一的,我假设这将为使用冗余密钥的每个数据元组节省4个字节(加上在一个4字节"值"为每个键)。重建了trie后,我检查了冗余数据不再存储。我预计序列化和内存大小都会大幅减少,但实际上磁盘上的trie从566MB增加到557MB(并且加载的trie的RAM使用量也有类似的减少)。

由此我得出结论,没有后缀压缩我一定是错的。我现在将条目存储为data2个冗余key 0xff data1 0xff,因此为了测试这个理论,我删除了尾随0xff并调整了使用trie来处理的代码。新的trie从557MB下降到535MB。

因此删除单个冗余尾随字节比删除 4字节序列的相同数量大2倍,因此后缀压缩理论是错误的,或者它实现了以一种非常复杂的方式。

我的剩余理论是,在某个特定方式中添加("", base_data2)条目中某个更高点会以某种可怕的方式抛出压缩,但它应该只增加4个字节。删除了多少来自特里的低层。

我对修复并不乐观,但我非常想知道为什么我会看到这种行为!谢谢你的关注。

1 个答案:

答案 0 :(得分:5)

我怀疑,它是由填充造成的。

{p}在lib/marisa/grimoire/vector/vector.h中,有以下功能:

void write_(Writer &writer) const {
  writer.write((UInt64)total_size());
  writer.write(const_objs_, size_);
  writer.seek((8 - (total_size() % 8)) % 8);
}

关键点是:writer.seek((8 - (total_size() % 8)) % 8);。在写入每个块之后,写入器将填充到下一个8字节边界。

这解释了您所看到的行为,因为通过填充的初始缩短删除的数据的一部分被替换为填充。

当您删除额外字节时,它会使密钥大小低于下一个边界限制,从而导致主要大小更改。

实际上,这意味着,由于填充代码位于库的序列化部分,您可能获得了预期的内存节省,但这并没有转化为磁盘上的节省即可。监控程序RAM使用情况应该确认。

如果您关心磁盘大小,那么您可能只是简单地缩小序列化数据,因为看起来MARISA似乎不应用任何压缩。