Pourquoi est-ce que `change_protection` hog CPU tout en chargeant une grande quantité de données dans la RAM?

Nous avons construit une firebase database en mémoire, qui mange environ 100-150G de RAM dans un seul Vec , qui est rempli comme ceci:

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

perf top montre que le temps est principalement passé dans cette fonction “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_ssortingng 0.52% iron [.] memcpy@plt 

L’utilisation du processeur de cette fonction augmente à mesure que de plus en plus de données sont chargées dans la mémoire vive:

  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 

Le code s’exécute sur une instance AWS EC2 r3.8xlarge, et la page énorme transparente est déjà désactivée.

 [~]$ 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: 

kernel

 3.14.35-28.38.amzn1.x86_64 

la vraie question est pourquoi y at-il tant de frais généraux dans cette fonction?

Cela semble être un problème de système d’exploitation, plutôt qu’un problème avec cette fonction spécifique de la rouille.

La plupart des systèmes d’exploitation (y compris Linux) utilisent la pagination à la demande . Par défaut, Linux n’allouera pas de pages physiques pour la mémoire nouvellement allouée. Au lieu de cela, il allouera une seule page zéro avec des permissions en lecture seule pour toute la mémoire allouée (c.-à-d., Toutes les pages de mémoire virtuelle pointeront sur cette page de mémoire physique unique).

Si vous essayez d’écrire dans la mémoire, une erreur de page se produira, une nouvelle page sera allouée et ses permissions seront définies en conséquence.

Je suppose que vous voyez cet effet dans votre programme. Si vous essayez de faire la même chose une seconde fois, cela devrait être beaucoup plus rapide. Il existe également des moyens de contrôler cette stratégie via sysctl : https://www.kernel.org/doc/Documentation/vm/overcommit-accounting .

Vous ne savez pas pourquoi vous avez désactivé THP, mais dans ce cas, les pages volumineuses peuvent vous aider puisque le changement de protection se produira une fois pour chaque grande page (2 Mo) au lieu d’une fois par page normale (4 Ko).