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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 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存储业务中:




相关文章
|
1月前
|
前端开发 Nacos 微服务
开发指南049-优化性能
如果发现某个操作耗时比较长,一般的解决框架如下:
|
3月前
|
存储 算法 Cloud Native
【PolarDB-X列存魔法】揭秘TPC-H测试背后的性能优化秘籍!
【8月更文挑战第25天】阿里巴巴的云原生数据库PolarDB-X以其出色的性能、可靠性和扩展性闻名,在多种业务场景中广泛应用。尤其在列存储模式下,PolarDB-X针对分析型查询进行了优化,显著提升了数据读取效率。本文通过TPC-H基准测试探讨PolarDB-X列存执行计划的优化策略,包括高效数据扫描、专用查询算法以及动态调整执行计划等功能,以满足复杂查询的需求并提高数据分析性能。
92 1
|
3月前
|
存储 关系型数据库 MySQL
"深入探索MySQL临时表:性能优化利器,数据处理的灵活之选"
【8月更文挑战第9天】MySQL临时表专为存储临时数据设计,自动创建与删除,仅在当前会话中存在,有助于性能优化。它分为本地临时表和全局临时表(通过特定逻辑模拟)。创建语法类似于普通表,但加TEMPORARY或TEMP关键字。适用于性能优化、数据预处理和复杂查询,需注意内存占用和事务支持问题。合理使用可大幅提升查询效率。
208 2
|
3月前
|
自然语言处理 Java
自研分布式训练框架EPL问题之实现显存的极致优化如何解决
自研分布式训练框架EPL问题之实现显存的极致优化如何解决
|
3月前
|
并行计算 算法 调度
自研分布式训练框架EPL问题之提高GPU利用率如何解决
自研分布式训练框架EPL问题之提高GPU利用率如何解决
|
4月前
|
缓存 监控 测试技术
优化PHP应用性能的关键技巧与实践
提升PHP应用性能是开发者关注的重点之一,本文探讨了几种有效的优化技巧和实际应用策略,包括缓存策略的选择、代码优化建议以及服务器端配置的最佳实践,旨在帮助开发者有效提升PHP应用的运行效率和响应速度。【7月更文挑战第2天】
33 0
|
缓存 算法 大数据
倚天710规模化应用 - 性能优化 - 软件预取分析与优化实践
软件预取技术是编程者结合数据结构和算法知识,将访问内存的指令提前插入到程序,以此获得内存访取的最佳性能。然而,为了获取性能收益,预取数据与load加载数据,比依据指令时延调用减小cachemiss的收益更大。
|
6月前
|
移动开发 监控 前端开发
构建高效Android应用:从内存优化到性能提升
【4月更文挑战第11天】在移动开发领域,一个流畅、高效的应用与用户体验息息相关。尤其对于安卓平台,设备的多样性和碎片化为开发者带来了额外的挑战。本文将探讨一系列针对Android应用的优化策略,从内存管理的最佳实践到布局优化,以及如何利用最新的Android框架特性来提升应用性能。我们将深入了解如何诊断性能瓶颈,并采用切实可行的技术手段进行调优,旨在帮助开发者构建出更加健壮、快速且响应灵敏的应用。
43 0
|
6月前
|
移动开发 Java Android开发
构建高效的Android应用:从内存优化到性能提升
【4月更文挑战第7天】 在移动开发领域,尤其是针对资源受限的Android设备,应用的性能和稳定性是决定用户满意度的关键因素。本文深入探讨了Android应用开发中如何通过内存优化、多线程处理以及电池使用效率的提升来增强应用的整体性能。我们将透过具体案例分析和最佳实践的分享,指导开发者避免常见的性能陷阱,打造更流畅、响应更快的Android应用。
|
6月前
|
机器学习/深度学习 并行计算 算法
掌握XGBoost:GPU 加速与性能优化
掌握XGBoost:GPU 加速与性能优化
1058 0