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

为何AMD CPU上原子操作后的附加指令能提升执行速度?

原子操作基准测试中的异常提速问题

我编写了以下C语言测试用例,对不同内存操作和计算操作进行性能基准测试:

#define BENCH_ROUNDS 1000000000 // 10**9

static volatile UINT64 _test_argument, _test_result;
static _Atomic(UINT64) _test_atom;

// 简化循环写法
#define bench_loop(var) for (UINT64 var = 0; var < BENCH_ROUNDS; var++)

// 空循环参考
VOID test_empty(VOID) {
    bench_loop (i);
}

// 寄存器内计算参考
VOID test_calc(VOID) {
    UINT64 res = _test_argument;
    bench_loop (i) {
        res += i;
    }
    _test_result = res;
}

// 单纯内存存储
VOID test_store(VOID) {
    bench_loop (i) {
        _test_result = i;
    }
}

// 内存读写修改
VOID test_modify(VOID) {
    _test_result = _test_argument;
    bench_loop (i) {
        _test_result += i;
    }
}

// 内存修改+寄存器计算
VOID test_modify__calc(VOID) {
    _test_result = _test_argument;
    UINT64 res = _test_argument;
    bench_loop (i) {
        _test_result += i;
        res += i;
    }
    _test_result = res;
}

// 原子内存修改
VOID test_atom_modify(VOID) {
    bench_loop (i) {
        _test_atom += i;
    }
}

// 原子修改+寄存器计算
VOID test_atom_modify__calc(VOID) {
    UINT64 res = _test_argument;
    bench_loop (i) {
        _test_atom += i;
        res += i;
    }
    _test_result = res;
}

// 原子修改+更多寄存器计算
VOID test_atom_modify__calc2(VOID) {
    UINT64 res = _test_argument;
    bench_loop (i) {
        _test_atom += i;
        res += i, res ^= i;
        res += i, res ^= i;
        res += i, res ^= i;
        res += i, res ^= i;
    }
    _test_result = res;
}

测试环境与结果

测试通过QueryPerformanceCounter统计执行时间,使用gcc -Wall -Og -m64编译(该选项不会消除可预测循环)。

AMD Ryzen 7 4800H @4.3GHz 测试结果

000233.787ms  test_empty
000467.274ms  test_calc
000233.846ms  test_store
001869.585ms  test_modify
001869.459ms  test_modify__calc
004102.050ms  test_atom_modify
004102.818ms  test_atom_modify__calc
003928.742ms  test_atom_modify__calc2

异常现象

test_atom_modify__calc2的执行时间比其他test_atom_modify系列测试快150~200ms(平均每次迭代不到1个时钟周期),且多次测试结果一致。但类似逻辑的test_modify__calc2(非原子操作版本)并未出现提速。

Intel Xeon E5-2643 v3 对比测试结果

循环次数降至100万次,测试间添加休眠固定CPU频率,结果无异常提速:

000000.835ms  test_empty
000000.836ms  test_calc
000000.836ms  test_store
000006.264ms  test_modify
000006.264ms  test_modify__calc
000015.926ms  test_atom_modify
000015.873ms  test_atom_modify__calc
000015.900ms  test_atom_modify__calc2

关键循环汇编代码对比

# test_modify:
    jmp .L11
.L12:
    movq    _test_result(%rip), %rdx
    addq    %rax, %rdx
    movq    %rdx, _test_result(%rip)
    addq    $1, %rax
.L11:
    cmpq    $999999999, %rax
    jbe .L12

# test_atom_modify:
    jmp .L20
.L21:
    lock addq   %rax, _test_atom(%rip)
    addq    $1, %rax
.L20:
    cmpq    $999999999, %rax
    jbe .L21

# test_atom_modify__calc:
    jmp .L23
.L24:
    lock addq   %rax, _test_atom(%rip)
    addq    %rax, %rdx
    addq    $1, %rax
.L23:
    cmpq    $999999999, %rax
    jbe .L24

# test_atom_modify__calc2:
    jmp .L26
.L27:
    lock addq   %rdx, _test_atom(%rip)
    addq    %rdx, %rax
    xorq    %rdx, %rax
    addq    %rdx, %rax
    xorq    %rdx, %rax
    addq    %rdx, %rax
    xorq    %rdx, %rax
    addq    %rdx, %rax
    xorq    %rdx, %rax
    addq    $1, %rdx
.L26:
    cmpq    $999999999, %rdx
    jbe .L27

计时包装程序

#include <stdio.h>
#include <stdatomic.h>

#include <Windows.h>

/* 测试代码粘贴在此处 */


static VOID bench_time(VOID (*func)(VOID)) {
    UINT64 freq, st, ed;
    QueryPerformanceFrequency((LARGE_INTEGER*) &freq);

    QueryPerformanceCounter((LARGE_INTEGER*) &st);
    (*func)();
    QueryPerformanceCounter((LARGE_INTEGER*) &ed);

    UINT64 t = ed - st;
    printf("%010.3lfms\n", t * 1000.0 / freq);
}

INT main() {
    _test_argument = GetCurrentProcessId();
    atomic_init(&_test_atom, 0);

    bench_time(&test_empty);
    bench_time(&test_calc);
    bench_time(&test_store);
    bench_time(&test_modify);
    bench_time(&test_modify__calc);
//  bench_time(&test_modify__calc2);
    bench_time(&test_atom_modify);
    bench_time(&test_atom_modify__calc);
    bench_time(&test_atom_modify__calc2);

    return 0;
}

原因分析

从汇编代码和CPU架构特性可以解释这一差异:

  1. 指令级并行与延迟隐藏:lock addq是总线锁定的原子操作,在AMD Zen2架构(Ryzen 7 4800H基于该架构)上,这类操作会产生较长的内存延迟。test_atom_modify__calc2中,lock addq之后紧跟了多组寄存器级的addq/xorq操作——这些操作完全在CPU寄存器内执行,无需等待内存响应,可以和lock addq的内存延迟重叠,从而隐藏部分等待时间。而其他原子测试中,lock addq之后仅跟随addq $1, %rax这类简单指令,可重叠的指令少,无法有效隐藏延迟。
  2. 寄存器分配差异:test_atom_modify__calc2使用rdx作为循环变量和原子操作的源操作数,rax用于寄存器计算;而其他原子测试使用rax同时承担循环变量和原子操作数。这种寄存器分配方式让CPU的指令流水线调度更高效,减少了寄存器依赖冲突。
  3. Intel架构的特性差异:Intel Xeon E5-2643 v3基于Haswell架构,其原子操作的延迟特性或流水线调度逻辑与Zen2不同,无法通过后续寄存器操作有效隐藏lock add的延迟,因此未出现提速现象。

内容的提问来源于stack exchange,提问作者Wilderness Ranger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 02:10:56