为何单线程C程序存在显著存储延迟?——基于Intel VTune分析的问题问询
为何单线程C程序存在显著存储延迟?——基于Intel VTune分析的问题问询
我遇到了一个奇怪的性能问题,先给大家看看我写的这段C程序(MSVC不会把里面的"work"逻辑优化掉,换成其他编译器的话,可能需要加asm语句来阻止编译器优化):
#include <inttypes.h> #include <stdlib.h> #define SIZE 10000 typedef struct { int32_t a, b, c; } Struct; void do_work(Struct* data) { int32_t* a = malloc(sizeof(int32_t) * SIZE), * b = malloc(sizeof(int32_t) * SIZE), * c = malloc(sizeof(int32_t) * SIZE); int32_t* a_ptr = a, * b_ptr = b, * c_ptr = c; for (size_t i = 0; i < SIZE; i++, a_ptr++, b_ptr++, c_ptr++, data++) { *a_ptr = data->a; *b_ptr = data->b; *c_ptr = data->c; } free(a); free(b); free(c); } int main() { Struct* data = malloc(sizeof(Struct) * SIZE); for (size_t i = 0; i < SIZE; i++) { data[i].a = i; data[i].b = i; data[i].c = i; } for (int i = 0; i < 500000; i++) { do_work(data); } free(data); }
补充说明:下面是do_work()函数的反汇编代码:
do_work PROC ; COMDAT $LN12: mov QWORD PTR [rsp+8], rbx mov QWORD PTR [rsp+16], rbp mov QWORD PTR [rsp+24], rsi push rdi sub rsp, 32 ; 00000020H mov rbx, rcx mov ecx, 40000 ; 00009c40H call QWORD PTR __imp_malloc mov ecx, 40000 ; 00009c40H mov rsi, rax call QWORD PTR __imp_malloc mov ecx, 40000 ; 00009c40H mov rbp, rax call QWORD PTR __imp_malloc mov r10, rsi lea rcx, QWORD PTR [rbx+8] sub r10, rax mov r11, rbp sub r11, rax mov rdi, rax mov r8, rax mov r9d, 10000 ; 00002710H npad 6 $LL4@do_work: mov edx, DWORD PTR [rcx-8] lea rcx, QWORD PTR [rcx+12] mov DWORD PTR [r10+r8], edx lea r8, QWORD PTR [r8+4] mov eax, DWORD PTR [rcx-16] mov DWORD PTR [r11+r8-4], eax mov eax, DWORD PTR [rcx-12] mov DWORD PTR [r8-4], eax sub r9, 1 jne SHORT $LL4@do_work mov rcx, rsi call QWORD PTR __imp_free mov rcx, rbp call QWORD PTR __imp_free mov rcx, rdi mov rbx, QWORD PTR [rsp+48] mov rbp, QWORD PTR [rsp+56] mov rsi, QWORD PTR [rsp+64] add rsp, 32 ; 00000020H pop rdi rex_jmp QWORD PTR __imp_free do_work ENDP
我用Intel VTune分析这个程序后,得到了让人困惑的结果:程序有63.1%的时间处于内存受限状态,其中52.4%是存储受限,存储延迟占比高达26%。VTune给出的优化建议是排查伪共享问题,但我实在想不通这里怎么会出现伪共享——程序是单线程运行的,所有数据都由一个核心处理,内存访问模式应该非常容易被CPU预测和预取,完全搞不懂为什么CPU会在存储操作上停滞。
我曾经有过几个猜想:
- 会不会是三个内存分配的地址的高低位相同,导致它们被映射到同一条缓存线?不过我记得现代CPU不会只是简单丢弃几位来分配缓存线,而是用更复杂的哈希算法来计算缓存映射。
- 会不会是释放内存后CPU还在忙着刷新存储操作,而下一次调用
do_work()时分配器又返回了相同(或接近)的地址,导致CPU需要等待之前的存储操作完成才能写入新数据?于是我试着去掉free()调用不释放内存,但这样反而让程序运行得更慢了。
我的运行环境是Windows 11系统,笔记本搭载Intel Core i9-13900HX处理器,有32个逻辑核心,包含8个性能核和16个能效核。
备注:内容来源于stack exchange,提问作者Chayim Friedman
相关产品推荐
相关产品推荐

