玛丽莎·特里后缀压缩?
marisa trie suffix compression?
我正在使用这个marisa trie库的自定义Cython包装器作为键值多重映射。
我的trie条目看起来像key 0xff data1 0xff data2
将key
映射到元组(data1, data2)
。data1
是一个可变长度的字符串,但data2
始终是一个 4 字节的无符号整数。0xff
是一个分隔符字节。
我知道从理论的角度来看,trie 不是最优化的数据结构,但各种实际考虑使其成为最佳选择。
在这个用例中,我有大约 10-2000 万个密钥,每个密钥平均有 10 个数据点。data2
对于许多条目都是多余的(在某些情况下,对于给定键的所有数据点data2
总是相同的(,所以我想到了采用最频繁的data2
条目并为每个键添加一个("", base_data2)
数据点。
据我所知,由于 MARISA 尝试没有后缀压缩,并且对于给定的键,每个键data1
都是唯一的,因此我认为这将为每个使用冗余键的数据元组节省 4 个字节(加上为每个键添加一个 4 字节的"值"(。重建trie后,我检查了冗余数据是否不再存储。我预计序列化和内存大小都会大幅减少,但实际上磁盘上的 trie 从 566MB 增加到 557MB(加载的 trie 的 RAM 使用量也有类似的减少(。
由此我得出结论,我一定是错误的,没有后缀压缩。我现在将带有冗余data2
编号的条目存储为key 0xff data1 0xff
,因此为了测试这一理论,我删除了尾随0xff
并调整了使用 trie 来应对的代码。新的trie从557MB下降到535MB。
因此,删除单个冗余尾随字节比删除相同数量的4 字节序列有 2 倍的改进,因此后缀压缩理论要么是完全错误的,要么是以某种非常复杂的方式实现的。
我剩下的理论是,在trie的较高点添加("", base_data2)
条目以某种方式以某种可怕的方式抛弃压缩,但是当我从trie的较低位置删除了比这更多的字节时,它应该只是增加了4个字节。
我对修复并不乐观,但我非常想知道为什么我会看到这种行为!感谢您的关注。
正如我怀疑的那样,这是由填充引起的。
在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似乎没有应用任何压缩。
- C++中高效的大型稀疏块压缩线性方程
- 嵌入方指针压缩已禁用
- C++使用整数的压缩数组初始化对象
- 增量运算符与后缀混淆
- 在C++中将函数压缩为两种方式
- 在C++中使用LZ4压缩目录
- 使用C++进行运行长度解压缩
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 捕获标准输出以压缩并使用 CTRL-C 中断会给出损坏的 zip 文件
- C++ 如何将数组值解压缩为函数参数
- struct.error:解压缩 C++ 结构时,解包需要 288 字节的缓冲区
- 如何在OpenSSL库的名称中添加后缀'd'?
- 在 Qt(C++) 中使用 QProcess 解压缩 - 提取目录问题
- 浏览压缩文件与游览解压缩它们
- 直接 2D 呈现到命令列表和打印:图片压缩
- C++17 十六进制浮点文字单精度后缀冲突?
- 如何在 OpenCV c++ 中压缩 TIFF 格式的图像?
- 使用带有链表的堆栈数据结构将中缀转换为后缀
- 如何在C++向量中解压缩多个值
- 玛丽莎·特里后缀压缩?