不同的进程运行 RDTSC可以同时存在吗?
Can different processes run RDTSC at the same time?
不同进程可以同时运行RDTSC吗?
或者这是一种只有一个内核可以同时运行的资源?
TSC在每个核心中(至少你可以为每个核心单独调整它),所以它应该是可能的。但是 Hyper Treading 呢?
我该如何测试?
每个物理核心都有自己的TSC;微码不必去off-core,所以没有他们竞争的共享资源。完全 off-core 会使它变得更慢,并使实现 更 复杂。在每个内核中物理设置一个计数器是一个更简单的实现,只需计算分配给所有内核的 reference-clock 信号的滴答声。
使用超线程,共享物理的逻辑内核总是竞争执行资源。从 Agner Fog's instruction tables,我们知道 Skylake 上的 RDTSC 对于 front-end 是 20 微指令,并且每 25 个周期吞吐量为 1。在只执行 RDTSC 指令的情况下,每个时钟不到 1 uop,竞争 front-end 可能不是问题。
可能这些微指令中的大部分都可以 运行 在任何执行端口上,因此很可能两个逻辑线程都可以 运行 rdtsc
具有该吞吐量。
但也许有一个 not-fully-pipelined 他们会竞争的执行单位。
您可以通过将 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
是一个带有 hand-written asm 循环的静态可执行文件,其中包含 times 20 rdtsc
(NASM 重复运算符)和 dec ebp
/jnz
,循环的顶部以 64 对齐,以防万一。在循环之前,mov ebp, 10000000
初始化计数器。 (请参阅 for details on how I do microbenchmarks this way. Or 另一个简单的 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 计数/秒确实代表 single-thread 模式。)
对比运行ning 的 0 和接近 0 的计数一起表明我成功地在同一个核心上同时执行这些任务 运行,使用超线程,基本上整个时间(~1.2 秒重复两次,或者2.4 秒)。
所以每个 RDTSC single-threaded 5.0038G 周期/10M iters/20 rdtsc/iter = 25.019 个周期,几乎是 Agner Fog 测量的值。
HT 测试的两个进程的平均值,平均约为 5.244G 周期/10M iter/20 rdtsc/iter = 26.22 个周期。
因此 运行同时在 Skylake 上的两个逻辑内核上使用 RDTSC 提供了近乎线性的加速,对吞吐量资源的竞争非常小。 无论 RDTSC 的瓶颈是什么,两个线程都不会相互竞争或减慢速度。
让另一个核心忙于 运行ning high-throughput 代码(如果它有自己的核心,每个时钟可以维持 4 微指令)可能会伤害 RDTSC 线程 更多 而不是另一个同样 运行ning RDTSC 的线程。也许我们甚至可以弄清楚是否有一个特定端口是 RDTSC 比其他端口更需要的,例如端口 1 很容易饱和,因为它是唯一可以 运行 整数乘法指令的端口。
不同进程可以同时运行RDTSC吗? 或者这是一种只有一个内核可以同时运行的资源? TSC在每个核心中(至少你可以为每个核心单独调整它),所以它应该是可能的。但是 Hyper Treading 呢?
我该如何测试?
每个物理核心都有自己的TSC;微码不必去off-core,所以没有他们竞争的共享资源。完全 off-core 会使它变得更慢,并使实现 更 复杂。在每个内核中物理设置一个计数器是一个更简单的实现,只需计算分配给所有内核的 reference-clock 信号的滴答声。
使用超线程,共享物理的逻辑内核总是竞争执行资源。从 Agner Fog's instruction tables,我们知道 Skylake 上的 RDTSC 对于 front-end 是 20 微指令,并且每 25 个周期吞吐量为 1。在只执行 RDTSC 指令的情况下,每个时钟不到 1 uop,竞争 front-end 可能不是问题。
可能这些微指令中的大部分都可以 运行 在任何执行端口上,因此很可能两个逻辑线程都可以 运行 rdtsc
具有该吞吐量。
但也许有一个 not-fully-pipelined 他们会竞争的执行单位。
您可以通过将 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
是一个带有 hand-written asm 循环的静态可执行文件,其中包含 times 20 rdtsc
(NASM 重复运算符)和 dec ebp
/jnz
,循环的顶部以 64 对齐,以防万一。在循环之前,mov ebp, 10000000
初始化计数器。 (请参阅 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 计数/秒确实代表 single-thread 模式。)
对比运行ning 的 0 和接近 0 的计数一起表明我成功地在同一个核心上同时执行这些任务 运行,使用超线程,基本上整个时间(~1.2 秒重复两次,或者2.4 秒)。
所以每个 RDTSC single-threaded 5.0038G 周期/10M iters/20 rdtsc/iter = 25.019 个周期,几乎是 Agner Fog 测量的值。
HT 测试的两个进程的平均值,平均约为 5.244G 周期/10M iter/20 rdtsc/iter = 26.22 个周期。
因此 运行同时在 Skylake 上的两个逻辑内核上使用 RDTSC 提供了近乎线性的加速,对吞吐量资源的竞争非常小。 无论 RDTSC 的瓶颈是什么,两个线程都不会相互竞争或减慢速度。
让另一个核心忙于 运行ning high-throughput 代码(如果它有自己的核心,每个时钟可以维持 4 微指令)可能会伤害 RDTSC 线程 更多 而不是另一个同样 运行ning RDTSC 的线程。也许我们甚至可以弄清楚是否有一个特定端口是 RDTSC 比其他端口更需要的,例如端口 1 很容易饱和,因为它是唯一可以 运行 整数乘法指令的端口。