LXCコンテナの中でDockerを使用するための設定

/var/lib/lxc/[container名]/configに以下の設定を追加し、コンテナを(再)起動する。

なるべく安全な方法

lxc.apparmor.profile = generated
lxc.apparmor.allow_nesting = 1

とにかく権限を与える方法

上記で駄目な場合、以下ならほとんどのケースで起動可能。上記にlxc.cap.drop =を加えて、Dockerを起動できる場合もあり。
lxc.apparmor.profile = unconfined
lxc.cgroup.devices.allow = a
lxc.cap.drop =

Ethernet、UDP、TCPパケットの構造

Ethernetパケット

ヘッダサイズ: 14B (802.1Q VLANタグがある場合18B)、チェックサムサイズ: 4B
サイズ (B)説明
6送信先MAC
6送信元MAC
[4]802.1Qタグ (VLANタグ)
2Ethernet type/size. IPv4:0x0800, IPv6:0x86dd
-ペイロード(IPなど)
4FCS (Frame Check Sequence; CRC)
参考: Wikipedia: イーサーネットフレーム

IPv4

ヘッダサイズ: 20B (拡張情報を除く)
サイズ (B)説明
1バージョン(MSB 4bit)、ヘッダ長(LSB 4bit) 4オクテット単位
1サービス種別
2全長(IPヘッダを含む)
2識別子
2フラグ(MSB 3bit)、フラグメントオフセット(LSB 13bit)
1TTL (Time to Live)
1上位プロトコル. ICMP: 0x01, TCP:0x06, UDP:0x11
2IPヘッダのチェックサム
4送信元アドレス
4送信先アドレス
[2]拡張情報
-データ
参考: Wikipedia: IPv4

UDP

ヘッダサイズ: 8B
サイズ (B)説明
2送信元ポート
2送信先ポート
2データ長
2チェックサム
-ペイロード(IPなど)
参考: Wikipedia: UDP

TCP

ヘッダサイズ: 20B >
サイズ (B)説明
2送信元ポート
2送信先ポート
4シーケンス番号
4確認応答番号
2ヘッダ長(MSB 4bit)[32bit単位]、予約(3bit)、フラグ(9bit)
2Windowサイズ
2チェックサム
2緊急ポインタ
[4-40]オプション
-ペイロード
参考: Wikipedia: TCP

QEMUでLinuxカーネルをGDBでデバッグする

QEMUの起動

以下の2つのオプションを使う
オプション説明
-sTCP 1234番でGDBのリモートコクションをオープンします
-SGDBにアタッチされるまでゲストの実行を待機します。カーネルの起動後にアタッチしても問題ない場合、不要です。

Linuxの起動オプション

QEMUで起動されるLinuxカーネルのコマンドラインオプションにnokaslrを付与する。通常、GRUBなどのブートローダーに指定する。このオプションは、カーネルの関数の配置アドレスのランダム化を抑止する。このオプションを指定しない場合、カーネルのデバッグ情報に記載されているアドレスと、実際のアドレスがことなり、GDBの操作が期待どおりに行われない。

デバッグ情報の入手

ターゲットのカーネルに対応するデバッグ情報ファイルを取得する。

Debianの場合の例

sudo apt install linux-image-5.10.0-22-amd64-dbg
あるいは、APT repositoryから直接取得する。
wget http://ftp.debian.org/debian/pool/main/l/linux/linux-image-5.10.0-22-amd64-dbg_5.10.178-3_amd64.deb
sudo dpkg -i linux-image-5.10.0-22-amd64-dbg_5.10.178-3_amd64.deb

自分でビルドしたカーネルの場合

カーネルのビルドディレクトリにvmlinuxがあるのでそれを使う。ただし、ビルド時、以下の点に留意
  • 以下のCONFIGを有効化
    • CONFIG_DEBUG_INFO_DWARF5、または、CONFIG_DEBUG_INFO_DWARF4
    • CONFIG_GDB_SCRIPTS
    • CONFIG_FRAME_POINTER
  • 以下のCONFIGを無効化
    • CONFIG_DEBUG_INFO_REDUCED
参考: https://docs.kernel.org/dev-tools/gdb-kernel-debugging.html

GDBの起動

デバッグ情報付きのvmlinuxを引数にしてGDBを起動する
gdb /usr/lib/debug/vmlinux-5.10.0-22-amd64
GDB起動後、以下のコマンドを実行する
(gdb) target remote [QEMU起動マシンのアドレス]:1234

グローバル変数の表示例

(gdb) p jiffies
$1 = 4295462764

ブレークポイントの例

do_sys_openにブレークポイントを仕掛けて、ゲストOSでファイルを開く操作をすると以下のようにブレークする
(gdb) b do_sys_open
Breakpoint 2 at 0xffffffff812d6ff0: do_sys_open. (11 locations)
(gdb) c
Continuing.

Breakpoint 2.4, do_sys_open (mode=0, flags=557056, filename=0x7f153b8cabe7 "/etc/ld.so.cache", dfd=-100) at fs/open.c:1201
1201    fs/open.c: No such file or directory.
(gdb) bt
#0  do_sys_open (mode=0, flags=557056, filename=0x7f153b8cabe7 "/etc/ld.so.cache", dfd=-100) at fs/open.c:1201
#1  __do_sys_openat (mode=0, flags=557056, filename=0x7f153b8cabe7 "/etc/ld.so.cache", dfd=-100) at fs/open.c:1218
#2  __se_sys_openat (mode=0, flags=524288, filename=139729170115559, dfd=4294967196) at fs/open.c:1213
#3  __x64_sys_openat (regs=<optimized out>) at fs/open.c:1213
#4  0xffffffff818f7e00 in do_syscall_64 (nr=<optimized out>, regs=0xffffc9000027bf58) at arch/x86/entry/common.c:46
#5  0xffffffff81a000a9 in entry_SYSCALL_64 () at /build/linux-ts3hOX/linux-5.10.178/arch/x86/entry/entry_64.S:125

実行中のプロセスのtask_structの表示例

上記のブレーク例は、lsを実行したときのものである。このときのプロセスが確かにlsであることと、そのプロセスIDを確認する。
(gdb) set $curr = (struct task_struct **)($gs_base + (void *)&current_task)
(gdb) p $curr
$2 = (struct task_struct **) 0xffff88803d01fbc0
(gdb) p $curr->comm
$3 = "ls\000h\000\000\000)\000\000\000\000\000\000\000"
(gdb) p $curr->pid
$4 = 552
上記について補足する。実行中のプロセスのtask_struct構造体へのポインタは、CPUごとの変数領域の中にあり、グローバル変数current_taskの値は、その領域におけるオフセットである。また、CPUごとの変数領域の開始位置はGSセグメントレジスタに格納されている。そのため、両者を加算したアドレス位置に実行中のプロセスに関するtask_struct領域へのポインタが格納されている。

参考

ふるさと納税でおおすすめの返礼品: バンバーグ編

ハンバーグ大好きな私が、ふるさと納税の返礼品でおすすめのハンバーグを紹介します。一概に優劣はつけにくいが、好きなものから順に紹介する。

兵庫県淡路市: 淡路島玉ねぎのBIG生ハンバーグ 200g×10個

玉ねぎ入り。けど玉ねぎが強すぎず、いい意味で肉感が減少して、すこしあっさり目で飽きない。

岩手県一関市:《格之進》金格ハンバーグ120g×6個

やや小ぶりだが、その分、美味しさが凝縮されている。高いレベルでバランスが良い。

佐賀県吉野ヶ里町: 佐賀牛入り 黒毛和牛 ハンバーグ 12個 大容量 1.8kg (150g×12個)

肉感が高く、肉の香り、肉汁が溢れ出している。とにかく肉好きにはおすすめ。

福岡県新宮町: どーんと3㎏!4種ハンバーグセット【150g×20個】

レンジでできるのありがたい。

QEMUでホストのディレクトリをゲストに9pで共有する

ホスト側

QEMUに起動時に次のオプションを付与する。青字のIDは適宜変更可能。紫のタグについても適宜変更。
-fsdev local,path=$(pwd),security_model=mapped-xattr,id=fsdev1 \
-device virtio-9p-pci,fsdev=fsdev1,mount_tag=tag1

参考

パラメータの詳細

QEMU起動の全体のパラメータ

ゲスト側

tag1の部分は、QEMU起動時にホスト側で指定した文字列と同じものを指定します。
mount -t 9p -o trans=virtio tag1 /mnt/dir_tag1 -oversion=9p2000.L

プログラミング言語の実行速度比較(2023/5)

はじめに

先月、プログラミング言語の実行速度比較(2023/4)という記事を投稿した。 思ったより多くの方に見ていただき、有用なコメントやPull Requestをいただいたので、それらを踏まえ以下の更新を行い結果を再投稿する。
  • 明示的な型をもつ言語では、それぞれ32bit、64bit長型の変数を使った場合についてそれぞれ測定
  • C言語については前回の結果で固定長配列とrealloc()を使った場合で大きな違いがなかったのでrealloc()版のみエントリ
  • C/C++では、実用上ほぼ使用されない最適化なしをドロップ
  • Go版では、@kaoriyaさんから、数値の型(int)をint32に変えると性能が向上する旨のコメントを頂いた。Go版もint32とint64で測定した
  • Scala版では、@windymeltさんから、GraalVMを使った場合の性能向上について情報を頂いた。JVM上で動作するプログラムについては、実行環境としてGraalVMを使った場合の結果も追加した
  • PHP版では、sj-iさんからJITを有効にするPull Requestをいただいたのでそれも追加
  • Ruby版を追加
  • 前回、低消費電力が特徴のIntel Core i7-8559Uで測定を行った。これに加えて、比較的能力の高いIntel Core i9-12900Kでも測定を行った
また、当然だがこの投稿で使った素数の算出は様々なワークロードの中のひとつ。実用上、CPUだけぶん回しているタスクはむしろ少数派かも。

速度比較

方法

  • シングルスレッドで、逐次既知の素数で割ることで素数を100,000,000まで求める(ソースはここ)。いわゆる試し割り法
  • その際の実行時間と最大物理メモリ使用量を計測する
  • 素数の数は5,761,455個なので、数値が32bitの場合、それらを格納するには少なくとも約22MiBのメモリが必要(64bitの場合、約44MiB)
  • 言語の比較なので、なるべくユーザモードで動作するコードとして素数の算出を選んだ。カーネルが関係するのはメモリのアロケーション時が中心だが、実行時間の1%もない

評価環境

CPU1 (C1)

CPU2 (C2)

OS

  • Debian 12 (sid)

結果

表の横幅が画面に収まりきらない場合、横スクロールしてご覧ください。
Category 言語 数値型 時間 (sec) 最大使用
物理メモリ
(MiB)
算出素数格納
コンテナ
備考
C1 C2 C1 C2
AOT
Compile
C int 12.6 7.5 23.6 23.6 realloc() gcc 12.2.0 最適化-O3
C long 41.6 12.5 45.5 45.6 realloc()
C++ int 12.7 7.7 36.5 36.5 std::vector
C++ long 41.9 12.5 67.6 68.7 std::vector
Rust i32 14.2 7.6 25.8 24.0 Vec rust 1.63.0
Rust i64 14.1 7.6 46.2 46.0 Vec
Golang int32 19.2 7.7 105.8 92.4 slice C1: Go 1.19.6, C2: Go 1.19.8
Golang int64 48.9 12.7 171.5 202.0 slice
JVM Java int (Integer) 19.5 8.2 230.4 248.0 ArrayList
Java long (Long) 53.0 13.3 269.1 294.1 ArrayList
Java (GraalVM) int 21.8 8.2 247.2 260.1 ArrayList GraalVM CE 22.3.2
Java (GraalVM) long 49.3 13.2 281.2 306.4 ArrayList
Kotlin Int 19.6 8.1 256.1 276.6 List Kotlin 1.8.20
Kotlin Long 51.8 13.1 294.1 323.0 List
Kotlin (GraalVM) Int 17.3 8.1 272.0 294.8 List Kotlin 1.8.20, GraalVM CE 22.3.2
Kotlin (GraalVM) Long 46.6 13.1 310.2 330.9 List
Scala Int 24.9 9.5 1081.8 1449.0 mutable.ArrayBuffer Scala 3.2.2
Scala Long 59.8 14.3 662.7 1430.6 mutable.ArrayBuffer
Scala (GraalVM) Int 19.3 8.2 245.0 321.8 mutable.ArrayBuffer Scala 3.2.2, GraalVM CE 22.3.2
Scala (GraalVM) Long 50.8 13.4 609.8 706.0 mutable.ArrayBuffer
Script JavaScript (Node.js) Number 17.3 7.8 196.1 203.2 Array Node.js 18.13.0
PHP integer 108.7 43.3 149.1 149.2 array PHP 8.2.2
PHP (JIT) integer 71.9 13.4 152.4 152.5 array
Python3 int 289.4 160.0 231.5 232.8 list Python 3.11.2
Ruby Integer 380.0 160.8 57.6 57.7 Array Ruby 3.1.2

グラフ

薄い青: AOT Compile (32bit)  濃い青: AOT Compile (64bit)  薄い赤紫: JVM (32bit)  濃い赤紫: JVM (64bit)  薄い紫: JVM/GraalVM (32bit)  濃い紫: JVM/GraalVM (64bit)  橙色: Script

CPU1 (Core i7-8559U)での実行時間

CPU2 (Core i9-12900K)での実行時間

コメント・感想

全般的な実行時間の傾向

  • AOT (Ahead-Of-Time)コンパイル型の言語は、おおよそ同じオーダーの実行性能で速い
  • 次いでJVM上で実行される言語が続く。多少の差はあるが、これらの実行時間もだいたい同じ
  • GraalVM上での実行では、OpenJDKより若干よいケースもある
  • Script系の言語は、Node.jsが群を抜いて速い
  • PHPのJITも一定の効果はある(約1.5〜3倍)

32bit長と64bit長の整数型について

  • 概して64bit長の数値型を使った場合、32bit長の数値型を使った場合の1.5〜4倍程度、実行時間を要した
  • ただし、Rustでは、i32とi64を使った場合でほぼ同じ。最大メモリ使用量は期待に近いが、実行時間が32bitとほぼ同じなのはなぜ?(コードの誤り?)
  • Core i9-12900Kのほうが32bit長と64bit長整数の乖離が小さい傾向にある
  • メモリ使用量は、32bit長と64bit長整数で大きな差はない。JVM系に関しては64bit長のほうがやや使用量が多いように思える。これは実行環境の搭載メモリ量が多いで単に多くヒープを使用しているだけかもしれない

Template/Generics

  • C++, Rust, Goでは、32bit長と64bit長版をなるべく共通のコードとするためにTemplate/Genericsを用いた。同じ条件なら前回の結果と大きく変わらなかったので、Template/Genericsによる性能のオーバーヘッドもほぼないと考えられる
  • Java, Kotlinに関しては、そもそもGenericsで実現する方法が分からなかった。ベースクラスで基本的な計算をして、型に固有の処理をサブクラスでオーバーライドするような設計も考えたが、他の言語と実装が乖離しそうなのでそのアプローチも諦めた。単純に同じコードで数値型の異なるクラスを2つ用意した。
  • Scalaに関しては、Integral traitで実現しようとしたが、かなり(3〜4倍)実行時間が増加しそうだったので、Genericsの使用を諦めた