1. 背景介绍
1.1 问题&思考
由于业务长期在x86架构下进行软件开发,业务侧对x86架构的性能优化有较多经验积累,代码在x86指令集下运行性能良好,当业务由x86切倚天710后,部分热点接口如何与倚天710的硬件能力深度融合、提升热点接口性能来促成业务软件更好的运行在倚天710平台,是当前面临的一个挑战;
业务对常见热点接口的优化诉求强烈,但是对全量的库升级如(glibc版本升级)保持慎重,我们交流过较多的业务,他们当前的glibc常见接口版本为glibc 2.32,甚至部分还是用glibc 2.17,基于此判断,我们把业务常用的glibc常见接口优化也归划进来,让用户不需要升级glibc版本,就能享受到比最新glibc版本性能更优的接口性能。
1.2 策略
ptg-optimize为平头哥数据中心解决方案团队开发的一款高性能库,该高性能库借助倚天的硬件架构优势,通过算法设计优化、SIMD向量化、指令优化等,实现包括mem、string、crc32、crypto、spinlock、等20+常用接口的性能优化,mem/string/spinlock等常用接口对标glibc 2.32,crc32/crypto等接口对标业务常用标准化接口。经测试,当前版本优化接口平均性能提升超过50%,性能优化效果显著,集成使用便捷,已为包括集团ebs存储、tair、rds数据库等多种场景提供应用性能加速。
2. 高性能库系统框图
如上为高性能库系统框图,绿色部分为已经实现的接口,红色部分为规划待实现的接口,已实现包括glibc 2.32中常见的mem、string等接口10+个,以及CRC32等业务侧常用的标准化接口4+个,crc32_with_memcpy等业务侧定制化接口2+个(业务热点中对同一串inputString先做copy,随后做crc32,平头哥数据中心解决方案团队通过定制优化,将crc32与copy整合在同一个接口中,来实现性能的大幅提升),crypto加解密SM4、AES相关的接口6+个,锁相关的接口6+个。后续我们将继续补齐glibc 2.32常见接口、hash常见接口的优化。
3. 优化接口列表
优化接口包括如下,后续我们会持续迭代,新增优化接口,如果业务侧在上倚天710过程中有函数热点需要优化,欢迎联系我们,我们将评估需求并规划进版本迭代中:
voidptg_memcpy_neon(voiddst, constvoid*src, size_tn); voidptg_memcpy_less32(voiddst, constvoid*src, size_tn); voidptg_memcpy_crc32c(voiddst, constvoidsrc, size_tn, uint32_tcrc); intptg_spin_lock(pthread_spinlock_t*lock); intptg_spin_unlock(pthread_spinlock_t*lock); intptg_spin_lock_cas(pthread_spinlock_t*lock); intptg_spin_unlock_cas(pthread_spinlock_t*lock); boolptg_atomic32_compare_exchange_n(volatileuint32_tptr, uint32_texpected, uint32_tdesired); boolptg_atomic64_compare_exchange_n(volatileuint64_tptr, uint64_texpected, uint64_tdesired); boolptg_atomic32_ldst_compare_exchange_n(volatileuint32_tptr, uint32_texpected, uint32_tdesired); boolptg_atomic64_ldst_compare_exchange_n(volatileuint64_tptr, uint64_texpected, uint64_tdesired); /*Polynomial 0x04C11DB7*/uint32_tptg_crc32(uint32_tcrc, constunsignedchar*data, size_tlen); uint16_tptg_crc16(uint32_tcrc, constunsignedchar*data, size_tlen); /*Polynomial 0x1EDC6F41*/uint32_tptg_crc32c(uint32_tcrc, constunsignedchar*data, size_tlen); uint32_tptg_crc32c_with_memcpy(uint32_tcrc, constunsignedchardata, size_tlen, voiddst); uint16_tptg_crc16c(uint32_tcrc, constunsignedchar*data, size_tlen); intptg_memcmp_len20(constvoids1, constvoids2, size_tn); intptg_memcmp(constvoids1, constvoids2, size_tn); voidptg_memchr(constvoidbuf, charch, unsignedcount); voidptg_memrchr(constvoidbuf, charch, unsignedcount); voidptg_memset(voids, intc, size_tn); charptg_strchr(constchars, intc); charptg_strchrnul(charstr,intc); intptg_strcmp(constchars1, constchars2); externLIBOPTIMIZE_APIintneon_sm4_ctr_encrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_sm4_ctr_decrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_sm4_cbc_encrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_sm4_cbc_decrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_aes_xts_encrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_aes_xts_decrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_aes_ctr_encrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]); externLIBOPTIMIZE_APIintneon_aes_ctr_decrypt(unsignedchar*input, intinlen, unsignedcharoutput, intoutlen, unsignedchar*key, intkeylen, unsignedchariv[16]);
4. 如何优化
倚天710基于armv9指令集架构,有如crc、aes、bf16、sve/neon等特性能力,L1、L2 i/dcache等均相比icelake有较大优势,具体特性如下图所示:
我们团队通过对倚天710微架构特性的剖析,基于top-down模型视角,在微指令融合、热点指令重排、访存预取、SIMD向量化(sve/neon)、循环展开、pmul+eor+crc32指令流取代crc32单指令来提升后端ALU吞吐等一些列手段,系统性优化接口性能,使常见接口在倚天710上高效运行。
由于接口较多,我仅选取如下5个接口来说明我们的优化思路及方法,如果读者对其他接口的优化也感兴趣,欢迎钉钉联系我们:
4.1 spinlock接口优化
自旋锁为glibc 2.32中提供了标准接口,该接口为多线程业务提供共享临界区的保护,倚天710单片128core,自旋锁通过对锁的原子操作实现,通过EMSI C2C协议保证数据的有效性,当同一时刻对同一cacheline的monitor监听core数增加时,即同一时刻较多线程在争抢同一个原子变量,会导致系统整体的吞吐性能下降:
为了提升多线程抢锁的性能,我们在自旋锁的内部实现上引入了casal以及随机散列算法,来降低同一时刻线程争抢同一原子操作的次数,极大提升了系统的并发性能,在抢锁位置插入随机打散算法,通过争抢失败的次数来作为随机生成变量来等待N个wfe:
测试多线程抢锁数据如下,结果表明,线程越多、争抢越激烈的场景,性能提升越多,提升2-20倍性能;
4.2 crc32接口优化
crc32为各个业务常用的标准接口,当前标准接口通过集成通用的crc32指令,可以达到较高的性能收益,但是每个core的crc32硬件引擎只有一个,后端backend成为影响整体吞吐的瓶颈点,我们通过引入eor以及pmul指令来计算crc32,提升了后端ALU引擎的并发使用度,达到了较高的性能收益:
相比标准使用crc32指令的接口,提升后端吞吐后的性能平均提升35%+,如下图所示,对crc32重载的业务有相当大的收益,当前已落地ebs存储业务;
4.3 sm4国密接口优化
SM4-CTR算法是一种基于SM4分组加密和CTR模式的对称加密算法,具有高效和安全性等优点,因为算法使用的是块加密的CTR模式,可以将固定长度的明文块转换成密文块,CTR模式具有并行处理能力,因此在实际应用中适合大规模数据的加密解密。其加密流程如下:
1. 初始化:选择一个固定长度的计数器nonce和密钥K,并从初始值开始,按照CTR模式进行初始化。
2. 生成密钥流:使用SM4算法加密计数器nonce,产生M-bit的密钥流Keystream(M为SM4短块长度)。
3. 加密:将Keystream和明文异或,生成密文Ciphertext,并更新计数器nonce的值。
4. 重复执行步骤2和3,直至所有明文块加密完成。
在2,3步骤中,在liboptimize库中我们使用armv8 sm4e进行加速,并通过.inst指令转换为机器码,进一步缩减指令数;
优化前后性能数据对比如下图所示,其中rds数据库集成该优化库算法后,端到端提升了加解密场景性能20%,当前已跟rds数据库完成联调测试;
4.4 memcpy接口优化
memcpy为各个业务常调用的接口,我们借助sve及neon向量化以及部分指令重排、
cacheline预取,相比glibc 2.32中的memcpy,极大提升了在256Bytes以上的性能;
优化后接口相比glibc 2.32平均提升45%,当前已落地到存储ebs业务中,并与tair业务进行联调中:
4.5 ptg_crc32c_with_memcpy接口优化
除了优化标准化接口,我们还会根据业务的实际做定制化优化,如ebs存储业务,他们业务中对crc32的计算负载较大,同时,用来做crc32计算的inputString需要提前做memcpy来获取,基于此场景,我们跟业务方对齐思路,可以在memcpy inputString过程中,就把这串inputString的crc32一并计算出来,可以极大的降低访存延迟:
优化后接口相比客户原先使用crc32+memcpy两次调用方式,可以提升100%的性能,当前已落地至ebs存储业务中: