倚天710规模化应用 - 性能优化 - 软件预取分析与优化实践

简介: 软件预取技术是编程者结合数据结构和算法知识,将访问内存的指令提前插入到程序,以此获得内存访取的最佳性能。然而,为了获取性能收益,预取数据与load加载数据,比依据指令时延调用减小cachemiss的收益更大。

1 背景

现代应用,如高性能计算(HPC)、大数据、数据库及数据处理等,对内存的时延大小很敏感。传统的解决方案是通过硬件模块检测规则的内存访问模式(循环步长)进行数据预取,也即是硬件预取方法。如此,处理器可将需要的数据提前加载到快速缓存内存中。然而,这些技术对非规则的内存访问模式,如链表形式的数据结构、通过index间接内存访问数组数据并不适用。
软件预取技术是编程者结合数据结构和算法知识,将访问内存的指令提前插入到程序,以此获得内存访取的最佳性能。然而,为了获取性能收益,预取数据与load加载数据,比依据指令时延调用减小cachemiss的收益更大。更为重要的是,过早预取数据,会导致数据cache超出,还未到用时,数据已经被弹出,进而数据再次load加载。若预取数据较晚,除了load自身数据的时延,还会增加冗余预取数据的时延。所以预取的时机不对常常导致软预取性能提升不明显,甚至性能下降。
结合业界提供了[论文]一种针对间接内存访问模式的自动化插入预取指令的优化算法,不仅可以处理更复杂的间接内存访问模式,而且可以规避提前加载导致的运行错误。核心是采用深度优化查找算法,找到load指令引用的循环归纳变量的路径指令集,进而对路径指令集进行数据提前加载,达到访存数据提前加载的目的。并在科学计算、HPC、大数据、数据库等商业应用,此方法在Intel Haswell架构上平均1.3X提升, Arm Cortex-A57架构有1.1X提升。发现软件预取的性能收益,和look-ahead预测距离、内存宽度、动态指令数及TLB大小强相关。
直观上理解,数据预取的方法,可以通过以下方法实现:

for (int i = 0; i < ARRAYLEN; i++) {
   
    arrayC[i] = arrayA[i] * arrayB[i];
}

增加Prefetch指令的代码,如下所示:

Prefetch(&arrayA[0]);
Prefetch(&arrayB[0]);
for (int i = 0; i < ARRAYLEN - ARRAYLEN % 4; i+=4) {
   
    Prefetch(&arrayA[i + 4]);
    Prefetch(&arrayB[i + 4]);
    arrayC[i] = arrayA[i] * arrayB[i];
    arrayC[i + 1] = arrayA[i + 1] * arrayB[i + 1];
    arrayC[i + 2] = arrayA[i + 2] * arrayB[i + 2]; 
    arrayC[i + 3] = arrayA[i + 3] * arrayB[i + 3];
}

软件预取的方法实现原理基本上述方法类似。
假如数组之间存在间接调用的场景,如

1 for (i=0; i<base_array_size; i++) {
   
2 target_array[func(base_array[i])]++;
3 }

这里,顺序访问的内存数组base_array, 且定义func(x)=x,也即是base_array的结果作为target_array的访问index,故涉及到两种内存访问模式,base_array[i]和target_array[index].这两种械均适用于软预取。但访问target_array地址依赖于base_array结果。硬件预取却不无法精确识别并预取数据。通过软预取技术,可插入预取指令如下:

1 for (int i=0; i<NUM_KEYS; i++) {
   
2 // The intuitive case, but also
3 // required for optimal performance.
4 SWPF(key_buff1[key_buff2[i + offset]]);
5 // Required for optimal performance.
6 SWPF(key_buff2[i + offset*2]);
7 key_buff1[key_buff2[i]]++;
8 }

更重要的是,以合适的预测距离(当前迭代的偏移)插入正确的预取代码,对最终用户来说,是一个挑战。通过上述代码,在第4行插入prefetch指令,性能提升1.08X。然而,在key_buff2和间接数据kye_buffer1均插入prefetch指令,也即是在第6行也插入prefetch,性能提升1.30X。所以,选择合适的预测距离至关重要,可以避免过早或过晚预取数据问题。

2 软件预取

基于LLVM IR,开发软预取优化PASS,思路是通过查找load指令,判别是否可以在数组内预测预取,然后生成软件预取指令。首先生成分析信息,然后代码生成。算法1:
image.png

软预取流程图

3 Benchmark验证

目前,Prefetch优化算法作为LLVM的新增Pass,注册到LLVM-内部的PASS流程中,通过编译选项控制此优化的开关,具有通用性及普适性。
Noahbenchmark是基于服务器提取的权威性验证Benchmark,通过此benchmark可获取比较有说服力的数据。首先验证了noahbenchmark内prefetch内11个场景。通过前后优化对比,每个benchmark场景均有提升,最高达10%+。

基于Prefetch优化,验证SPEC2017性能,总体软预取带来5个benchmark提升,最大提升2.49%,3个benchmark持平,2个benchmark略有下降。

相关文章
|
缓存 编译器
BOLT 二进制反馈优化技术
大型应用的代码往往达到数十甚至上百MB,这导致在程序执行时缓存机制无法充分利用,导致大量时间花费在CPU和内存链路上。通过对热点函数的布局进行优化,我们可以更好地利用CPU cache,从而获得较为可观的性能提升。针对这一问题,在编译技术上有PGO和Bolt两种解决办法,两者都是一种通过收集程序在运行时如跳转,调用关系,函数热度等执行信息,这些收集到的程序运行情况数据(profile data),可以更好地指导一些程序优化的策略,如是否对函数进行内联,以及对基本块和函数布局的排布来提高特定场景下的程序性能。
4031 2
BOLT 二进制反馈优化技术
|
网络协议 C# 文件存储
C# 利用FluentFTP实现FTP上传下载功能
C# 利用FluentFTP实现FTP上传下载功能
1379 0
C# 利用FluentFTP实现FTP上传下载功能
|
前端开发 芯片
【芯片前端】保持代码手感——握手协议ready打拍时序优化
【芯片前端】保持代码手感——握手协议ready打拍时序优化
827 0
|
机器学习/深度学习 人工智能 分布式计算
5分钟搭建企业级AI问答知识库
【8月更文挑战第14天】5分钟搭建企业级AI问答知识库
|
关系型数据库 MySQL 数据库
[图解MySQL]MySQL组提交(group commit)
[图解MySQL]MySQL组提交(group commit)
19038 0
|
程序员 编译器 C语言
C语言中的预处理器指令,涵盖其基本概念、常见指令(如`#define`、`#include`、条件编译指令等)、使用技巧及注意事项
本文深入解析C语言中的预处理器指令,涵盖其基本概念、常见指令(如`#define`、`#include`、条件编译指令等)、使用技巧及注意事项,并通过实际案例分析,展示预处理器指令在代码编写与处理中的重要性和灵活性。
813 2
|
缓存 前端开发 Linux
PAS工具分享 - FrameScope与RTRadar
本次分享的主题是PAS工具分享 - FrameScope与RTRadar,由阿里云基础软件系统研发团队工程师刘依男、苏峰分享。主要分为三个部分: 1. PAS性能分析套件综述 2. PAS FrameScope 3. PAS RTRadar
529 0
|
存储 算法 搜索推荐
倚天性能优化—ptg-optimize高性能基础库
ptg-optimize为平头哥数据中心解决方案团队开发的一款高性能库,该高性能库借助倚天的硬件架构优势,通过算法设计优化、SIMD向量化、指令优化等,实现包括mem、string、crc32、crypto、spinlock、等20+常用接口的性能优化,mem/string/spinlock等常用接口对标glibc 2.32,crc32/crypto等接口对标业务常用标准化接口。经测试,当前版本优化接口平均性能提升超过50%,性能优化效果显著,集成使用便捷,已为包括集团ebs存储、tair、rds数据库等多种场景提供应用性能加速。
倚天性能优化—ptg-optimize高性能基础库
|
存储 Oracle JavaScript
300万数据导入导出优化方案,从80s优化到8s(实测)
300万数据导入导出优化方案,从80s优化到8s(实测)
300万数据导入导出优化方案,从80s优化到8s(实测)