鲲鹏开发重点3––新的精彩,ARM64下函数性能优化
前言:新的精彩
挑战:在ARM64常见的多CPU系统中,跨CPU访问时延有点大
一、局部变量:不提前赋值
二、全局变量:不频繁访问
后续:止损需要魄力,而坚持,需要更大的勇气和远见。
鲲鹏开发重点3––新的精彩,ARM64下函数性能优化
(老古,大何)
前言:新的精彩
随着鲲鹏计算生态如火如荼的发展,ARM64的性能优化进入了遍地开花的阶段,很多开发者希望,在遍地开花时,也结出性能卓越的成果。
挑战:在ARM64常见的多CPU系统中,跨CPU访问时延有点大
但在日常开发中,大家发现一个常见现象,就是在ARM64常见的多CPU系统中,跨CPU访问时延有点大,特别是对全局变量的访问。在多CPU的系统中,跨CPU访问的时延是比较大的,鲲鹏计算生态已经有消除全局变量的计划和方案来减少跨P的内存访问。
但对于热爱挑战的顶级开发者来说,是这些热点函数使用全局变量的方式还得仔细琢磨一下。
一、局部变量:不提前赋值
局部变量提前摘取全局变量值,可能函数返回了,流程中也没有使用此局部变量,造成CPU浪费。
perf抓到热点函数pickNextIod( )
(图片来源:网络)
函数源码如下,局部变量pHighCmdQ从全局变量g_pIodGlb中赋值,定义变量的时候就赋值了,但是实际流程从375行提前返回了,导致这个赋值没有意义。
(图片来源:网络)
从汇编代码中也发现了函数首部就有这个赋值动作,可能ret返回前都没有使用这个pHighCmdQ局部变量,而访问全局变量地址0x783a0e8却发生了。
这个和预取无关,属于多余操作。
“符号表:783a0e8 8 OBJECT GLOBAL DEFAULT 81 g_pIodGlb”
(图片来源:网络)
所以、源代码的排版可以调整,先定义一个局部变量,在使用前才赋值,不提前赋值,排除无效访问全局变量(发生跨CPU的内存访问)的动作。
编译器好像也不够优秀,没有从流程上梳理,并推迟局部变量的赋值,也许是编译器被do while(0)循环欺骗了。
这样照本宣科的翻译源码,没有保证最优的执行代码流程,合理调整一下指令就好了。
二、全局变量:不频繁访问
(图片来源:网络)
(图片来源:网络)
另外,这个的指令排布也是不合理的,没有进行流水线优化? 或者是还有另外的原因。
(图片来源:网络)
我理解,也许下面的排版更好一些,把单周期的寄存器操作放到两个内存读取之间。
"
ldr x2,[x0,#152]
sub x1,x1,#14
ldp x29,x30,[sp],#16
add x0,x1,x2
ret
"
obj_ctrl = (shm_pool_slice_ctrl*)((ulong)slice_addr + shm_pool->slice_whole_size - sizeof(shm_pool_slice_ctrl));
修改为:
obj_ctrl = (shm_pool_slice_ctrl*)( shm_pool->slice_whole_size + (ulong)slice_addr - sizeof(shm_pool_slice_ctrl));
按理,是编译器来调整最终的指令序,结果也不变。
这个全局变量的消除正在进行中,正在进行每CPU变量的改造,在numa内访问,减少跨P的访问。
后续:止损需要魄力,而坚持,需要更大的勇气和远见
ARM在服务器领域,从10多年前就已经开始了默默耕耘。那时候ARM自己才刚刚发布A15的核心,仅仅是一个双发射部分乱序执行的处理器,和当时的intel酷睿系列相距甚远。
在移动互联网领域的A15后,经过A57的试水,随后从A72开始,保持每年一代新核心的更替速度,牢牢踩住了移动互联网的节奏,随着移动领域的腾飞而快速成长为现在的霸主。
在当时很多人已经开始视ARM为intel在服务器领域强有力的竞争对手,认为基于RISC的ARM在功耗上教CISC架构的intel有着巨大的优势,在同等功耗下可以提供比intel更高的性能。由于intel在服务器领域一枝独秀,有一些比较激进的公司也愿意押宝ARM。
早期第一波吃螃蟹的基本上全军覆没,第一次ARM服务器泡沫的破灭,MARVELL等公司纷纷止损。
经过第一波、第二波、第三波,到现在有一个生态异军突起,超乎寻常的坚持了下来,经历了数次迭代,推出了国内最具竞争的ARM服务器平台“鲲鹏”,就是华为,以及全中国的鲲鹏生态伙伴弟兄们。
我们将持续发挥鲲鹏优势,把性能优化推进到新的阶段。
止损需要魄力,而如今的坚持优化,需要更大的勇气和远见。