You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何单线程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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:43:00