作者:转化术治_953 | 来源:互联网 | 2022-10-22 09:25
不同的进程可以同时运行RDTSC吗?还是这是只有一个内核可以同时运行的资源?TSC位于每个内核中(至少您可以针对每个内核分别进行调整),因此应该可行。但是超级跑步呢?
我该如何测试?
1> Peter Cordes..:
每个物理核心都有自己的TSC;微代码不必非核心使用,因此没有竞争的共享资源。完全脱离内核会使它变慢得多,并使实现更加复杂。在每个内核中都有一个物理上的计数器是一个更简单的实现,只需对分配给所有内核的参考时钟信号的滴答声进行计数即可。
借助超线程技术,共享物理资源的逻辑核心始终可以争夺执行资源。从Agner Fog的指令表中,我们知道Skylake上的RDTSC的前端为20 ups,每25个周期的吞吐量为1。在不执行RDTSC指令的情况下,每时钟频率小于1 uop的情况下,争夺前端可能不是问题。
这些微指令中的大多数都可以在任何执行端口上运行,因此两个逻辑线程都可以rdtsc
以该吞吐量运行。
但是也许有一个他们没有竞争的流水线执行单元。
您可以通过将其置于times 20 rdtsc
运行数千万次迭代的循环中,然后在一个内核上单独运行该微基准测试,然后将其两次固定到一个物理内核的逻辑内核上来对其进行测试。
我很好奇,在Linux上与perf
Skylake i7-6700k一起使用时,我做的很好,并且使用taskset -c 3
和taskset -c 7
(Linux枚举此CPU内核的方式,这些数字是第4个物理内核的逻辑内核。您可以检查/ proc / cpuinfo以在您的系统上找到。)
为了避免在输出线几乎同时完成时交错输出线,我使用了bash进程替换cat <(cmd1) <(cmd2)
来同时运行它们,并以固定顺序打印输出。这些命令
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
初始化计数器。(见灿86的MOV真的是“免费”?为什么我不能在所有重现此?我如何做微基准这样的细节,或者理解lfence的环路上有两个长依赖性链的影响,增加长度的另一具有循环times
以重复指令的循环的简单NASM程序的示例。)
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 uops)可能比另一个同样运行RDTSC的线程对RDTSC线程的伤害更大。也许我们甚至可以弄清楚RDTSC是否比其他端口更需要一个特定的端口,例如,端口1很容易饱和,因为它是唯一可以运行整数乘法指令的端口。