同步性能优化(五)
细心的朋友可能有注意到,近两个月,ckb 的 release 列表中,多了一个 ckb-async-download-rc
,它分发了与正常版本相同的一系列 ckb 版本,但我估计,应该很少人尝试使用该版本替换正常 release 使用,但近期,该分支将会在适当的时候合并进入 ckb 主分支,这是一个比较大的变化,也是 ckb 同步优化的又一次升级。
同步的变化
关于 ckb 的同步优化,在之前的一系列文章中,已经有所描述,但那已经是四年前的事情了,并且,在四年前,也有一些继续优化的思路,但由于各种各样的原因,并未展开,直到今年初,我们终于捡起了之前的思路,尝试进行实现,在验证过几个月之后,近期将会合入主支,大概能带来 25%-30% 的提速。
异步化验证和下载
这是核心修改,ckb 的下载验证是在之前,是同步的,所谓同步就是,block A 在到达节点后,只有当 block A 中所有交易验证通过,才会处理其他 block 的网络消息,这意味着,block 的下载会被本地验证功能所阻塞,无法达到最高的带宽利用率,进而降低了同步速度。而在 ckb 的整个同步过程中,验证是最为耗时的步骤,随着 ckb 链使用量的增加,它的耗时将会以线性的量级逐步提升,如何降低或者充分利用这部分耗时就是提升同步速度的关键了。
所谓异步化验证和下载,就是将上面说到的串行验证和下载的行为修改掉,让验证块的有效性不影响块的下载,那么这里需要考虑另一个问题,下载速度必然大于验证速度,如果将下载的块都放在内存中,大部分机器都将无法接受这个方案,而将 block 不验证存盘,可能会导致一些虚假数据攻击,我们在这个分支上做了一些处理,即乐观认为经过 pow 校验的 block 大概率是没问题的,先存盘,但标记为未验证,然后让 block 的 tansaction 验证异步进行,当验证通过后,标记为已验证,而未验证通过的 block 及其后续一系列的 block 都将被标记为 invalid,予以删除处理。
异步化验证和下载之后,之前优化过程中提到的 Download scheduler 功能也变得更精准了,因为它记录的时间只剩下了下载时间,而不是之前的 下载 + 验证时间。
优化 Orphan pool
由于下载近乎于无阻塞了,那么它可能带来的 orphan block 将会变得更多,对内存压力也相应变得更大,我们对 Orphan pool 做了一些调整,将原本需要完全存在内存中的数据修改成了只存 block hash + number,真正的 block 将会直接落盘。
IBD concurrent download
在之前的优化中,使用了 locator 去确认对端的 header,然后开启同步节点的同步下载,在 PR 的当时,是没什么大问题的,但四年过去了,locator 由于是指数级取值,定位对端 header 的能力在链长度变长之后,变得越来越鶸了,会导致整个 IBD 期间,同步下载的间距变得很大,导致真正同步下载的时间变得很小,locator 的意义变得很小了,我们将考虑用另一种方式去开启同步下载。
虽然 IBD 期间只与一个远端节点进行 header sync,但我们可以尝试将得到的 best known 发往连接的其他 outbound 节点,如果返回的 header 与 header sync 的节点一致,则能肯定的是,两个节点在当前 best known 上并没有分叉,即这个节点可以作为下载源进行下载请求。这样就达到了确认对端 header 的目的,即能更快地开启并行下载,并且尽可能保证整个 IBD 期间,并行下载一直进行。
不过当前分支中,这个优化还未实现,只是一个可行的计划,也正在尝试中。
小结
这篇内容本应该在更早的时候放出来,这些优化我参与了部分开发和 review 工作,这也是我今年开年几个月的主要工作之一。如果能早放出来,也许社区的部分人员能够更早尝试使用这个分支进行测试和反馈,而不是等到真正快要合入主分支的时候才开始了解到这些内容,只能说,下次注意,尽可能早分享这些内容。