3

我的任务是计算 RAM 读/写速度。我使用 asm 插入来避免编译器优化。为了测量时间,我使用 TSC 和 CPU 频率。为了移动数据,我使用不使用缓存层次结构的 asm 指令 MOVNTDQ。

问题在于结果。数据速率(根据数据表)为 800 Mbps,我通过测试得到 > 2000 Mbps 的写入速度。

void memory_notCache_write_128(void* src, long blocks_amount) 
{
    _asm    
    {
        mov ecx, blocks_amount
        mov     esi, src
    a20:
        movntdq [esi], xmm0
        movntdq [esi + 16], xmm1
        movntdq [esi + 32], xmm2
        movntdq [esi + 48], xmm3
        movntdq [esi + 64], xmm4
        movntdq [esi + 80], xmm5
        movntdq [esi + 96], xmm6
        movntdq [esi + 112], xmm7
        add esi,  128
        loop    a20;
    }
}

int main()
{ 
    unsigned __int64 tick1, tick2;
    const long nBytes = 32*KByte;   

    char* source = (char*)_mm_malloc(nBytes*sizeof(char),16);

    tick1 =  getTicks();
    memory_notCache_write_128(source, current_times.t128);
    tick2 =  getTicks();

    double time = (double)(tick2-tick1)/(ProcSpeedCalc());
    cout << "Time WRITE_128[seconds]:" << time << endl;
    cout << (double) nBytes / time / MByte << endl;

    return 0;
}

我使用的 RAM 数据表 - http://www.alldatasheet.com/datasheet-pdf/pdf/308537/ELPIDA/EBE11UE6ACUA-8G-E.html

源代码(为 Win patform 编写):https ://bitbucket.org/closed_eyes/ram_speed_for_win/downloads/memory_test.cpp

4

2 回答 2

4

您不应该对此类代码使用非临时操作。构建内存性能测试器的真正方法是使用访问模式来确保您永远不会命中缓存。通常,这是通过循环一个非常大的内存块来完成的,该内存块大于系统中的最后一级缓存,其中您的步幅与缓存行大小相同。如果您这样做,您将确保每次访问都将是所有级别的缓存未命中。不要忘记,当您从内存中读取一个字节时,处理器将获取整个缓存行,因此如果您执行 64 位加载,在具有 64 字节缓存行的机器上(非常常见),您应该从内存中读取 64 字节。

于 2012-06-05T00:21:44.163 回答
2

Yury,您使用 movntdq 测量“物理”内存通道带宽的想法是正确的。我同意 Nathan Binkert 关于如何解决“系统范围”内存性能的观点,但是我想详细说明您关于 movntdq 一般适用性和 800Mbps 混淆的原始问题。

简洁版本:

  1. movntdq工作正常,当您想测量“物理”内存通道的带宽时可以使用。
  2. 800Mbps 是“位通道”规范。每个(可能有两个)内存控制器通道都是 64 位宽。两个内存控制器通道将提供接近 1600MBytes/s 的原始写入性能,但这仍然与您的实际测量不匹配,因此请查看下面的详细信息。
  3. 真的停止使用rdtsc。如果您遇到测量精度问题,请 使用QueryPerformanceFrequency和进行分析并增加测试缓冲区大小。QueryPerformanceCounter
  4. 请指定您的硬件平台的详细信息(cpu、sodimms 数量等)确保您在 bios 设置中没有任何内存超频。

更长的版本。

  1. 简而言之:movntdq 没问题。您必须将一系列 movntdq 写入对齐为 CPU 缓存行(64 字节)的倍数,并且必须将movntdq写入开始对齐到 64 字节边界。使用非对齐访问将导致指令的非时间提示无效,因此memory_notCache_write_32memory_notCache_write_16函数都不是使用movndq指令的正确选择。
  2. 简而言之:800Mbps 是单比特通道速度。SODIMM数据路径为 64 位,与 cpu/北桥内存通道相同。在谈论movntdq指令时,很可能有两个内存通道,但只有在主板上的正确内存插槽中安装了两个匹配的 sodimm 时,它们才会以“双通道”模式运行。两个通道应有效地为您提供 1600 Mbytes/s,而单通道将为您提供 800Mbytes/s 的性能。您的实际数字与 1600Mbytes/s 的估计值相差不大,但仍远未接近。这可能来自不正确的测量方法(见第 3 点)和/或超频内存(不太可能,但以防万一见第 4 点)。
  3. QueryPerformanceFrequency并且QueryPerformanceCounter应该对每个人都足够了” :) 说真的,rdtsc在项目的这个阶段完全停止使用。QueryPerformanceFrequency当您测量超过 10Mbytes 内存区域的内存写入性能时,3+ MHz 计时器精度 ( ) 就可以了。考虑到 1600MBytes/s 的理论内存带宽 3MHz 定时器的每个滴答声将导致 533 字节的“测量错误”,这在写入 10Mbytes 时没什么。rdtsc 是非常棘手的东西,主要是因为它在启用电源管理的 CPU 上随着时间的推移不稳定(我敢肯定你不是在rdtsc提供稳定计数的第二代/第三代英特尔核心 CPU 上)。请从系统提供的计时测量功能开始,以正确完成测量。它'QueryPerformanceFrequency
  4. 由于您正在测量物理内存通道带宽,因此值得指定用于此类测量的硬件平台。请确保您在 bios 设置中没有任何手动内存时序设置(如果内存控制器处于双通道模式,具有 533MHz 内存总线将提供 2+GBytes/s 内存带宽)。鉴于您使用的是嵌入式系统 ( SODIMM),因此可能会在 bios 中调整内存控制器设置。只需仔细检查没有超频设置。

作为结论 - 不要使用rdtsc,仅使用QueryPerformanceFrequencyQueryPerformanceCounter,继续使用对齐版本的内存写入movntdq并检查嵌入式系统的配置。我还强烈建议完全避免使用内联汇编并改用使用_mm_stream_si128( http://msdn.microsoft.com/en-us/library/ba08y07y.aspx )

于 2012-08-13T10:22:58.523 回答