【问题标题】:C microbenchmark 'bug' when measuring store latency测量存储延迟时的 C 微基准测试“错误”
【发布时间】:2021-03-15 12:57:11
【问题描述】:

我一直在 x86 上尝试一些实验 - 即 mfence 对存储/加载延迟等的影响。

这是我开始的:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#define ARRAY_SIZE 10
#define DUMMY_LOOP_CNT 1000000
int main()
{
    char array[ARRAY_SIZE];

    for (int i =0; i< ARRAY_SIZE; i++)
        array[i] = 'x'; //This is to force the OS to give allocate the array

    asm volatile ("mfence\n");
    for (int i=0;i<DUMMY_LOOP_CNT;i++); //A dummy loop to just warmup the processor

    struct result_tuple{
        uint64_t tsp_start;
        uint64_t tsp_end;
        int offset;
        };

    struct result_tuple* results = calloc(ARRAY_SIZE , sizeof (struct result_tuple));
    for (int i = 0; i< ARRAY_SIZE; i++)
    {
        uint64_t *tsp_start,*tsp_end;
        tsp_start = &results[i].tsp_start;
        tsp_end = &results[i].tsp_end;
        results[i].offset = i;

        
        asm volatile (
        "mfence\n"
        "rdtscp\n"
        "mov %%rdx,%[arg]\n"
        "shl $32,%[arg]\n"
        "or %%rax,%[arg]\n"
        :[arg]"=&r"(*tsp_start)
        ::"rax","rdx","rcx","memory"
        );

        array[i] = 'y'; //A simple store

        asm volatile (
        "mfence\n"
        "rdtscp\n"
        "mov %%rdx,%[arg]\n"
        "shl $32,%[arg]\n"
        "or %%rax,%[arg]\n"
        :[arg]"=&r"(*tsp_end)
        ::"rax","rdx","rcx","memory"
        );
    }

    
    printf("Offset\tLatency\n");
    for (int i=0;i<ARRAY_SIZE;i++)
    {
        printf("%d\t%lu\n",results[i].offset,results[i].tsp_end - results[i].tsp_start);
    }
    free (results);
}   

我用gcc microbenchmark.c -o microbenchmark 编译很简单 我的系统配置如下:

CPU : Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
Operating system : GNU/Linux (Linux 5.4.80-2)

我的问题是这样的:

  • 在一次运行中,所有延迟都相似
  • 反复重复实验时,我没有得到与上一次类似的结果!

例如:

In run 1 I get:
Offset  Latency
1   275
2   262
3   262
4   262
5   275
...
252 275
253 275
254 262
255 262

In another run I get:
Offset  Latency
1   75
2   75
3   75
4   72
5   72
...
251 72
252 72
253 75
254 75
255 72

这非常令人惊讶(运行间变化非常高,而运行内变化可以忽略不计)!我不知道如何解释这一点。我的微基准有什么问题?

注意:我确实知道普通存储将是 write allocate 存储。从技术上讲,我的测量是负载(而不是存储)。此外,mfence 应该刷新存储缓冲区,从而确保没有存储被“延迟”。

【问题讨论】:

  • 您的预热虚拟循环仅进行 100 万次迭代,在 -O0 调试构建中约 6 百万个时钟周期 - 可能不足以让 CPU 在 CPU 上达到最大涡轮增压在 Skylake 的硬件 P 状态管理之前。 (Idiomatic way of performance evaluation?) RDTSCP 计算固定频率参考周期,而不是核心时钟周期。您的运行时间如此之短,以至于所有运行之间的变化都可能由 CPU 频率低或高来解释。见How to get the CPU cycle count in x86_64 from C++?

标签: c performance assembly x86 cpu-architecture


【解决方案1】:

您的预热虚拟循环仅进行 100 万次迭代,在 -O0 调试构建中约 600 万个时钟周期 - 可能不足以让 CPU 达到最大涡轮增压,在 Skylake 硬件之前的 CPU 上 P-状态管理。 (Idiomatic way of performance evaluation?)

RDTSCP 计算固定频率参考周期,而不是核心时钟周期。您的运行时间如此之短,以至于所有运行之间的变化都可能由 CPU 频率低或高来解释。见How to get the CPU cycle count in x86_64 from C++?


此外,此调试 (-O0) 构建将在您的定时区域内执行额外的存储和重新加载,但“幸运的是”results[i].offset = i; 存储加上第一个 mfence 之前的 mfence 确保结果数组也是在进入定时区域之前缓存中的热点。

你的数组很小,你只做 1 字节的存储(所以 64 个存储都在同一个缓存行中。)它很可能从你初始化它时仍然处于 MESI 修改状态,所以我不会期待任何array[i] = 'y' 商店的 RFO。在您的定时循环之前涉及的几行堆栈内存已经发生了这种情况。如果您想在不缓存阵列的情况下对阵列进行预故障,则可能每 4k 页触摸一行,而其他行保持不变。但是硬件预取将领先于您的存储,特别是如果您一次只存储 1 个字节,每个存储有 2 个慢速 mfences,因此等待核外内存请求将再次超出定时区域。您应该期望数据已经在 L1d 缓存或至少 L2 处于 Exclusive 状态,准备好在存储中翻转为 Modified。

顺便说一句,拥有offset 成员似乎毫无意义;它可以从数组索引中隐含。例如打印 i 而不是 offset[i]。存储开始和停止绝对 TSC 值也不是很有用。你可以只存储一个 32 位的差异,然后你就不需要在你的内联 asm 中移位 / 或,只需在未使用的 EDX 输出上声明一个 clobber。

另请注意,“存储延迟”通常仅在涉及mfence 时才影响实际代码中的性能。否则重要的是存储->加载转发,这可以在存储提交到 L1d 缓存之前从存储缓冲区发生。这大约是 6 个周期,如果不立即尝试重新加载,则有时会更低。 (它在 Sandybridge 家族中是可变的。)

【讨论】:

  • 我的偏移成员的原因如下:将来,也许我想以随机顺序访问数组...偏移成员会有所帮助
  • 另外,如何知道 RDTSC 滴答频率?从某个 MSR 中读取?
  • @SuraajKS:哦,我明白了,这是array[] 偏移量,而不是结果索引。奇怪的是它只是一个int,而不是size_t。回复:TSC 频率:不幸的是,很难让 CPU 告诉你参考频率是多少。 How to get the CPU cycle count in x86_64 from C++? 有一个链接,Determine TSC frequency on Linux
  • 还有Getting TSC rate in x86 kernel——最简单的方法是使用你的Linux内核在启动时检测到的值,来自dmesg。例如在我的 i7-6700k 上 - tsc: Refined TSC clocksource calibration: 4008.000 MHz
猜你喜欢
  • 2013-07-13
  • 2021-05-20
  • 2015-12-15
  • 2014-02-17
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
  • 1970-01-01
  • 2011-12-20
相关资源
最近更新 更多