VirtualBox 5 で利用可能になった Paravirtualization 機能 kvmclock を使う

Oracle に買収されてからずっと 4.x のまま大きな進化が無いのではないかと心配した時期もありましたが、とうとう VirtualBox 5 が出ましたね。 買収後は vagrant や、最近だと boot2docker などで重要度が大きく増しているので、今後の発展にも期待しています。

さて、 VirtualBox 5 の目玉の新機能といえば Paravirtualization 機能がいろいろな所で表に出てますね。これって何なんでしょう?

一般論を言えば、ホストマシンがハードウェアをエミュレートするのが HVM、ゲストマシンにAPIを提供するのが Paravirtualization (PVM) なので、CPU等の仮想化支援機能を使える場所を除いて一般的には PVM の方が高速化・効率化が可能です。

VirtualBox でも 4.x 時代からNICが virtio に対応していました。ネットワークインターフェイスの種類として virtio を利用すれば、実在するハードウェアのNICエミューレートするために無駄なコストを払う必要がなく、すこし軽くなるはずです。

ということは、 VirtualBox 5 で何か PVM のデバイスが追加されたはずです。(個人的にはブロックデバイスが virtio になって高速化されると嬉しいなと期待していました。) 調べてみたところ、ドキュメントにちゃんと載っていました。

10.4. Paravirtualization providers

KVM: Presents a Linux KVM hypervisor interface which is recognized by Linux kernels starting with version 2.6.25. VirtualBox's implementation currently supports paravirtualized clocks and SMP spinlocks. This provider is recommended for Linux guests.

Paravirtualization Provider に KVM を指定すると、 clock と spinlock の PVM が利用できるようです。

このうち clock については、過去に VirtualBox にマルチコアを割り当てると gettimeofday 等のシステムコールが非常に遅くなるという問題にぶつかったことがあり、最近でもよく EC2 などでマルチスレッドプログラムのベンチマークをするときは clocksource を xen から tsc に変更してるなど、個人的に非常に分かりやすく効果がありそうなので、試してみました。

まず、 Paravirtualization Provider に KVM を指定して (GUI では System > Accelaration にあります) Ubuntu 14.04.2 を起動します。

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock tsc acpi_pm
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
kvm-clock

kvm-clock が利用可能で、デフォルトで利用されていることが解ります。

次に、以下のプログラムで gettimeofday の速度を計測してみます。

#include <sys/time.h>
#include <stdio.h>

int main()
{
    struct timeval tv;
    for (int i=0; i<1000000; i++) {
        int r = gettimeofday(&tv, 0);
        if (r < 0) {
            perror("gettimeofday");
            return 1;
        }
    }
    return 0;
}
$ gcc -O2 -o t t.c -std=c99
$ time ./t
./t  0.05s user 0.00s system 99% cpu 0.058 total

非常に速いですね。 tsc のように、実際にはシステムコールを発行しないようです。 strace しても gettimeofday は確認できませんでした。

tsc と速度を比較してみます。

$ sudo sh -c "echo tsc > /sys/devices/system/clocksource/clocksource0/current_clocksource"
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
$ time ./t
./t  0.02s user 0.00s system 95% cpu 0.023 total

流石に tsc よりは倍くらい遅いのかな。最後に acpi とも比較してみます。 VirtualBox 4 ではマルチコアだとすごい遅かった覚えがあるのですが、さて。

$ sudo sh -c "echo acpi_pm > /sys/devices/system/clocksource/clocksource0/current_clocksource"
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
acpi_pm
$ time ./t
./t  0.00s user 1.64s system 99% cpu 1.645 total

gettimeofday 1回あたり 1.64μs. VirtualBox 4 で計測してなかったので比べられないけど、速くなってるかもしれません。

とはいえ、 kvm-clock と比べて30倍、 tsc と比べて80倍遅いので、 gettimeofday() 等のクロック系システムコールを多用するプログラムを動かす場合は避けたいところです。

クロックソースについてはよく知らないので思い込みですが、tsc がCPUのベースクロック(あるいはそれに類する、同期回路を動かすためのクロック) を利用している分、実時間を計測するには不安定(たぶん1日当たりの誤差は1000円の腕時計の方が小さい)なのに対して、 kvm-clock だと自動的にホスト側の時計に同期してくれることを期待しています。

tsc に比べたら倍くらい遅いと言っても、もともと tsc を使った時の gettimeofday が他のシステムコールに比べてタダ同然だったので、気にせず kvm-clock を使って置けば良さそう。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。