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

DRAM读写带宽差异远超预期,测量方法是否合理?差异正常吗?

DRAM读写带宽测量疑问

我希望测量DRAM的读写带宽,采用的测量方法为:反复扫描一个128MB数组,仅访问每个缓存行的前4字节;读取操作使用add eax/ebx, [mem]指令,写入操作使用mov [mem], eax指令。

基于缓存写回策略,我预期写入带宽应为读取带宽的1/2,但实际测量结果显示写入带宽仅为读取带宽的1/20(读取耗时113.529ms,写入耗时2254.57ms)。

代码实现

asm (R"(
.bss
.align 64
    A: .zero 128 * 1024 * 1024
.text
)");
void read() {
    asm volatile (R"(
.intel_syntax noprefix

lea rdi, A[rip]
lea rsi, [rdi + 128 * 1024 * 1024]
.align 64
1:
    add eax, [rdi + 64 * 0]
    add ebx, [rdi + 64 * 1]
    add ecx, [rdi + 64 * 2]
    add edx, [rdi + 64 * 3]
    add eax, [rdi + 64 * 4]
    add ebx, [rdi + 64 * 5]
    add ecx, [rdi + 64 * 6]
    add edx, [rdi + 64 * 7]
    add rdi, 64 * 8
    cmp rdi, rsi
    jne 1b

.att_syntax
)"::: "rdi", "rsi", "eax", "ebx", "ecx", "edx"
    );
}
void write() {
    asm volatile (R"(
.intel_syntax noprefix

lea rdi, A[rip]
lea rsi, [rdi + 128 * 1024 * 1024]
.align 64
1:
    mov [rdi + 64 * 0], al
    mov [rdi + 64 * 1], eax
    mov [rdi + 64 * 2], eax
    mov [rdi + 64 * 3], eax
    mov [rdi + 64 * 4], eax
    mov [rdi + 64 * 5], eax
    mov [rdi + 64 * 6], eax
    mov [rdi + 64 * 7], eax
    add rdi, 64 * 8
    cmp rdi, rsi
    jne 1b

.att_syntax
)"::: "rdi", "rsi"
    );
}

#include <chrono>
#include <iostream>

using namespace std;

void measure(const char* name, void (*f)()) {
    for (int i = 0; i < 20; i++) f();
    auto start = chrono::steady_clock::now();
    for (int i = 0; i < 200; i++) f();
    auto end = chrono::steady_clock::now();
    cout << name << ": " << (end - start).count() * 1e-6 << " ms" << endl;
}
int main() {
    measure("reading", read);
    measure("writing", write);
}

实际输出

reading: 113.529 ms
writing: 2254.57 ms

现咨询:我的测量方法是否存在问题?DRAM的读写带宽差异真的会达到如此大的程度吗?


问题分析与解答

测量方法的核心问题

你的测量代码存在两个关键问题,直接导致了读写带宽的巨大差异:

1. 写入操作的指令效率极低

在write()函数的汇编代码中,第一条写入指令是mov [rdi + 64 * 0], al——这是一个单字节写入。现代CPU对窄宽度内存写入有额外开销:

  • 单字节写入需要CPU先读取整个缓存行(缓存行是最小内存操作单位),修改其中1字节后标记为脏行等待写回。
  • 更关键的是,窄写入会破坏CPU的**写合并(Write Combining)**机制。写合并允许CPU将多个小写入合并成一个总线事务,大幅提升写入效率,但单字节写入通常无法触发该机制,迫使每个写入单独发起总线请求,极大增加延迟。

而读取操作使用32位读取(add eax, [mem]),能高效利用缓存和总线带宽,指令吞吐量更高。

2. 读写循环的指令吞吐量不匹配

读取循环中,8条add指令可被CPU乱序执行,充分利用流水线和内存并行访问能力。而写入循环中,第一条单字节写入的延迟会拖慢整个循环节奏,加上写合并失效,每条写入指令的实际执行周期远高于读取指令,导致整体耗时剧增。

DRAM本身的读写带宽差异

正常情况下,DRAM写入带宽确实会比读取带宽低,但不会达到1/20的程度。写回缓存策略下,读取缓存不命中直接加载缓存行;写入缓存不命中会先加载缓存行(写分配),修改后标记为脏,后续批量写回DRAM。通常写入带宽是读取带宽的70%-90%,极端情况也不会低于50%。

修复建议

要得到准确的DRAM读写带宽测量结果,需修改写入代码:

  • 把所有写入操作改成相同宽度的32位或64位写入,比如全部用mov [rdi + 64 * n], eax,或更高效的64位写入mov [rdi + 64 * n], rax。
  • 确保写入操作能触发写合并,避免窄宽度写入。
  • 保持读写循环的指令结构一致,让两者的指令吞吐量和内存访问模式尽量匹配,对比结果才具备参考性。

修改后的写入循环示例:

1:
    mov [rdi + 64 * 0], eax
    mov [rdi + 64 * 1], eax
    mov [rdi + 64 * 2], eax
    mov [rdi + 64 * 3], eax
    mov [rdi + 64 * 4], eax
    mov [rdi + 64 * 5], eax
    mov [rdi + 64 * 6], eax
    mov [rdi + 64 * 7], eax
    add rdi, 64 * 8
    cmp rdi, rsi
    jne 1b

修改后,写入带宽应该会接近读取带宽的50%-90%,符合缓存写回策略下的预期表现。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:52:12