倚天性能优化—ptg-optimize高性能基础库

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: ptg-optimize为平头哥数据中心解决方案团队开发的一款高性能库,该高性能库借助倚天的硬件架构优势,通过算法设计优化、SIMD向量化、指令优化等,实现包括mem、string、crc32、crypto、spinlock、等20+常用接口的性能优化,mem/string/spinlock等常用接口对标glibc 2.32,crc32/crypto等接口对标业务常用标准化接口。经测试,当前版本优化接口平均性能提升超过50%,性能优化效果显著,集成使用便捷,已为包括集团ebs存储、tair、rds数据库等多种场景提供应用性能加速。

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过程中有函数热点需要优化,欢迎联系我们,我们将评估需求并规划进版本迭代中:

#define LIBOPTIMIZE_API attribute((visibility("default")))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存储业务中:




相关文章
|
9月前
|
机器学习/深度学习 人工智能 TensorFlow
倚天产品介绍|倚天性能优化—YCL AI计算库在resnet50上的优化
Yitian710 作为平头哥第一代ARM通用芯片,在AI场景与X86相比,软件生态与推理性能都存在一定的短板,本文旨在通过倚天AI计算库的优化,打造适合ARM架构的软件平台,提升倚天性能
|
数据中心 Anolis
性能优化特性之:LSE指令集编译优化
本文介绍了倚天实例上的编译优化特性:LSE,并从优化原理、使用方法进行了详细阐述。
|
缓存 算法 大数据
倚天710规模化应用 - 性能优化 - 软件预取分析与优化实践
软件预取技术是编程者结合数据结构和算法知识,将访问内存的指令提前插入到程序,以此获得内存访取的最佳性能。然而,为了获取性能收益,预取数据与load加载数据,比依据指令时延调用减小cachemiss的收益更大。
|
9月前
|
存储 缓存 前端开发
性能优化:通用快照方案
性能优化对于提供卓越的用户体验至关重要,钉钉终端团队特别关注用户体验。我们团队采用了一系列创新的性能优化措施,显著提升了首次有意义绘制(FMP)和首次内容绘制(FCP)的性能指标。其中,利用快照方案,结合用户的本地存储能力,我们能够进一步提高页面性能。快照方案是在完成常规手段前端优化(如优化首屏加载体积、实施懒加载、渲染优化和缓存提升等)和资源离线处理之后的又一重要步骤,旨在更迅速地向用户展示页面内容。
152 1
|
9月前
|
存储 人工智能 弹性计算
倚天使用|倚天性能优化—YCL AI计算库在resnet50上的优化
本文介绍了x86软件迁移到Arm过程中可能遇到的弱内存序问题的解决方案,解析了弱内存序问题的根因,介绍了Hawkeyes的架构和实现原理。欢迎有需求的团队发送邮件咨询
|
存储 编译器 C语言
性能优化特性之:LTO
本文介绍了倚天实例上的编译优化特性:LTO,并从优化原理、使用方法进行了详细阐述。
|
存储 算法 大数据
倚天性能优化--基于倚天优化后的zstd在大数据场景应用:降低存储成本+提升重IO场景性能
倚天性能优化--基于倚天优化后的zstd在大数据场景应用:降低存储成本+提升重IO场景性能
|
编译器 C++ Anolis
性能优化特性之:PGO
本文介绍了倚天实例上的编译优化特性:PGO,并从优化原理、使用方法进行了详细阐述。
|
存储 缓存 NoSQL
性能优化方案及思考
周末闲暇在家,朋友让我帮忙优化一个接口,这个接口之前每次加载都需要40s左右,经过优化将性能提了10倍左右;又加了缓存直接接口响应目前为300ms左右,于是将自己的优化思路整理总结一下
|
弹性计算 Cloud Native Java