HDiffPatch:一个基于字节的流式diff算法

一个基于字节的流式diff算法

作者: housisong@hotmail.com 2022.09.14
tag:补丁,patch,补丁算法,diff,hdiffpatch

本文是对我开源的 HDiffPatch库 中的-s模式diff算法的思路介绍;
阅读前可以先看看我以前的2篇文章:开源我的基于字节的数据补丁算法库HDiffPatch一个高效的二进制数据补丁算法

新的需求

随着app的大小以GB为单位地不断增长,HDiffPatch -m模式(逐字节匹配)的diff算法在内存占用和速度上都很难让人满意。
需要一种资源占用更小并且速度更快的补丁创建算法。

算法抽象

约束模型:假设old和new数据都无法全部载入内存,而现在需要找到old中与new相同的那些数据区(即-m模式的覆盖线)。
这个约束和常见的2台PC之间的数据同步算法要解决的问题非常一致。
那就以rsync算法为基础思路来实现。

算法基础实现

将old数据按参数设定的blockSize大小分成块,计算存储每个块的adler32校验值;只需要依次遍历old就可以了,不需要加载全部old的数据。
对new数据从头至尾用Rolling Hash的方式依次计算出当前位置的校验值,去匹配old块的校验值。 roll-hash计算方法:首先计算new开头位置的blockSize大小的数据的adler32值;然后利用rolling原理,根据当前位置的校验值,继续添加后面的一个字节数据并移除最前面的一个字节,用O(1)时间就可以计算出new从下一个位置开始blockSize大小的数据的adler32值。
可以将old的块adler32校验值储存在一个hash表中,key为old块adler32值,value为old块序号index; new的当前roll-adler32值在hash表中查找匹配,adler32值相等的情况下,再进一步验证old块和new当前的数据是否真的相等。 在blockSize大小的数据相等的情况下,可以尝试前后延长该覆盖线的长度。
找到所有覆盖线后,-s模式后续的处理就和-m模式一致了,所以2种模式可以输出相同格式的补丁。

算法实现和优化

old和new数据不能全部加载到内存,那就抽象成流;每次都只加载少量数据到内存,利用一些缓存来减低磁盘访问量;根据old和new数据访问的场景不同实施不同的缓存策略。
adler32的hash冲突很严重,距离理论最小冲突(1/2^32)差很多,我实验得到的有效位数只有约29bit;而每次错误匹配的速度惩罚却很严重。而改用adler64效果也不是太好(我实验得到的有效位数只有约30bit) 。 所以就自己尝试改写出了一个新的roll-hash算法,库中的fadler32和fadler64,执行速度更快(移除了一个求余运算,增加了一个查表运算),而且从我的实验来看实际冲突接近于理论最优值:fadler32有效位数约32bit,而fadler64有效位数约63bit。改用fadler64后,无效的hash冲突几乎降低到了理论最低值。
hash表的匹配也不够快,创建hash表的内存占用也不太可控;所以进行了替换:将old的块fadler64值(和其index)放在一个数组中,进行排序,然后用二分法查询;但这样fadler64值查找速度会从hash表的平均O(1)变成O(log(n)),所以在该查询之前再用一个bloom过滤器来预先降低大量的无效匹配。 这样处理后内存占用固定(即2个数组),查询匹配速度也更快。
单个fadler64的匹配和处理效率还不够,可以将fadler64值按块顺序看成“字符串”进行字符串的查找和匹配。 对old的块fadler64值(和其index)的数组进行排序的时候,看作字符串(下一个“字符”为index+1块的fadler64值,即后缀数组)进行排序,匹配的时候也将new的当前和之后多个连续块的fadler64值进行类字符串的搜索匹配(当前值已经有了,需要的下一个“字符”值可以在前面已经匹配的情况下才按需依次计算),从而一次找出尽可能长的连续匹配块。

另外:可以利用块匹配来优化-m模式的速度和内存占用(即-m模式时添加的-block参数);在执行-m匹配之前,先用快速的较大的块匹配寻找出比较长的覆盖线;然后移除old和new中这部分已经匹配的数据,剩余数据再执行较慢的-m匹配,寻找到的覆盖线需要注意不能跨越删除线;合并2次匹配得到的覆盖线。

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值