让压缩库ZSTD在aarch64更顺滑

作者: 姜逸坤 曹亚珍

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寄存器作为暂存的中转站,加速数据处理
image

我们以u8的复制为例,总结下本次我们在ZSTD具体的优化实现:

1
2
3
4
5
6
7
static void ZSTD_copy8(void* dst, const void* src) {
#ifdef __aarch64__
vst1_u8((uint8_t*)dst, vld1_u8((const uint8_t*)src));
#else
memcpy(dst, src, 8);
#endif
}

核心步骤包含两步:

  1. 将src利用vld1加载到neon寄存器。
  2. 使用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的速度差异,大致如下图所示:
image

可以看到,从cache中拿数据,将比直接从memory拿数据性能提升几十甚至上百倍,因此,我们也在本次的优化中,为aarch64加入的预取指令

1
2
#define PREFETCH_L1(ptr)  __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr)))
#define PREFETCH_L2(ptr) __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr)))

同时,将预取加速加入到了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机制,加速了循环时数据的访问。

希望本篇文章,能够对大家带来一些性能优化的启发。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×