作者: 姜逸坤 曹亚珍
Facebook的ZSTD压缩库从1.0版本发布的那天起,就引起了业界的关注,对比业界常用的压缩库lz4、zilib、xz,ZSTD更注重速度和压缩比的均衡,对比zlib来看,更是在保证压缩比的情况下,较zlib压缩性能提升6倍左右,解压性能提升2倍左右。
我们团队也在2020年年初时,对ZSTD压缩库进行了性能优化,最终优化已推入到Facebook的上游社区中,本文将详细的介绍我们进行的优化。
1. 利用neon指令集对数据复制优化。
完整的Patch链接:facebook/zstd#2041
优化思路:
aarch64提供了一系列的neon指令,本次优化则利用了VLD和VST指令,借助neon寄存器进行读写加速,ARM的官方文档是这样描述这两个指令的:
VLDn and VSTn (single n-element structure to one lane)
- Vector Load single n-element structure to one lane. It loads one n-element structure from memory into one or more NEON registers. Elements of the register that are not loaded are unaltered.
- Vector Store single n-element structure to one lane. It stores one n-element structure into memory from one or more NEON registers.
来自ARM的官方文档Coding for Neon - Part 1: Load and Stores中,写的非常详细,引用一张图来描述neon寄存器和memory加载和存储的方式,核心思想就是:利用neon寄存器作为暂存的中转站,加速数据处理:
我们以u8的复制为例,总结下本次我们在ZSTD具体的优化实现:
1 | static void ZSTD_copy8(void* dst, const void* src) { |
核心步骤包含两步:
- 将src利用vld1加载到neon寄存器。
- 使用vst1将neon寄存器的值store到dst的memory中。
这样便利用neon完成了对u8的memcpy的优化,对于此类优化,有兴趣的可以阅读What is the fastest way to copy memory on a Cortex-A8?,了解在Cortext-A8的架构下,如何快速的进行memory copy。
性能测试:
完成neon优化后,我们对压缩和解压缩都进行了测试,最终,在压缩场景获得了大概1+%的提升:
Average gains(level 1~19) | gcc9.2.0 | clang9.0.0 |
---|---|---|
Compression | 1.67% | 1.23% |
Decompression | 0.02% | 0.36% |
2. 使用prefetch机制加速数据读取。
完整的Patch链接:facebook/zstd#2040
优化思路:
Prefetch的中文是预取,原理是通过将数据预取到cache中,加速数据的访问。一个比较常见的场景就是在循环中,我们可以通过显示的调用,充分的预取未来将会访问的数据或指令便能快速从Cache中加载到处理器内部进行运算或者执行。
在Jeff Dean的一次经典的talk–Software Engineering Advice from
Building Large-Scale Distributed Systems中,提到了cache和memory的速度差异,大致如下图所示:
可以看到,从cache中拿数据,将比直接从memory拿数据性能提升几十甚至上百倍,因此,我们也在本次的优化中,为aarch64加入的预取指令。
1 |
同时,将预取加速加入到了ZSTD_compressBlock_fast_generic和ZSTD_compressBlock_doubleFast_generic的主循环中,在数据访问前,预先先将数据加载到cache中,从而加速后续访问对数据读取。
性能测试:
我们仅对压缩进行了优化,因此,也仅对压缩进行了测试,测试结果可以看出,速度在aarch64架构下获得了1.5-3+%的提升:
Average gains(level 1~19) | gcc9.2.0 | clang9.0.0 |
---|---|---|
level 1~2 | 3.10% | 3.69% |
level 3~4 | 2.49% | 1.51% |
3. 总结
在Facebook的ZSTD中,我们使用了neon指令集对memcpy的过程进行了加速,同时,也利用了prefetch机制,加速了循环时数据的访问。
希望本篇文章,能够对大家带来一些性能优化的启发。