【问题标题】:Can different processes run RDTSC at the same time?不同的进程可以同时运行RDTSC吗?
【发布时间】:2019-10-19 17:42:29
【问题描述】:

不同进程可以同时运行RDTSC吗? 或者这是只有一个核心可以同时操作的资源? TSC 在每个核心中(至少您可以为每个核心单独调整它),因此应该是可能的。但是 Hyper Treading 呢?

我该如何测试这个?

【问题讨论】:

    标签: x86 intel multicore hyperthreading rdtsc


    【解决方案1】:

    每个物理内核都有自己的 TSC;微代码不必脱离核心,因此它们没有竞争共享资源。完全脱离核心会使它变得更慢,并使实现更加复杂。在每个内核内部都有一个物理计数器是一种更简单的实现方式,只需计算分配给所有内核的参考时钟信号的滴答声。

    使用超线程,共享物理的逻辑内核总是竞争执行资源。从Agner Fog's instruction tables,我们知道 Skylake 上的 RDTSC 是前端 20 uop,每 25 个周期有 1 个吞吐量。在只执行 RDTSC 指令时,每个时钟不到 1 uop,竞争前端可能不是问题。

    可能这些微指令中的大多数都可以在任何执行端口上运行,因此两个逻辑线程很可能都可以在该吞吐量下运行rdtsc

    但也许他们会竞争一个不完全流水线的执行单元。

    您可以通过将times 20 rdtsc 放在一个循环中进行测试,该循环运行几千万次迭代,并在一个核心上单独运行该微基准,然后将其运行两次,固定在一个物理核心的逻辑核心上。

    我很好奇,我自己在 Linux 上使用 perf 在 Skylake i7-6700k 上,使用 taskset -c 3taskset -c 7(Linux 枚举此 CPU 上的内核的方式,这些数字是逻辑内核)第 4 个物理核心。您可以查看 /proc/cpuinfo 以了解您的系统。)

    为了避免在它们几乎同时完成时交错输出行,我使用带有cat <(cmd1) <(cmd2) 的bash 进程替换来同时运行它们并以固定顺序打印输出。命令是 taskset -c 3 perf stat -etask-clock:u,context-switches,cpu-migrations,page-faults,cycles:u,instructions:u,branches:u,branch-misses:u,uops_issued.any:u,uops_executed.thread:u,cpu_clk_thread_unhalted.one_thread_active:u -r2 ./testloop 计算核心时钟周期(不是参考周期,所以我不必担心涡轮/空闲时钟频率)。

    testloop 是一个静态可执行文件,带有一个包含times 20 rdtsc(NASM 重复运算符)和dec ebp/jnz 的手写 asm 循环,循环顶部以 64 对齐,以防万一。在循环之前,mov ebp, 10000000 初始化计数器。 (有关我如何以这种方式进行微基准测试的详细信息,请参阅 Can x86's MOV really be "free"? Why can't I reproduce this at all?。或者 Understanding the impact of lfence on a loop with two long dependency chains, for increasing lengths 另一个简单的 NASM 程序示例,其中使用 times 重复指令。)

     Performance counter stats for './testloop' (2 runs):
    
              1,278.19 msec task-clock:u              #    1.000 CPUs utilized            ( +-  0.19% )
                     4      context-switches          #    0.004 K/sec                    ( +- 11.11% )
                     0      cpu-migrations            #    0.000 K/sec                  
                     2      page-faults               #    0.002 K/sec                  
         5,243,270,118      cycles:u                  #    4.102 GHz                      ( +-  0.01% )  (71.37%)
           219,949,542      instructions:u            #    0.04  insn per cycle           ( +-  0.01% )  (85.68%)
            10,000,692      branches:u                #    7.824 M/sec                    ( +-  0.03% )  (85.68%)
                    32      branch-misses:u           #    0.00% of all branches          ( +- 93.65% )  (85.68%)
         4,010,798,914      uops_issued.any:u         # 3137.885 M/sec                    ( +-  0.01% )  (85.68%)
         4,010,969,168      uops_executed.thread:u    # 3138.018 M/sec                    ( +-  0.00% )  (85.78%)
                     0      cpu_clk_thread_unhalted.one_thread_active:u #    0.000 K/sec                    (57.17%)
    
               1.27854 +- 0.00256 seconds time elapsed  ( +-  0.20% )
    
    
     Performance counter stats for './testloop' (2 runs):
    
              1,278.26 msec task-clock:u              #    1.000 CPUs utilized            ( +-  0.18% )
                     6      context-switches          #    0.004 K/sec                    ( +-  9.09% )
                     0      cpu-migrations            #    0.000 K/sec                  
                     2      page-faults               #    0.002 K/sec                    ( +- 20.00% )
         5,245,894,686      cycles:u                  #    4.104 GHz                      ( +-  0.02% )  (71.27%)
           220,011,812      instructions:u            #    0.04  insn per cycle           ( +-  0.02% )  (85.68%)
             9,998,783      branches:u                #    7.822 M/sec                    ( +-  0.01% )  (85.68%)
                    23      branch-misses:u           #    0.00% of all branches          ( +- 91.30% )  (85.69%)
         4,010,860,476      uops_issued.any:u         # 3137.746 M/sec                    ( +-  0.01% )  (85.68%)
         4,012,085,938      uops_executed.thread:u    # 3138.704 M/sec                    ( +-  0.02% )  (85.79%)
                 4,174      cpu_clk_thread_unhalted.one_thread_active:u #    0.003 M/sec                    ( +-  9.91% )  (57.15%)
    
               1.27876 +- 0.00265 seconds time elapsed  ( +-  0.21% )
    

    对比一个人跑:

     Performance counter stats for './testloop' (2 runs):
    
              1,223.55 msec task-clock:u              #    1.000 CPUs utilized            ( +-  0.52% )
                     4      context-switches          #    0.004 K/sec                    ( +- 11.11% )
                     0      cpu-migrations            #    0.000 K/sec                  
                     2      page-faults               #    0.002 K/sec                  
         5,003,825,966      cycles:u                  #    4.090 GHz                      ( +-  0.00% )  (71.31%)
           219,905,884      instructions:u            #    0.04  insn per cycle           ( +-  0.04% )  (85.66%)
            10,001,852      branches:u                #    8.174 M/sec                    ( +-  0.04% )  (85.66%)
                    17      branch-misses:u           #    0.00% of all branches          ( +- 52.94% )  (85.78%)
         4,012,165,560      uops_issued.any:u         # 3279.113 M/sec                    ( +-  0.03% )  (85.78%)
         4,010,429,819      uops_executed.thread:u    # 3277.694 M/sec                    ( +-  0.01% )  (85.78%)
            28,452,608      cpu_clk_thread_unhalted.one_thread_active:u #   23.254 M/sec                    ( +-  0.20% )  (57.01%)
    
               1.22396 +- 0.00660 seconds time elapsed  ( +-  0.54% )
    

    cpu_clk_thread_unhalted.one_thread_active:u 的计数器仅以较慢的速率计数;​​在此测试期间系统相当空闲,因此它应该一直拥有核心。即〜23.2 M 计数/秒确实代表单次-线程模式。)

    对比一起运行的 0 和接近 0 计数表明我成功地让这些任务在同一个内核上同时运行,使用超线程,基本上整个时间(~1.2 秒重复两次,或 2.4 秒)。

    因此,每个 RDTSC 单线程 5.0038G 周期 / 10M 迭代 / 20 rdtsc/iter = 25.019 周期,这与 Agner Fog 测量的差不多。

    对 HT 测试的两个过程进行平均,平均约为 5.244G 周期 / 10M iter / 20 rdtsc/iter = 26.22 个周期。

    因此,在 Skylake 上同时在两个逻辑内核上运行 RDTSC 可以提供近乎线性的加速,并且对吞吐量资源的竞争非常小。 无论 RDTSC 遇到什么瓶颈,两个线程都不会相互竞争或减慢速度。

    让另一个核心忙于运行高吞吐量代码(如果它自己有一个核心,则每个时钟可以维持 4 微秒)可能会比另一个刚刚运行的线程更多伤害 RDTSC 线程RDTSC。也许我们甚至可以弄清楚 RDTSC 是否比其他端口更需要一个特定的端口,例如端口 1 很容易饱和,因为它是唯一可以运行整数乘法指​​令的端口。

    【讨论】:

    • 您能发布完整的testloop 代码吗? times 指令是什么?找不到任何东西。可能是因为它的名字模棱两可。
    • times 是一个 NASM 运算符,它多次重复该指令。就像我在回答中所说,Can x86's MOV really be "free"? Why can't I reproduce this at all? 拥有完整的源代码,只需将循环体替换为 times 20 rdtsc
    • 令人惊讶的是,有些东西表面上很简单,因为rdtsc 需要 20 μops。任何人都知道为什么会这样?我本来希望它只是读取一些时间戳寄存器
    • @BrennanVincent:我也很惊讶。也许与虚拟化能够为来宾虚拟机扩展和抵消它有关? (即使不在 VM 内运行,它也总是以相同的方式解码。)
    • 不知道最近的 CPU 型号应该如何,但我的笔记本电脑的 Intel Pentium T4300 有 2 个内核以不同的频率递增 RDTSC 值,有时速率为 0.48 ns/cycle(大部分时间),有时0.96 ns/cycle(过热时很少见)。用 C++ 程序和__rdtsc() 内在函数测量了这一点。我在 2008 年左右买了笔记本电脑。
    猜你喜欢
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2017-08-07
    • 1970-01-01
    相关资源
    最近更新 更多