KVM ( 5 ) ~ EPT ( Extended Page Tables )~

June 19, 2018

仮想化環境における MMU

仮想化環境下で扱うメモリアドレスには以下の種類がある。

  • ゲスト仮想アドレス: ゲスト OS から見える仮想アドレス
  • ゲスト物理アドレス: ゲスト OS から見える物理アドレス。ハイパーバイザーがエミュレートした擬似物理アドレス
  • ホスト仮想アドレス: ホスト OS から見える仮想アドレス
  • ホスト物理アドレス: ホスト OS から見える物理アドレス

上記の通り、ゲスト物理アドレスはハイパーバイザーがエミュレートした擬似的なものとなる。このゲスト物理アドレスでは実際の物理メモリにアクセスできないため、ホスト物理アドレスに変換する必要がある。
Xen や KVM ではハイパーバイザーがシャドウページングという方法で MMU のエミュレーションを行う。
シャドウページングでは、ハイパーバイザーがゲスト OS のページテーブルの設定処理を検出して、ゲスト OS のページテーブル設定処理をエミュレートする。ハイパーバイザーはゲスト仮想アドレスをホスト物理アドレスに変換するテーブル (シャドウページテーブル) を設定する。

EPT とは

EPT (Extended Page Talbes) は Intel の CPU に実装されているハードウェアの仮想化支援機構。MMU をハードウェア側で仮想化することにより、ハイパーバイザーのオーバーヘッドを軽減し、ゲスト OS の処理を高速化することができる。AMD の CPU にも NPT (Nested Page Tables) という同様の機能がある。
EPT ではハイパーバイザーがゲスト物理アドレスをホスト物理アドレスに変換するためのテーブルを用意する。EPT を有効にした場合は、ゲスト OS 稼働時は CPU がこのテーブルを利用してゲスト仮想アドレスをホスト物理アドレスに自動的に変換する。

CPU に EPT が実装されている場合、/proc/cpuinfo の flags に ept が表示される。

$ cat /proc/cpuinfo | head -25  
processor	    : 0  
vendor_id	    : GenuineIntel  
cpu family	    : 6  
model		      : 79  
model name	      : Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz  
stepping	      : 1  
microcode	      : 0xb000022  
cpu MHz		      	: 2986.322  
cache size		: 46080 KB  
physical id		: 0  
siblings : 36  
core id	   : 0  
cpu cores  : 18  
apicid	     : 0  
initial apicid : 0  
fpu	       : yes  
fpu_exception  : yes  
cpuid level    : 20  
wp    	       : yes  
flags	       	 : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts  
bogomips	 : 4599.88  
clflush size	 : 64  
cache_alignment	 : 64  
address sizes	 : 46 bits physical, 48 bits virtual  
power management:  

Linux の最新ディストリビューションでは基本的に EPT は有効になっている。
KVM を利用した場合は、/sys/module/kvm_intel/parameters/ept が Y であれば EPT が有効になっている。

$ cat /sys/module/kvm_intel/parameters/ept  
Y  

EPT を感じる

以下のようなコードを用意して、EPT の効力を肌で感じてみましょう。

EPT-Test.c
#include <unistd.h>  
#include <time.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <err.h>  
  
#define BUFFER_SIZE (1024*1024*1024)  
#define PAGE_SIZE 4096  
  
int main(void) {  
  char *p;  
  time_t t;  
  char *s;  
  
  t = time(NULL);  
  s = ctime(&amp;t);  
  
  p = malloc(BUFFER_SIZE);  
  if (p == NULL)  
    err(EXIT_FAILURE, "malloc() failed.");  
  
  printf("%.*s: allocated %d MB.\n", (int)(strlen(s) - 1), s, BUFFER_SIZE/(1024*1024));  
  
  int i = 0;  
  int j = 0;  
  for (i = 0; i < BUFFER_SIZE; i += PAGE_SIZE) {  
    p[i] = 0;  
      
    if (j % 4096== 0)   
      printf("%.*s: touched %d MB.\n", (int)(strlen(s) - 1), s, i / (1024*1024));  
      
    j++;  
  }  
  printf("%.*s: touched %d MB.\n", (int)(strlen(s) - 1), s, BUFFER_SIZE / (1024*1024));  
  
  exit(EXIT_SUCCESS);  
}  

基本的には、メモリを確保して順番にアクセスしているだけです。

それではまず EPT 無しの状態で測ってみましょう。
まずは running なゲスト OS を shutdown します。

$ sudo virsh list --all  
 Id    Name                           State  
----------------------------------------------------  
 9     CentOS7                        running  
  
$ sudo virsh shutdown CentOS7  

次に EPT を無効化します。

$ sudo rmmod kvm_intel  
$ sudo modprobe kvm_intel ept=0  
$ cat /sys/module/kvm_intel/parameters/ept  
N  

それでは実際にログインしてコードを実行しましょう。

$ sudo virsh start CentOS7 --console  
$ gcc -o EPT-Test EPT-Test.c  
$ time ./EPT-Test  
Tue Jun 19 03:47:26 2018: allocated 1024 MB.  
Tue Jun 19 03:47:26 2018: touched 0 MB.  
Tue Jun 19 03:47:26 2018: touched 16 MB.  
Tue Jun 19 03:47:26 2018: touched 32 MB.  
Tue Jun 19 03:47:26 2018: touched 48 MB.  
Tue Jun 19 03:47:26 2018: touched 64 MB.  
  
...  
  
Tue Jun 19 03:47:26 2018: touched 960 MB.  
Tue Jun 19 03:47:26 2018: touched 976 MB.  
Tue Jun 19 03:47:26 2018: touched 992 MB.  
Tue Jun 19 03:47:26 2018: touched 1008 MB.  
Tue Jun 19 03:47:26 2018: touched 1024 MB.  
  
real    0m1.195s  
user    0m0.420s  
sys     0m0.769s  

real が 1.195s でした。

次に EPT を有効化した状態で測定します。
一旦、再度ゲスト OS を停止し、EPT を有効化します。

$ sudo virsh shutdown CentOS7  
$ sudo rmmod kvm_intel  
$ sudo modprobe kvm_intel ept=1  
$ cat /sys/module/kvm_intel/parameters/ept  
Y  

ゲスト OS を起動し、コードを実行します。

$ sudo virsh start CentOS7 --console  
$ time ./EPT-Test  
Tue Jun 19 03:49:55 2018: allocated 1024 MB.  
Tue Jun 19 03:49:55 2018: touched 0 MB.  
Tue Jun 19 03:49:55 2018: touched 16 MB.  
Tue Jun 19 03:49:55 2018: touched 32 MB.  
Tue Jun 19 03:49:55 2018: touched 48 MB.  
Tue Jun 19 03:49:55 2018: touched 64 MB.  
  
...  
  
Tue Jun 19 03:49:55 2018: touched 960 MB.  
Tue Jun 19 03:49:55 2018: touched 976 MB.  
Tue Jun 19 03:49:55 2018: touched 992 MB.  
Tue Jun 19 03:49:55 2018: touched 1008 MB.  
Tue Jun 19 03:49:55 2018: touched 1024 MB.  
  
real    0m0.337s  
user    0m0.005s  
sys     0m0.330s  

real が 0.337s でした。
測定は 1 度で、本当は統計を取るべきですが、これは驚異的ですね。

UnixBench もやってみます。UnixBench に関してはここが詳しいです。
まずは EPT 無しで。

$ yum -y install perl perl-Time-HiRes make gcc git  
$ git clone https://github.com/kdlucas/byte-unixbench  
$ cd byte-unixbench/UnixBench  
$ ./Run  

結果は以下。

Benchmark Run: Tue Jun 19 2018 04:42:43 - 05:11:43  
1 CPU in system; running 1 parallel copy of tests  
  
Dhrystone 2 using register variables       35990073.6 lps   (10.0 s, 7 samples)  
Double-Precision Whetstone                     3462.4 MWIPS (15.3 s, 7 samples)  
Execl Throughput                                407.1 lps   (29.6 s, 2 samples)  
File Copy 1024 bufsize 2000 maxblocks        116965.5 KBps  (30.0 s, 2 samples)  
File Copy 256 bufsize 500 maxblocks           29485.7 KBps  (30.0 s, 2 samples)  
File Copy 4096 bufsize 8000 maxblocks        449854.3 KBps  (30.0 s, 2 samples)  
Pipe Throughput                              128515.3 lps   (10.0 s, 7 samples)  
Pipe-based Context Switching                  44579.4 lps   (10.0 s, 7 samples)  
Process Creation                               1300.7 lps   (30.0 s, 2 samples)  
Shell Scripts (1 concurrent)                    787.3 lpm   (60.1 s, 2 samples)  
Shell Scripts (8 concurrent)                    107.9 lpm   (60.3 s, 2 samples)  
System Call Overhead                          71169.6 lps   (10.0 s, 7 samples)  
  
System Benchmarks Index Values               BASELINE       RESULT    INDEX  
Dhrystone 2 using register variables         116700.0   35990073.6   3084.0  
Double-Precision Whetstone                       55.0       3462.4    629.5  
Execl Throughput                                 43.0        407.1     94.7  
File Copy 1024 bufsize 2000 maxblocks          3960.0     116965.5    295.4  
File Copy 256 bufsize 500 maxblocks            1655.0      29485.7    178.2  
File Copy 4096 bufsize 8000 maxblocks          5800.0     449854.3    775.6  
Pipe Throughput                               12440.0     128515.3    103.3  
Pipe-based Context Switching                   4000.0      44579.4    111.4  
Process Creation                                126.0       1300.7    103.2  
Shell Scripts (1 concurrent)                     42.4        787.3    185.7  
Shell Scripts (8 concurrent)                      6.0        107.9    179.8  
System Call Overhead                          15000.0      71169.6     47.4  
                                                                   ========  
System Benchmarks Index Score                                         221.7  

次に EPT を有効にして測定した結果が以下です。

Benchmark Run: Tue Jun 19 2018 04:08:03 - 04:36:57  
1 CPU in system; running 1 parallel copy of tests  
  
Dhrystone 2 using register variables       36249477.4 lps   (10.0 s, 7 samples)  
Double-Precision Whetstone                     3447.0 MWIPS (15.4 s, 7 samples)  
Execl Throughput                               3686.7 lps   (30.0 s, 2 samples)  
File Copy 1024 bufsize 2000 maxblocks        694441.0 KBps  (30.0 s, 2 samples)  
File Copy 256 bufsize 500 maxblocks          179576.5 KBps  (30.0 s, 2 samples)  
File Copy 4096 bufsize 8000 maxblocks       2358772.8 KBps  (30.0 s, 2 samples)  
Pipe Throughput                              937300.5 lps   (10.0 s, 7 samples)  
Pipe-based Context Switching                 182325.2 lps   (10.0 s, 7 samples)  
Process Creation                              13425.7 lps   (30.0 s, 2 samples)  
Shell Scripts (1 concurrent)                   5592.4 lpm   (60.0 s, 2 samples)  
Shell Scripts (8 concurrent)                    767.2 lpm   (60.0 s, 2 samples)  
System Call Overhead                         968740.3 lps   (10.0 s, 7 samples)  
  
System Benchmarks Index Values               BASELINE       RESULT    INDEX  
Dhrystone 2 using register variables         116700.0   36249477.4   3106.2  
Double-Precision Whetstone                       55.0       3447.0    626.7  
Execl Throughput                                 43.0       3686.7    857.4  
File Copy 1024 bufsize 2000 maxblocks          3960.0     694441.0   1753.6  
File Copy 256 bufsize 500 maxblocks            1655.0     179576.5   1085.1  
File Copy 4096 bufsize 8000 maxblocks          5800.0    2358772.8   4066.8  
Pipe Throughput                               12440.0     937300.5    753.5  
Pipe-based Context Switching                   4000.0     182325.2    455.8  
Process Creation                                126.0      13425.7   1065.5  
Shell Scripts (1 concurrent)                     42.4       5592.4   1319.0  
Shell Scripts (8 concurrent)                      6.0        767.2   1278.7  
System Call Overhead                          15000.0     968740.3    645.8  
                                                                   ========  
System Benchmarks Index Score                                        1146.3  

非常に大きな違いです。


 © 2023, Dealing with Ambiguity