为什么`change_protection`在将大量数据加载到RAM时会占用CPU?

Dap*_*eng 11 linux rust

我们已经构建了一个内存数据库,它在一个Vec中占用大约100-150G RAM ,其填充方式如下:

let mut result = Vec::with_capacity(a_very_large_number);
while let Ok(n) = reader.read(&mut buffer) {
    result.push(...);
}
Run Code Online (Sandbox Code Playgroud)

perf top 表明时间主要花在这个"change_protection"函数上:

Samples: 48K of event 'cpu-clock', Event count (approx.): 694742858
 62.45%  [kernel]              [k] change_protection
 18.18%  iron                  [.] database::Database::init::h63748
  7.45%  [kernel]              [k] vm_normal_page
  4.88%  libc-2.17.so          [.] __memcpy_ssse3_back
  0.92%  [kernel]              [k] copy_user_enhanced_fast_string
  0.52%  iron                  [.] memcpy@plt
Run Code Online (Sandbox Code Playgroud)

随着越来越多的数据加载到RAM中,此函数的CPU使用率也在增长:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
12383 iron      20   0  137g  91g 1372 D 76.1 37.9  27:37.00 iron
Run Code Online (Sandbox Code Playgroud)

代码在r3.8xlarge AWS EC2实例上运行,透明的hugepage已被禁用.

[~]$ cat /sys/kernel/mm/transparent_hugepage/defrag
always madvise [never]
[~]$ cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]
Run Code Online (Sandbox Code Playgroud)

cpuinfo中

processor   : 0
vendor_id   : GenuineIntel
cpu family  : 6
model       : 62
model name  : Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz
stepping    : 4
microcode   : 0x428
cpu MHz     : 2500.070
cache size  : 25600 KB
physical id : 0
siblings    : 16
core id     : 0
cpu cores   : 8
apicid      : 0
initial apicid  : 0
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology eagerfpu pni pclmulqdq ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm xsaveopt fsgsbase smep erms
bogomips    : 5000.14
clflush size    : 64
cache_alignment : 64
address sizes   : 46 bits physical, 48 bits virtual
power management:
Run Code Online (Sandbox Code Playgroud)

核心

3.14.35-28.38.amzn1.x86_64
Run Code Online (Sandbox Code Playgroud)

真正的问题是为什么这个功能有这么多的开销?

yni*_*ous 1

这似乎是操作系统问题,而不是特定 Rust 函数的问题。

大多数操作系统(包括 Linux)都使用请求分页。默认情况下,Linux不会为新分配的内存分配物理页。相反,它将为所有已分配的内存分配具有只读权限的单个零页(即,所有虚拟内存页将指向该单个物理内存页)。

如果您尝试写入内存,则会发生页面错误,将分配新页面,并相应地设置其权限。

我猜您在程序中看到了这种效果。如果你尝试第二次做同样的事情,它应该会快得多。还有一些方法可以通过以下方式控制此策略sysctl: https: //www.kernel.org/doc/Documentation/vm/overcommit-accounting

不确定为什么禁用 THP,但在这种情况下,大页面可能会帮助您,因为保护更改将针对每个大页面 (2Mib) 发生一次,而不是每个普通页面 (4KiB) 发生一次。