一、大页的处理
在前面的相关文章分析中,对内存大页的相关内容和应用进行了多次分析,基本对大页内存的技术已基本掌握。但如何在实际开发如何使用大页内存,还需要结合具体的开发平台进行分析,从而能真正的将技术应用到实践。
下面就对在Linux平台上如何使用gcc进行大页处理进行整体的分析说明。重点针对热函数如何直接映射入大页。
二、C/C++的热函数
Hot Functions,热函数。其实从名字容易理解,这个函数会被高频的调用或者说执行的时间占用所占比例很高。这和前面的perf等性能监测工具中的“热”是一个道理。其实从前面的分析也可以看出来,热调用相关的代码在哪个方面都非常重要。所以其优化往往能带来更好的性能,一般来说,主要的优化方法有以下几种:
- 编译期的优化,即使用编译相关优化的选项和参数
- 使用内联函数,这个在前面分析过
- 内存优化,提高命中率
- 降低循环中的不当处理,不过一般来说,现代编译器对这个处理基本都可以比较好的应对。
在上面的内存优化中,除了基本的内存对齐、预取等相关的方法外,一个重要的方法就是将热函数映射到内存大页内,从而在根本上提高内存的命中率。本文也重点对此进行分析说明。
三、GCC中映射热函数到大页的方法
在Linux平台上,GCC可以下面的代码将热函数映射到内存大页:
- 直接编码实现内存大页映射
- 使用链接器脚本
- 使用gcc9版本以上的优化配置
另外,其实在Linux平台上,还可以直接配置大页进行开发(即直接启用透明大页),正如前面的DPDK中一样(具体的细节可参看前面的DPDK中相关的细节)。
四、例程
下面看上面几种方法的相关例程:
- 直接代码处理
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
void hotFunc1() {
/*......*/
}
void hotFunc2() {
/*......*/
}
// 映射大页内存
int hotFuncToHugePage() {
// 获取函数地址和大小
void* func1 = (void*)hotFunc1;
void* func2 = (void*)hotFunc2;
// 计算热函数代码大小(需要根据实际情况调整)
size_t codeSize = 2 * 1024 * 1024; // 2MB大页
// 申请大页内存
void* addr = mmap(NULL, codeSize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
-1, 0);
if (addr == MAP_FAILED) {
return -1;
}
//拷贝并计算热函数代码
memcpy(addr, func1, codeSize);
// 重点:刷新指令缓存
__builtin___clear_cache(addr,
(char*)addr + codeSize);
return 0;
}
编译使用普通的编译方法即可。
- 使用脚本
创建脚本hp_layout.ld:
SECTIONS {
.text : {
*(.text)
}
/* 热函数单独处理到指定段,2MB对齐 */
.text.hot : {
. = ALIGN(2M);
__hot_text_start = .;
*(.text.hot)
*(.text.hot.*)
. = ALIGN(2M);
__hot_text_end = .;
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
// 利用section属性标记热函数
#define HOT_FUNC __attribute__((section(".text.hot")))
// 热函数声明
HOT_FUNC void processData(int* , size_t );
HOT_FUNC void matrixMult(float** , float** , float** , int );
// hot func
HOT_FUNC void processData(int* d, size_t len) {
//相关实现代码
}
HOT_FUNC void matrixMult(float** a, float** b, float** ret, int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
//具体代码
}
}
}
}
编译方法如下:
# 调用上面创建的脚本
gcc -Wl,-T,hp_layout.ld -Wl,-zcommon-page-size=0x200000 -Wl,-zmax-page-size=0x200000 -O3 hot_func.c -o hotTest
# 也可以直接操作
gcc -Wl,--section-start=.text.hot=0x200000 -O3 hot_func.c -o hotTest
- gcc9以后的编译处理方式
// GCC的函数节特性
__attribute__((section(".text.hot")))
__attribute__((optimize("O3")))
void hotFunc() {
//相关代码
}
// 热函数分组
__attribute__((section(".text.hot.group1")))
void hotFuncGroup1() { /* ... */ }
__attribute__((section(".text.hot.group2")))
void hotFuncGroup2() { /* ... */ }
编译方法如下:
gcc -ffunction-sections -fdata-sections -O3 -Wl,--gc-sections -Wl,-Map=hot_map.txt test.c -o test
代码仅作为示例,重点在GCC的处理上,而不在代码实现上。
五、总结
其实很多开发者的技术水平已经不错了,但在实际开发中为什么很多的技术应用起来却不太顺畅?主要原因就在于一些技术与平台环境无法有机的整合在一起。就像人们说的,哪个零件都是好的,但组合在一起就不好使了。
什么意思呢?就是技术的应用一定要和实际的环境结合起来,不但有技术平台还有环境平台等。所以,给大家一个建议,不但要学好技术,还要把技术周边的相关应用掌握清楚。


被折叠的 条评论
为什么被折叠?



