Почему "change_protection" обрабатывает CPU при загрузке большого объема данных в оперативную память?
Мы создали базу данных в памяти, которая ест около 100-150G RAM в одном Vec, который заполняется следующим образом:
let mut result = Vec::with_capacity(a_very_large_number);
while let Ok(n) = reader.read(&mut buffer) {
result.push(...);
}
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 [.] [email protected]
Использование ЦП этой функции возрастает по мере того, как все больше и больше данных загружается в ОЗУ:
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
Код работает на экземпляре r3.8xlarge AWS EC2, а прозрачная огромная страница уже отключена.
[~]$ cat /sys/kernel/mm/transparent_hugepage/defrag
always madvise [never]
[~]$ cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]
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:
Ядро
3.14.35-28.38.amzn1.x86_64
реальный вопрос - почему в этой функции так много накладных расходов?
Ответы
Ответ 1
Кажется, это проблема ОС, а не проблема с этой специфической функцией ржавчины.
Большинство ОС (включая Linux) используют запрос подкачки. По умолчанию Linux не будет выделять физические страницы для вновь выделенной памяти. Вместо этого он будет выделять одну нулевую страницу с разрешениями только для чтения для всей выделенной памяти (т.е. Все страницы виртуальной памяти будут указывать на эту страницу отдельной физической памяти).
Если вы попытаетесь записать в память, произойдет ошибка страницы, будет выделена новая страница, и соответствующие разрешения будут установлены соответствующим образом.
Я предполагаю, что вы видите этот эффект в своей программе. Если вы попытаетесь сделать то же самое во второй раз, это должно быть намного быстрее. Существуют также способы управления этой политикой через sysctl
: https://www.kernel.org/doc/Documentation/vm/overcommit-accounting.
Не знаете, почему вы отключили THP, но в этом случае вам могут помочь большие страницы, так как изменение защиты произойдет один раз для каждой большой страницы (2Mib) вместо одного на обычную страницу (4KiB).