不同的进程可以同时运行RDTSC吗?

时间:2019-06-04 08:02:55

标签: x86 intel multicore hyperthreading rdtsc

不同的进程可以同时运行RDTSC吗? 还是这是只有一个内核可以同时运行的资源? TSC位于每个内核中(至少您可以针对每个内核分别进行调整),因此应该可行。但是超级踏步呢?

我该如何测试?

1 个答案:

答案 0 :(得分:4)

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

使用HyperThreading,共享物理资源的逻辑核心总是争夺执行资源。从Agner Fog's instruction tables,我们知道Skylake上的RDTSC的前端为20 oups,每25个周期的吞吐量为1。在仅执行RDTSC指令的情况下,每时钟频率不到1 uop,争夺前端可能不是问题。

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

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

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

我很好奇,我自己在Linux上使用Skylake i7-6700k上的perftaskset -c 3taskset -c 7(Linux枚举该CPU内核的方式,这些数字)是第四个物理核心的逻辑核心。您可以检查/ 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是一个静态可执行文件,带有一个手写的asm循环,其中包含times 20 rdtsc(NASM重复运算符)和dec ebp / jnz,并且循环的顶部对齐乘以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% )

vs。独自运行:

 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 counts / sec确实表示单线程模式。)

vs。在一起运行的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 uops)可能会比另一个同样正在运行的线程对RDTSC线程的伤害更大[em> RDTSC。也许我们甚至可以弄清楚RDTSC是否需要比其他特定端口更多的端口,例如端口1很容易饱和,因为它是唯一可以运行整数乘法指​​令的端口。