Linux-4.4.0 中 PTI=on 时性能下降
performance degrading with PTI=on in Linux-4.4.0
我正在 运行 java 测试 Ubuntu 16.04,我发现 PTI 打开和关闭时的性能差异。
我的主机系统使用 CPU Ivybridge(2 核,4 HT)1.6GHz,16GB 内存。
我尝试用perf来分析差异来自哪里,如下
在 grub.cfg、
中 pti=off
# perf stat -e bus-cycles,cache-misses,cache-references,L1-dcache-load-misses,dTLB-load-misses,L1-dcache-prefetch-misses,LLC-prefetches ./test.sh
Performance counter stats for './test.sh':
774,986,827 bus-cycles (59.13%)
24,044,906 cache-misses # 12.803 % of all cache refs (58.17%)
187,799,652 cache-references (57.51%)
207,345,039 L1-dcache-load-misses (57.65%)
13,081,612 dTLB-load-misses (58.85%)
22,678,453 L1-dcache-prefetch-misses (59.62%)
24,089,506 LLC-prefetches (59.99%)
6.210151360 seconds time elapsed
并且,使用 pti=on(Linux 内核中的默认设置),我得到了,
# perf stat -e bus-cycles,cache-misses,cache-references,L1-dcache-load-misses,dTLB-load-misses,L1-dcache-prefetch-misses,LLC-prefetches ./test.sh
“./test.sh”的性能计数器统计信息:
1,205,903,578 bus-cycles (57.92%)
23,877,107 cache-misses # 13.167 % of all cache refs (57.31%)
181,340,147 cache-references (57.46%)
206,177,901 L1-dcache-load-misses (58.42%)
63,285,591 dTLB-load-misses (59.06%)
24,012,988 L1-dcache-prefetch-misses (58.65%)
24,928,410 LLC-prefetches (58.23%)
10.344839116 seconds time elapsed
test.sh 是要分析的程序,从上面的 perf 输出来看,test.sh 在 pti=on 时比在 pti=off 时花费更多时间,但事件输出不清楚差异在哪里来自.
在这种情况下还有其他性能事件可以提供帮助吗?
更新了更多性能事件。
PTI=关闭
# perf stat --repeat 5 -e cache-references,cache-misses,cpu-cycles,ref-cycles,faults,L1-dcache-loads,L1-dcache-load-misses,L1-icache-load-misses,branches,branch-misses,node-loads,node-load-misses,instructions,cs java mytest
Performance counter stats for 'java mytest' (5 runs):
8,711,306 cache-references ( +- 4.13% ) (48.49%)
1,290,234 cache-misses # 14.811 % of all cache refs ( +- 4.04% ) (49.44%)
709,587,381 cpu-cycles ( +- 1.44% ) (48.91%)
671,299,480 ref-cycles ( +- 1.95% ) (58.09%)
5,918 faults ( +- 0.12% )
185,928,475 L1-dcache-loads ( +- 4.29% ) (35.90%)
9,249,983 L1-dcache-load-misses # 4.98% of all L1-dcache hits ( +- 5.91% ) (27.84%)
4,718,632 L1-icache-load-misses ( +- 5.47% ) (20.83%)
106,021,866 branches ( +- 1.98% ) (31.56%)
4,487,091 branch-misses # 4.23% of all branches ( +- 5.35% ) (40.34%)
450,170 node-loads ( +- 9.18% ) (38.32%)
0 node-load-misses (40.62%)
509,344,631 instructions # 0.72 insns per cycle ( +- 5.59% ) (49.64%)
458 cs ( +- 2.05% )
0.216794242 seconds time elapsed ( +- 3.44% )
PTI=开
# perf stat --repeat 5 -e cache-references,cache-misses,cpu-cycles,ref-cycles,faults,L1-dcache-loads,L1-dcache-load-misses,L1-icache-load-misses,branches,branch-misses,node-loads,node-load-misses,instructions,cs java mytest
Performance counter stats for 'java mytest' (5 runs):
10,109,469 cache-references ( +- 4.10% ) (44.67%)
1,360,012 cache-misses # 13.453 % of all cache refs ( +- 2.16% ) (45.28%)
1,199,960,141 cpu-cycles ( +- 2.44% ) (46.13%)
1,086,243,141 ref-cycles ( +- 1.28% ) (54.64%)
5,923 faults ( +- 0.24% )
163,902,394 L1-dcache-loads ( +- 3.46% ) (41.91%)
8,588,505 L1-dcache-load-misses # 5.24% of all L1-dcache hits ( +- 5.59% ) (27.82%)
5,576,811 L1-icache-load-misses ( +- 3.87% ) (18.41%)
117,508,300 branches ( +- 3.98% ) (27.34%)
4,878,640 branch-misses # 4.15% of all branches ( +- 2.28% ) (35.55%)
585,464 node-loads ( +- 9.05% ) (34.55%)
0 node-load-misses (36.68%)
614,773,322 instructions # 0.51 insns per cycle ( +- 4.11% ) (46.10%)
476 cs ( +- 2.75% )
0.375871969 seconds time elapsed ( +- 0.81% )
不知道 "bus cycles" 实际测量的是什么事件。核心时钟周期通常更相关。
但无论如何,PTI=on 使每个系统调用(以及其他进入内核的入口)都变得更加昂贵,因为它必须修改 x86 CR3 控制寄存器(设置新页面 table 顶级指针).这就是它如何将用户-space 与内核页面-table 的访问隔离开来。
请注意 dTLB-load-misses
的大幅增加。借助进程上下文 ID (PCID) 支持,PTI 可能能够避免在每次进入内核时完全刷新 TLB。但我不知道细节。 (没有 PCID,替换页面 tables 会使整个 TLB 失效。)
您可以使用 strace -c
为系统调用计时。
使用perf record
(有足够的权限),你可以记录包含内核代码的样本,这样你就可以看到内核中哪些指令实际上花费了很长时间。 (转移到 CR3 也需要时间,Spectre 缓解也需要时间,它与 Meltdown 缓解 (PTI) 是分开的。但我认为 大部分 Meltdown 缓解成本是 TLB 遗漏内核中的时间和返回用户 space 后的时间,而不是来自实际的交换页面 tables。)
我正在 运行 java 测试 Ubuntu 16.04,我发现 PTI 打开和关闭时的性能差异。
我的主机系统使用 CPU Ivybridge(2 核,4 HT)1.6GHz,16GB 内存。
我尝试用perf来分析差异来自哪里,如下
在 grub.cfg、
中 pti=off# perf stat -e bus-cycles,cache-misses,cache-references,L1-dcache-load-misses,dTLB-load-misses,L1-dcache-prefetch-misses,LLC-prefetches ./test.sh
Performance counter stats for './test.sh':
774,986,827 bus-cycles (59.13%)
24,044,906 cache-misses # 12.803 % of all cache refs (58.17%)
187,799,652 cache-references (57.51%)
207,345,039 L1-dcache-load-misses (57.65%)
13,081,612 dTLB-load-misses (58.85%)
22,678,453 L1-dcache-prefetch-misses (59.62%)
24,089,506 LLC-prefetches (59.99%)
6.210151360 seconds time elapsed
并且,使用 pti=on(Linux 内核中的默认设置),我得到了,
# perf stat -e bus-cycles,cache-misses,cache-references,L1-dcache-load-misses,dTLB-load-misses,L1-dcache-prefetch-misses,LLC-prefetches ./test.sh
“./test.sh”的性能计数器统计信息:
1,205,903,578 bus-cycles (57.92%)
23,877,107 cache-misses # 13.167 % of all cache refs (57.31%)
181,340,147 cache-references (57.46%)
206,177,901 L1-dcache-load-misses (58.42%)
63,285,591 dTLB-load-misses (59.06%)
24,012,988 L1-dcache-prefetch-misses (58.65%)
24,928,410 LLC-prefetches (58.23%)
10.344839116 seconds time elapsed
test.sh 是要分析的程序,从上面的 perf 输出来看,test.sh 在 pti=on 时比在 pti=off 时花费更多时间,但事件输出不清楚差异在哪里来自.
在这种情况下还有其他性能事件可以提供帮助吗?
更新了更多性能事件。
PTI=关闭
# perf stat --repeat 5 -e cache-references,cache-misses,cpu-cycles,ref-cycles,faults,L1-dcache-loads,L1-dcache-load-misses,L1-icache-load-misses,branches,branch-misses,node-loads,node-load-misses,instructions,cs java mytest
Performance counter stats for 'java mytest' (5 runs):
8,711,306 cache-references ( +- 4.13% ) (48.49%)
1,290,234 cache-misses # 14.811 % of all cache refs ( +- 4.04% ) (49.44%)
709,587,381 cpu-cycles ( +- 1.44% ) (48.91%)
671,299,480 ref-cycles ( +- 1.95% ) (58.09%)
5,918 faults ( +- 0.12% )
185,928,475 L1-dcache-loads ( +- 4.29% ) (35.90%)
9,249,983 L1-dcache-load-misses # 4.98% of all L1-dcache hits ( +- 5.91% ) (27.84%)
4,718,632 L1-icache-load-misses ( +- 5.47% ) (20.83%)
106,021,866 branches ( +- 1.98% ) (31.56%)
4,487,091 branch-misses # 4.23% of all branches ( +- 5.35% ) (40.34%)
450,170 node-loads ( +- 9.18% ) (38.32%)
0 node-load-misses (40.62%)
509,344,631 instructions # 0.72 insns per cycle ( +- 5.59% ) (49.64%)
458 cs ( +- 2.05% )
0.216794242 seconds time elapsed ( +- 3.44% )
PTI=开
# perf stat --repeat 5 -e cache-references,cache-misses,cpu-cycles,ref-cycles,faults,L1-dcache-loads,L1-dcache-load-misses,L1-icache-load-misses,branches,branch-misses,node-loads,node-load-misses,instructions,cs java mytest
Performance counter stats for 'java mytest' (5 runs):
10,109,469 cache-references ( +- 4.10% ) (44.67%)
1,360,012 cache-misses # 13.453 % of all cache refs ( +- 2.16% ) (45.28%)
1,199,960,141 cpu-cycles ( +- 2.44% ) (46.13%)
1,086,243,141 ref-cycles ( +- 1.28% ) (54.64%)
5,923 faults ( +- 0.24% )
163,902,394 L1-dcache-loads ( +- 3.46% ) (41.91%)
8,588,505 L1-dcache-load-misses # 5.24% of all L1-dcache hits ( +- 5.59% ) (27.82%)
5,576,811 L1-icache-load-misses ( +- 3.87% ) (18.41%)
117,508,300 branches ( +- 3.98% ) (27.34%)
4,878,640 branch-misses # 4.15% of all branches ( +- 2.28% ) (35.55%)
585,464 node-loads ( +- 9.05% ) (34.55%)
0 node-load-misses (36.68%)
614,773,322 instructions # 0.51 insns per cycle ( +- 4.11% ) (46.10%)
476 cs ( +- 2.75% )
0.375871969 seconds time elapsed ( +- 0.81% )
不知道 "bus cycles" 实际测量的是什么事件。核心时钟周期通常更相关。
但无论如何,PTI=on 使每个系统调用(以及其他进入内核的入口)都变得更加昂贵,因为它必须修改 x86 CR3 控制寄存器(设置新页面 table 顶级指针).这就是它如何将用户-space 与内核页面-table 的访问隔离开来。
请注意 dTLB-load-misses
的大幅增加。借助进程上下文 ID (PCID) 支持,PTI 可能能够避免在每次进入内核时完全刷新 TLB。但我不知道细节。 (没有 PCID,替换页面 tables 会使整个 TLB 失效。)
您可以使用 strace -c
为系统调用计时。
使用perf record
(有足够的权限),你可以记录包含内核代码的样本,这样你就可以看到内核中哪些指令实际上花费了很长时间。 (转移到 CR3 也需要时间,Spectre 缓解也需要时间,它与 Meltdown 缓解 (PTI) 是分开的。但我认为 大部分 Meltdown 缓解成本是 TLB 遗漏内核中的时间和返回用户 space 后的时间,而不是来自实际的交换页面 tables。)