NVIDIA Dockerで簡単にGPU対応のTensorFlow入りコンテナを作る方法

2016年5月8日日曜日

こんにちは。
小説家になろうの作品群を読んでいたら連休が終わりそうです。
連休の終わりのアンミラ帰りに衝撃作TensorFlowでGPUが使えないとそのアンサーエントリであるTensorFlow on DockerでGPUを使えるようにする方法を読み、書いてみたエントリです。先月購入した最低限Maxwell的なグラフィックカード(図1)絡みで見つけたNVIDIA Docker、なかなか良いものなのでもっと多くの方に知ってもらいたいなと思い。
図1 NVIDIA GTX 750Ti搭載グラフィックカード
今回はNVIDIA Docker + TensorFlowでGPUを有効活用する手順を紹介します。他の方による関連記事として、日本語でのNVIDIA Docker + Caffe解説はすでにUbuntu14.04.3でnvidia-docker使ってCaffeをインストールしてみたがあります。Caffeな方はそちらを参照ください*1。今回はお題がTensorFlowという点と、NVIDIA公式配布のcuDNN入りイメージを利用する点が異なります。
[*1] このQiitaエントリが書かれたのは2016年1月で、NVIDIA Dockerがイマイチこなれていなかった頃です。当時と今では公式イメージのバリエーションやコマンド利用時の注意点などに若干差異がありますが原則は変わりません。

NVIDIA Dockerの概要

NVIDIA Dockerは、NVIDIAが開発しているDockerプラグインです。Dockerホスト側にNVIDIA GPUドライバを持たせ、Dockerコンテナ側にCUDA Toolkit(+cuDNN)を持たせるという明確な役割分担により、同一ホスト上の複数コンテナでバージョンの異なるCUDA Toolkitを柔軟に組み合わせられます。
さらに、NVIDIA Docker用に事前ビルドされたcuDNN入りDockerイメージを利用すると、NVIDIAへの開発者登録無しに(!)cuDNNライブラリを利用できます。

NVIDIA Docker利用のメリット

  • ホストとコンテナでCUDA Toolkitのバージョンを揃える必要がなくなる
    • 何よりもこれです。コンテナ間で異なるCUDA Toolkitバージョン(たとえば7.0と7.5)を利用したい場合に悩む必要がなくなります。
    • CUDA Toolkit 6.5系で開発して7.5系へ上げたくない、というコンテナが「古いバージョンにひきこもり続け」られるようになります。
  • 導入が比較的簡単
    • 通常のNVIDIA GPUドライバをインストールした通常のDocker環境にプラグインひとつインストールするだけなので、手軽です。
  • EULAにまつわる厄介事を回避できる(かもしれない)
    • ドライバや各種ツールキットのEULAというのは、なかなか厄介なものです。最近もDockerコンテナ上でのJavaの実行はライセンス違反なのか?あたりが話題になっていました。
    • 個人的にはDocker Hubから入手できるイメージの利用に関するライセンス同意に改善の余地があると思いますが、なにぶんNVIDIA Docker向け公式イメージはNVIDIA本家がメンテナンスしているものです。Docker Hub上のプロジェクトページにEULAの記載がないことは少々不安ですが、公式配布している以上は最低限の実行+ローカル派生イメージ作成あたりで文句をいわれることはないでしょう(多分*2)
[*2] その派生イメージをDocker Hubへ突っ込んで良いかどうかは別問題かと思いますので、ここでは議論しません。issueを立てて開発チームへ確認すべき案件でしょう。

NVIDIA Docker利用のデメリット

  • まだ利用者があまり多くないので枯れていない
    • 現状(2016/05/08)、1.0.0-rc状態*3で、そろそろ基本的なところでつまづくことはなくなってきたようには思いますが、利用者が何万人もいる状況ではありません。
    • 簡単なテスト動作をおこなううえでは問題ありませんが、ちょっと実用的で凝ったことをやろうと思うと謎のエラーと戦ったりGitHubのissuesとにらめっこすることになるかもしれません。
  • 通常のDockerとNVIDIA Dockerを混ぜて利用する手順の確立に工夫が必要
    • docker-machineやcompose、swarmを利用して大規模環境を構築する際にはラベルフィルタなどをうまく使って非GPUホストへGPU利用前提のコンテナをデプロイしないなどの制約掛けが必要となるはずです(未検証)。
    • もちろん、この部分の厄介さは非NVIDIA Docker環境(通常のDockerを使ってディレクトリ共有・デバイス共有でCUDA/cuDNNを利用するパターン)でも変わりません。
[*3] 個人的には、cuDNN 5のRCが取れて正式リリースされるタイミングでNVIDIA Dockerも1.0.0正式版をリリースするのだろうと踏んでいます。

導入手順の概略

NVIDIAの一定以上新しいドライバを正しくインストールできたUbuntu環境があれば、10分とかからずに動作確認できるでしょう。
ここでは途中段階でトラブルシュートしやすいように動作確認ステップを多く設けていますが、一旦手順を確立した後は飛ばしても構わないと思います。
  • 生のUbuntu 14.04環境(Dockerホスト)を用意する
  • Dockerホスト側へNVIDIA GPUの最新ドライバをインストールする
  • Dockerホスト側へDocker(docker-engine)とNVIDIA Dockerをインストールし、動作確認をする
  • cuDNN入りイメージをベースにTensorFlow込みのイメージ(カスタムイメージ)を作成する
  • カスタムイメージから生成したコンテナでGPUを利用できることを確認する
    • MNISTでTensorFlow+GPU(cuDNN)の動作確認
さすがに他用途のDockerとは混ぜないほうが良いですね。
実験用に作った環境はこんな感じでした。

環境

  • グラフィックカード: GeForce GTX 750Ti
  • NVIDIAドライバ: 361.42
  • OS: Ubuntu 14.04.4 LTS
  • Docker engine: 1.10.3-cs3
  • NVIDIA Dockerコンテナ
    • CUDA: 7.5
    • cuDNN: 4(4.0.7)
    • TensorFlow: 0.8.0(GPU版)
    • Python: 2.7.6

生のUbuntu 14.04環境(Dockerホスト)を用意する

Ubuntu 14.04のインストールです。
今回はUNetbootinで作成したUSBメモリのライブ環境からHDDへインストールしたUbuntu環境を利用しました。
標準グラフィックドライバの都合か、インストール画面の英文字列が歯抜けになって解読に苦労しましたが、普通にデスクトップ版をインストールしました。
OSのインストールが完了して軽く動作確認したらXのターンは終わりです。NVIDIAのドライバはXが走っているとインストールできないので、Xを落とすかGRUBの設定でテキストモードbootさせましょう。そして、デフォルトインストールされるNouveauドライバも邪魔なので無効化しておきます*4
/etc/default/grubは次のように編集します。
GRUB_CMDLINE_LINUX_DEFAULT="text nouveau.modeset=0"
今年こそはUbuntuデスクトップ元年になるでしょうか。このタイミングであわせてOpenSSH Serverをインストールし、以後の作業はSSH経由でおこないます。
[*4] Nouveauドライバの無効化についてはAutodeskのヘルプページが分かりやすいので、困ったら参照してください。

Dockerホスト側へNVIDIAの最新ドライバをインストールする

前段階できちんとNouveauドライバを無効化しておけば、
$ sudo ./NVIDIA-Linux-x86_64-361.42.run
としていくつかの質問に答えるだけですんなりドライバのインストールが完了するはずです。

Dockerホスト側へDocker(docker-engine)とNVIDIA Dockerをインストールし、動作確認をする

Docker(docker-engine)をインストールする

NVIDIA DockerはDocker(docker-engine)のプラグインとして構築されているので、まずDocker本体をインストールする必要があります。
1.9.0以上が必要なので、今回はhttps://docs.docker.com/docker-trusted-registry/cs-engine/install/#install-on-ubuntu-14-04-ltsの手順に従ってインストールしました。
docker infoの結果は次の通りです。
$ docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 1.10.3-cs3
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 0
 Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Plugins:
 Volume: local
 Network: bridge null host
Kernel Version: 4.2.0-27-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 7.734 GiB
Name: muo-desktop
ID: 7MD3:N656:PHZ6:LROY:ML3K:J4OF:4RGW:YRV4:HCCK:YQI4:ZMSZ:SI5I
WARNING: No swap limit support

NVIDIA Dockerをインストールする

公式の手順通り次のコマンドにてインストールします。
$ wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.0-rc/nvidia-docker_1.0.0.rc-1_amd64.deb
$ sudo dpkg -i /tmp/nvidia-docker_1.0.0.rc-1_amd64.deb && rm /tmp/nvidia-docker*.deb
Docker(docker-engine)を正しくインストールできていない場合にはエラーが発生するかもしれません。
正常にインストールできれば、次のようなターミナル出力で終了するはずです。
Preparing to unpack .../nvidia-docker_1.0.0.rc-1_amd64.deb ...
nvidia-docker stop/waiting
Unpacking nvidia-docker (1.0.0~rc-1) over (1.0.0~rc-1) ...
nvidia-docker (1.0.0~rc-1) を設定しています ...
Adding system user `nvidia-docker' (UID 117) ...
Adding new user `nvidia-docker' (UID 117) with group `nogroup' ...
Not creating home directory `/var/lib/nvidia-docker'.
nvidia-docker start/running, process 18051
Processing triggers for ureadahead (0.100.0-16) ...
muo@muo-desktop:~$
このあたりで一旦ホストOSを再起動しておきます。

NVIDIA Dockerの簡単な動作確認をおこなう

これまた公式の手順通り
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
としてみます。
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun May  8 07:00:24 2016
+------------------------------------------------------+
| NVIDIA-SMI 361.42     Driver Version: 361.42         |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 750 Ti  Off  | 0000:01:00.0     Off |                  N/A |
| 22%   33C    P8     1W /  38W |      7MiB /  2046MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
というようにGPUが認識されていれば、ひとまずOKそうです。
アーキテクチャ世代が古かったり、ドライバ競合などによって認識できていなければ、金の弾丸が必要な雰囲気です*5

さらにいくつかCUDA関連の動作確認をおこなう

deviceQueryの動作確認

NVIDIA Dockerリポジトリには、NVIDIA DockerがサポートするGPUデバイスを検出するサンプルがあります。
https://github.com/NVIDIA/nvidia-docker/blob/master/samples/ubuntu/deviceQuery/Dockerfile
このDockerfileをベースに先頭の行をnvidia/cudaへ書き換えて適当なディレクトリへ保存(私は~/docker/deviceQuery/Dockerfileというパスへ保存しました)し、
$ nvidia-docker build -t local:deviceQuery .
でビルドして
$ nvidia-docker run --rm local:deviceQuery
と実行すると、
./deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "GeForce GTX 750 Ti"
  CUDA Driver Version / Runtime Version          8.0 / 7.5
  CUDA Capability Major/Minor version number:    5.0
  Total amount of global memory:                 2047 MBytes (2145927168 bytes)
  ( 5) Multiprocessors, (128) CUDA Cores/MP:     640 CUDA Cores
  GPU Max Clock rate:                            1084 MHz (1.08 GHz)
  Memory Clock rate:                             2700 Mhz
  Memory Bus Width:                              128-bit
  L2 Cache Size:                                 2097152 bytes
  Maximum Texture Dimension Size (x,y,z)         1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096)
  Maximum Layered 1D Texture Size, (num) layers  1D=(16384), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers  2D=(16384, 16384), 2048 layers
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       49152 bytes
  Total number of registers available per block: 65536
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  2048
  Maximum number of threads per block:           1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             512 bytes
  Concurrent copy and kernel execution:          Yes with 1 copy engine(s)
  Run time limit on kernels:                     No
  Integrated GPU sharing Host Memory:            No
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Disabled
  Device supports Unified Addressing (UVA):      Yes
  Device PCI Domain ID / Bus ID / location ID:   0 / 1 / 0
  Compute Mode:
     < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 8.0, CUDA Runtime Version = 7.5, NumDevs = 1, Device0 = GeForce GTX 750 Ti
Result = PASS
のような結果が出力されるはずです。手元のグラフィックカードによって結果は大きく異なるはずですが、最後にResult = PASSと出ているとなんだか安心しますね。

その他のサンプル動作確認

実は、前述のdeviceQueryで作成したイメージにはCUDAのサンプルが一式含まれます。
nbodyなどのサブディレクトリでmakeをおこなえばそれぞれ実行できるので、何か詰まった時には試してみると良いかもしれません。
[*5] NVIDIA DockerリポジトリのwikiにCUDA Toolkitバージョンごとの最低ドライババージョンとGPUアーキテクチャが記載されているので、参考にしてください。

cuDNN入りイメージをベースにTensorFlow込みのイメージ(カスタムイメージ)を作成する

お待ちかねの本編というところですが、とても簡単です。
FROM nvidia/cuda:cudnn

RUN apt-get update \
 && apt-get install -y --no-install-recommends python-pip python-dev \
 && pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.8.0-cp27-none-linux_x86_64.whl \
 && rm -rf /var/lib/apt/lists/*
上記をDockerfile(今回は~/docker/tensorflow/Dockerfileにしました)へ保存して、Dockerfileのあるディレクトリにて
$ nvidia-docker build -t local:tf .
とコマンドを実行するだけです。
ビルド作業の途中でライブラリのビルドが走るため少々時間がかかりますが、成功すれば以下のようにイメージが生えたことを確認できるはずです。
$ nvidia-docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
local               tf                  7461583755dd        4 minutes ago       1.707 GB
local               nbody               c7559dcc54c7        21 minutes ago      1.467 GB
local               deviceQuery         3bab450c41f8        29 minutes ago      1.464 GB
nvidia/cuda         cudnn               baffdac82a9d        4 days ago          1.354 GB
nvidia/cuda         latest              981203204dd5        4 days ago          1.229 GB
あとは、TensorFlow公式の手順に従って簡単な動作確認をおこないましょう。
$ nvidia-docker run --rm -it local:tf /bin/bash
# python
...
>>> import tensorflow as tf
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcurand.so locally
>>> sess = tf.Session()
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:900] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties:
name: GeForce GTX 750 Ti
major: 5 minor: 0 memoryClockRate (GHz) 1.0845
pciBusID 0000:01:00.0
Total memory: 2.00GiB
Free memory: 1.96GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:755] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 750 Ti, pci bus id: 0000:01:00.0)
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print(sess.run(a + b))
42
>>>
動作していますね。

カスタムイメージから生成したコンテナでGPUを利用できることを確認する

MNISTでTensorFlow+GPU(cuDNN)の動作確認

ここでは、定番のMNISTを利用して正しくGPUを利用できていることを確認します。
ホスト側で次のコマンドを実行します。
$ nvidia-docker run --rm local:tf python -m tensorflow.models.image.mnist.convolutional
すると次のようにログが延々と出力されます。
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcurand.so locally
Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:900] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties:
name: GeForce GTX 750 Ti
major: 5 minor: 0 memoryClockRate (GHz) 1.0845
pciBusID 0000:01:00.0
Total memory: 2.00GiB
Free memory: 1.96GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:755] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 750 Ti, pci bus id: 0000:01:00.0)
Initialized!
Step 0 (epoch 0.00), 577.0 ms
Minibatch loss: 12.054, learning rate: 0.010000
Minibatch error: 90.6%
Validation error: 84.6%
Step 100 (epoch 0.12), 15.3 ms
Minibatch loss: 3.294, learning rate: 0.010000
Minibatch error: 4.7%
Validation error: 7.3%
Step 200 (epoch 0.23), 15.1 ms
Minibatch loss: 3.470, learning rate: 0.010000
Minibatch error: 10.9%
Validation error: 4.0%
...
以下略
実行中に他のターミナルからnvidia-smiを実行してGPUの利用状況を確認してみます。
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun May  8 08:07:38 2016
+------------------------------------------------------+
| NVIDIA-SMI 361.42     Driver Version: 361.42         |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 750 Ti  Off  | 0000:01:00.0     Off |                  N/A |
| 36%   57C    P0    30W /  38W |   1926MiB /  2046MiB |     80%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
GPU温度が上がっていますね。ホクホクですね。57度といえばもう少しで大半のタンパク質が変質する温度ですから、長時間稼働すれば半熟玉子ぐらいは作れそうです。
電力をジャブジャブと突っ込んでいるとさすがに個人用PCとしては気になるところですが、計算が終わって1-2分後に再度GPU状況を確認すると
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun May  8 08:11:27 2016
+------------------------------------------------------+
| NVIDIA-SMI 361.42     Driver Version: 361.42         |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 750 Ti  Off  | 0000:01:00.0     Off |                  N/A |
| 27%   35C    P8     1W /  38W |      7MiB /  2046MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
となり、きっちり消費電力も下がっています。安心です。

その他

Dockerイメージとそのバリエーションについて

nvidia-dockerコマンドでは特殊なDockerレジストリからイメージを拾ってくるかとおもいきや、普通にDocker Hub上のnvidia/cudaからイメージを拾ってくるという素直な作りです。
タグ一覧をざっと見てみると、結構柔軟にさまざまな版を用意してくれていることがわかります(どれだけ動作確認されているかは別問題です)。Ubuntu嫌いの方はCentOSベースのものを利用できます。
latestタグが打たれているデフォルトイメージはUbuntuベースで、pullした結果のサイズは次の通り1.2GB程度です。
muo@muo-desktop:~$ nvidia-docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nvidia/cuda         latest              981203204dd5        4 days ago          1.229 GB
扱いやすいですね。
Docker Hub上に標準的な置き方をされているため、派生イメージを作りやすい(実際、今回の手順の途中でTensorFlow入りのものを作ったように)のでとても便利です。

トラブルシューティング: MNISTの動作が途中で終了してしまう場合

$ nvidia-docker run --rm local:tf python -m tensorflow.models.image.mnist.convolutional
での実行が
...
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
Initialized!
のように元気よくInitialized!と叫んであっさり終わってしまう場合、以下のログエントリが出力されていないか確認してみてください。
I tensorflow/stream_executor/dso_loader.cc:99] Couldn't open CUDA library libcudnn.so. LD_LIBRARY_PATH: /usr/local/nvidia/lib:/usr/local/nvidia/lib64:
I tensorflow/stream_executor/cuda/cuda_dnn.cc:1562] Unable to load cuDNN DSO
この場合、cuDNNを含まないDockerイメージを基盤として派生したイメージからコンテナを生成している可能性があります。
イメージ作成用Dockerfileの先頭がFROM nvidia/cudaFROM nvidia/cuda:latest(あるいはFROM nvidia/cuda:devel)になっている場合、そのイメージにはcuDNNが含まれません。
残念ながら、Dockerfileを修正してTensorFlowを含むDockerイメージをビルドし直する必要があります。この部分を正しくFROM nvidia/cuda:cudnn(またはFROM nvidia/cuda:cudnn-ubuntu14.04など)に変更して再度buildを実施してください。

公式Dockerイメージに含まれるcuDNNのバージョン

現状、公式のcudnnイメージに含まれるcuDNNのバージョンは4系(4.0.7)を正式として3系も別タグで提供されています。現在(2016/05/08時点)、cuDNN 5のRC版がリリースされていますが、このイメージは提供されていません。この点について、NVIDIAのチームメンバーがcuDNN 5のRCが取れて正式リリースされたらイメージも更新すると表明しています。

GPUステータス情報へのWebからのアクセス

NVIDIA Dockerでは、HTTP経由でdiag情報へアクセスできます。
$ curl http://localhost:3476/gpu/info

Driver version:          361.42
Supported CUDA version:  8.0

Device #0
  Model:         GeForce GTX 750 Ti
  UUID:          GPU-9e723e86-d279-8a3d-25a4-010ecfa20551
  Path:          /dev/nvidia0
  Family:        Maxwell
  Arch:          5.0
  Cores:         640
  Power:         38 W
  CPU Affinity:  NUMA node0
  PCI
    Bus ID:     0000:01:00.0
    BAR1:       256 MiB
    Bandwidth:  15760 MB/s
  Memory
    ECC:        false
    Global:     2046 MiB
    Constant:   64 KiB
    Shared:     64 KiB
    L2 Cache:   2048 KiB
    Bandwidth:  86400 MB/s
  Clocks
    Cores:        1293 MHz
    Memory:       2700 MHz
  P2P Available:  None

$ curl http://localhost:3476/gpu/status

Device #0
  Power:        1 / 38 W
  Temperature:  33 °C
  Utilization
    GPU:      0 %
    Memory:   1 %
    Encoder:  0 %
    Decoder:  0 %
  Memory
    Global:  7 / 2046 MiB
    ECC Errors
      L1 Cache:  N/A
      L2 Cache:  N/A
      Global:    N/A
  PCI
    BAR1:  1 / 256 MiB
    Throughput
      RX:  0 MB/s
      TX:  0 MB/s
  Clocks
    Cores:    135 MHz
    Memory:   405 MHz
  Processes:  None
GPUを利用するプロセスを走らせた状態で/gpu/statusへアクセスすると
...
  Clocks
    Cores:   1019 MHz
    Memory:  2700 MHz
  Processes
    - PID:     16759
      Name:    python
      Memory:  1841 MiB
と、プロセス情報も出してくれます。至れり尽くせり感がありますね。
これだけあれば情報を定点観測+加工して動作状況として集計、マシン調達や稼働の計画を作るのも楽になるのではないでしょうか。

docker-compose + docker-machine ?

おそらく、単純な実験を超えた実用の際にはdocker-machine+docker-compose(そしてswarm)を利用するでしょう。その場合はhttps://github.com/NVIDIA/nvidia-docker/issues/8を参照することになると思いますが、手元では今回試していません。

まとめ

NVIDIA Dockerを使ってcuDNN+TensorFlow環境を作る手順は割とシンプルです。先人がハマりにハマって踏み固められた道という感じで、今回の手順中で特段ハマる箇所はありませんでした。
個人的にはWindows上のdocker-machine+Hyper-V環境のコンテナから楽にGPU利用計算を扱えるととても嬉しいのですが、CUDA Toolkit対NVIDIA GPUドライバの通信仕様がLinux版とWindows版で同じということはさすがにない(=うまくいかない)と思うので試していません。

「Unreal Engine 4で極めるゲーム開発」のtypoメモ(その1)

2016年5月5日木曜日


去年買ってざっと目を通し、「これは良書だ!!」と確信しつつもきちんとハンズオンで読み進めてはいなかった"極める本"です。
そろそろ購入から1年経ってしまうなーというタイミングでいわゆるゴールデンウィークに当たったので、せっせと読み進めてみました。
確信の通り、本当に良書です。3Dゲーム開発で必要なポイントを分かりやすく説明しつつ、UE4の便利機能もマメに紹介していく構成です。手を動かしながら読み終わった後には「あー、あれどうやるんだっけ」と迷った時に振り返って読める逆引き本にもなります。
ボリュームが約600ページと半端ないので、校正しきれなかったらしい箇所がぽつぽつと見受けられました。ワンチャン次刷あたりで直ると良いなーとの思いを込めて、見つけた誤記のメモを公開します。ひょっとすると第2刷でサイレント更新されている箇所があるかもしれません。
内容は初版第1刷(2015年7月25日と奥付に記載)ベースです。本書のサポートページは2016年4月15日改訂状態まで一通り確認しました。
p.374までメモを書いて一旦心が折れたのでここまで。残200ページは改めてメモを書きます。

第1部 はじめに

第1章 p.1

  • 昨日トピックごとに → 機能トピックごとに

第2部 プロトタイピング

第6章 p.63

  • そのルール基づいて → そのルールに基づいて

第7章 p.86

  • Prop\Mesh\ → Props\Meshes\ (※2箇所)

第9章 p.148 コラム

  • Poc → PoC

第10章 p.153

  • プロジェクトの設定 → プロジェクト設定

第10章 p.161

  • アセットツリーで[Game > Blueprints] → アセットツリーで[コンテンツ > Blueprints] (※日本語UIなので)

第10章 p.163

  • アセットツリーで[Game > Blueprints] → アセットツリーで[コンテンツ > Blueprints] (※日本語UIなので)
  • Bluneprint → Blueprint

第10章 p.165

  • コンポーネントを追加 → コンポーネントの追加

第11章 p.172

  • 画面に文字を出力された→画面に文字が出力された

第11章 p.179

  • 図11.25の(2)でPMShurikenActorと書かれているが、正しくはPNShurikenActor(本文に従う)

第11章 p.180

  • 下から7行目 Transormation → Transformation

第11章 p.185

  • 下から6行目 ProijectileMovement → ProjectileMovement

第12章 p.195

  • キーボードの[W][A]で → キーボードの[W][S]で

第12章 p.201

  • 図12.29 キャプション Add Contorller Pitch Input → Add Controller Pitch Input

第12章 p.204

  • 左サムティック → 左サムスティック

第13章 p.211

  • T_Ninja01_D → T_Ninja_D

第14章 p.222

  • ボーンに適用 → ボーンに適応
  • コリジョン形状 → コリジョンジオメトリ

第14章 p.231

  • 追加したボックスをの → 追加したボックスの

第14章 p.233

  • ツールバーで[選択されたシミュレーション]を → ツールバーで[選択したシミュレーション]を

第14章 p.236

  • コラム中の図14.38 赤枠をつける対象はPhysics AssetではなくEnable Per Poly Collisionのはず

第16章 p.255

  • タイミングの違いよって → タイミングの違いによって

第16章 p.262

  • 役不足 → 力不足

第16章 p.263

  • Box Exntent → Box Extent

第17章 p.274

  • 標準で用意た → 標準で用意した

第17章 p.278

  • ドアが空いたり → ドアが開いたり

第17章 p.279

  • まずは、新規テストマップを作るところから開始します。 → (※ここでは新規テストマップを作らないので記述自体不要そう)

第18章 p.291

  • コラム中ほどの文頭: ます。トランスフォームは → トランスフォームは (※前段落の末尾からの流れで混入したぽい)
  • コラム中ほどの文中: ~の3つ情報を → ~の3つの情報を

第18章 p.298

  • [コンテンツ>Bluprints] → [コンテンツ>Blueprints]

第18章 p.301

  • コラム Route → Reroute (※本文に3箇所、絵解きに1箇所)

第18章 p.302

  • [FloorSwitch02] → [Fusuma02 Switch] (※サポートページに手順不足の記載があるが、それとは別)

第19章 p.309

  • [InputAction Sprit] → [InputAction Sprint]

第19章 p.312

  • 新しい変数を追加します。 → 新しい関数を追加します。

第19章 p.314

  • 図19.25中 PawnをPownとtypo

第19章 p.319

  • [Utilities]>[Timer] → [Utilities]>[Time]

第19章 p.323

  • サブルーチンと関数は → カスタムイベントと関数は

第20章 p.337

  • Fricition → Friction

第20章 p.341

  • Note見出し [Set Simulated Physics]ノード → [Set Simulate Physics]ノード

第20章 p.342

  • 動作を切り替えてることで、 → 動作を切り替えることで、

第20章 p.346

  • Add ImpulseのNote本文 座標や骨の指定して → 座標や骨を指定して

第20章 p.348

  • [Angle]入力ピン → [Angle Deg]入力ピン

第21章 p.357

  • Vechile → Vehicle

第21章 p.363

  • [Collision Enable] → [Collision Enabled]

第21章 p.366

  • [SK_Ninja_Skeltone] → [SK_Ninja_Skeleton]
  • [Pikcup] → [Pickup]

第21章 p.367

  • Charactes\Mesh → Characters\Meshes

第21章 p.368

  • Bluepritns → Blueprints

第21章 p.369

  • [ブロック] → [Block]

第21章 p.370

  • [InvinsibleWall] → [InvisibleWall] (※2箇所)
  • 図21.23 絵解き [InvisinbleWall] → [InvisibleWall]

第21章 p.374

  • サポートページの訂正記述ミス: これに伴い、図20.5も訂正いたします → これに伴い、図21.29も訂正いたします

FPGAマガジンNo.13の誤記/誤植一覧

2016年4月27日水曜日

特集 プロローグ

  • p.5
    • Arduinoシールド試す → Arduinoシールドで試す (※章タイトルはこうなっているため)

特集 第1章 MAX 10+BLEモジュールでスマホ制御ラジコンを作る!

  • p.7
    • 表1 MAX 10 10MH08SAU → MAX 10 10M08SAU*1
  • p.8
    • Nexsus7 → Nexus7 (※またはNexus 7)
  • p.11
    • GAT_xxxx.bin → GATT_xxxx.bin
  • p.15
    • パーソナリティ・データ(GAT_xxxx.dat) → パーソナリティ・データ(GATT_xxxx.bin)
[*1] あまり関係ありませんが、マクニカのパーツリスト内でBCM20736SとBCM20737Sという型番が使い分けられている理由はhttp://ja.broadcom.com/collateral/hs/2073X-HS100-R.pdfを読んで納得しました。

特集 Appendix 1 20ピン&40ピンDIPサイズMAX 10ボード2品種紹介

なし!

特集 第2章 MAX 10+カメラ・モジュールで動き成分をディスプレイ表示

  • p.31
    • 図8 PIO は1 ビットでBidirect → PIO は1 ビットでBidir (※もちろん意味としては何の問題もないけれど、本文中・UI上・表5でもBidirと統一されているので)

特集 第3章 トランジスタ技術増刊号付属MAX 10基板でHDMI&オーディオ出力!

  • p.46
    • Pin Prannner → Pin Planner
  • p.51
    • Genarate → Generate

特集 Appendix 2 DE0拡張ボードを使ってMAX 10をEthernetに接続する!

  • p.58
    • 表B PIO(SWITH) → PIO(SWITCH)
  • p.59
    • Intarval Timer → Interval Timer
  • p.60
    • Pallael I/O → Parallel I/O
    • 表D Data Witdh → Data Width
    • 表D Initalization reflesh cycles → Initialization refresh cycles
    • 表D Delay after powerup,before initalization → Delay after powerup,before initialization
    • 表D ACTUVE to READ or WRITE delay(t_rcd) → ACTIVE to READ or WRITE delay(t_rcd)
  • p.61
    • 10/100Mb Small Mac → 10/100Mb Small MAC
    • 45(100/45=2.5MHz) → 40(100/40=2.5MHz) (※2.5MHzにしたいなら40へ改め、45のままでいくなら2.222MHzへと改める必要あるのでは。図J内では20になっているので判断つかず)

特集 第4章 純正MAX 10評価キット+Arduinoシールドで試す初めてのMAX 10

なし!

特集 第5章 MAX 10の特徴と使用する開発ツール

  • p.82
    • Enprion → Enpirion
  • p.83
    • Quaruts → Quartus
  • p.84
    • Quarus → Quartus

Vivado HLxの各エディションとHLS(High Level Synthesis)の特徴

  • p.90
    • Airtx-7 → Artix-7
    • Vivado シュミレータ → Vivado シミュレータ
  • p.91
    • IP Integator → IP Integrator

Cyclone V SoCとZynqのAXIバスの共通点と相違点,その性能を比較する

  • p.100
    • シテム → システム
    • キャラクタは2体 → キャラクタ2体
  • p.101
    • r_backets → r_buckets (※Verilog HDLのコードでは正しくこう書かれているため)
  • p.102
    • r_backets → r_buckets (2箇所) (※Verilog HDLのコードでは正しくこう書かれているため)
    • リスト3 サイクス数カウンタ → サイクル数カウンタ

Cyclone V SoC&Zynq共通システムで割り込み制御を試す!

  • p.108
    • 表3 Ethernet1 Waku-Up → Ethernet1 Wake-Up
  • p.110
    • リスト2 fd = open(/dev/uio0); → fd = open("/dev/uio0");
  • p.112
    • gpio-alitera → gpio-altera
    • 自体に陥ります → 事態に陥ります
    • taskler → Tasklet

入門評価ボードZYBOでHDMI(DVI)画像入出力を学ぶ

  • p.115
    • VESA(Video Electoronics Standard Association) → VESA(Video Electronics Standards Association)
  • p.118
    • VAG端子 → VGA端子
  • p.121
    • bitsip → bitslip
  • p.122
    • hmdi_in_sp → hdmi_in_sp
  • p.123
    • 表3 Degilent社 → Digilent社

USBコミュニケーション・デバイス・クラス対応のUSBターゲット機器の製作

  • p.131
    • usbCdcTarge → usbCdcTarget

FPGA で高速シリアル通信 ~ SERDES を使ってみる~

  • p.136
    • SERDES:Serializer/Desilializer → SERDES:Serializer/Deserializer
  • p.137
    • 表3 Aitix-7 → Artix-7
  • p.138
    • パターンでで定め → パターンで定め
  • p.139
    • 図6 (b)Line Rate,Refclk Selection タブの設定 → (b)Line Rate,RefClk Selection タブの設定
    • 図6 (c)Encodeng and Clocking タブの設定 → (c)Encoding and Clocking タブの設定
  • p.141
    • 見出し Line Rate,Refclk Selection タブの設定 → Line Rate,RefClk Selection タブの設定
    • Line Rate,refclk Selectionタブ → Line Rate,RefClk Selectionタブ
    • 見出し Encodeng and Clocking タブの設定 → Encoding and Clocking タブの設定
    • Encodeng and Clocking タブ → Encoding and Clocking タブ

気持ち

いつもながら尖った記事が満載で読み応えがありました。
読みながら見つけたtypo/誤植系は合計49件でした。記事を読みながら手を動かす際に気付かないと詰む系のものはほとんどありませんでした。割り込み制御章のサンプルコードぐらいだと思います。トラ技増刊MAX 10との並行作業という離れ業を考えると、「やり遂げたよ、最後まで。」という感じが圧倒的に強いですね。
Quartusのタイプミスは過去号からの伝統感があり、そういう意味では覚えやすくてタイプもしやすいVivado最高だな!という気持ちが芽生えました(冗談です)。
内容としては、見事に積みデバイスと化しているBeMicro MAX 10を活かそうな、という気持ちが大きくなると共に次号でのVivado HLS記事が楽しみになった号でした。あと、Cyclone V SoCとZynqでの性能検証の章がとてもかっこよくて参考になりました(「メモリのクロック差じゃないっすかねー?」で終わらせない分析力、身に付けたい)。
p.105のコラムに書かれていた次号予告のPmod接続HDMI(DVI)ネタ、今号のZYBO+HDMI(DVI)ネタとぴったりかぶってしまっているのでは...?という気持ちと共に締めます。

MS主催の機械学習コンテスト開催中らしい (ML blogメモ)

2016年4月25日月曜日

Cortana Intelligence and Machine Learning Blog(build 2016での機械学習系サービス発表を受けて名前が変わりました)で興味深いエントリを見かけたので簡単にメモしておきます。
元記事: Game On! Introducing Cortana Intelligence Competitions (by Charis Loveland氏)
Cortana Intelligence Competitionsというイベントをやるよ、という告知記事です。ざっくりと、Kaggleのようなイメージでしょうか。

コンペティションの概要メモ

  • 機械学習コンテストをやっている
  • 初回のミッションは脳信号のデコード
    • 脳がどのようにして電気信号を翻訳しているのかを解き明かすという、実世界の問題に関連するコンテスト
    • 具体的には、脳表面の電気信号情報を入力として被験者へ提示された画像が「家」であるか「顔」であるかを正確に推測するのがミッション
    • 実験条件とデータの性質
      • 家もしくは顔の画像は400ms表示の後に400msブランク画像を表示、というサイクルで被験者へ提示
      • ECoG(皮質脳波記録)信号のサンプリングレートは1,000Hz
    • 都合、4名の被験者での各300件の画像提示結果中の各200件ずつを利用して学習をおこなう
  • 実世界の脳科学に貢献するチャンス
  • 従来は非公開だったデータセットを利用できるので見逃せないよ(と書いてある)
  • 良い成績を出したら賞品と名誉を得られるよ(と書いてある)
    • ガチ勢に勝てるか挑戦カモン(意訳)
  • 大丈夫、ボクも2ヶ月前にMSへ入社するまでは機械学習のバックグラウンドなかったし、学習リソースが充実してるからキャッチアップしやすいよ!(執筆者談)
  • コンテスト参加にはMicrosoftアカウントかOffice 365アカウントが必要でAzure MLを利用するよ
    • でもクレジットカード登録なしで使えるAzure ML枠でokだから安心してね
前述のようにコンテストはAzure MLを利用して進められます。データ一式とコンテスト参戦に必要な仕込みのおこなわれたスターターパックを自分のAzure MLワークスペースへコピー、あとは好きにやって!という感じのようです。

最初のコンテストの概要

  • お題: 脳内信号のデコード(Brain Signal Decoding)
  • 開催期間: 2016/03/30 - 2016/06/30
  • 賞金: 総額$5,000(内訳は$3,000、$1,500、$500とのこと)

日本語FAQがあった

書いてから気付いたのですが、https://azure.microsoft.com/ja-jp/documentation/articles/machine-learning-competition-faq/で日本語のFAQ(今回の設問に関するものではなく、Cortana Intelligenceコンペティション全体に関するもの)が公開されています。Azure MLのワークスペースなどに関する周辺情報はこのページを読んでおくのが楽そうです。

FPGAマガジンNo.12の誤記/誤植一覧

2016年3月31日木曜日

FPGAマガジンは毎号技術的にエッジーな記事が多く勉強になる、本当にありがたい存在です。しかし誤植が割とコンスタントに多いです。技術専門誌としての本分は「他で得難い情報」にあるので、限られた執筆・編集時間をそちらへ割くのは当然なのですが、利用者の裾野を広げる意味ではちょっと悲しいです。
No.12もけっこう誤植多いな…と思いつつ読んでいたのですが、毎回「誤植多いぽよ~~」と言っているだけでも改善しない気がします。著者の方々に誤植一覧が届けば次以降の原稿で気をつけるヒントになるかもしれない、という期待も持ちつつ精読して洗い出しました。66箇所ありました。
多くは細かな表記上のミスや写植上のミスです。脳内で補正しながら読めば良いので大した問題ではなく、放っておいても良いようにも感じられます。自分の慣れているエリアの文章は脳内での読み替えが効きます。しかし当然スピードが落ちます。バイナリトランスレーション大変でしょ。とりわけ私の場合、記事の序盤で複数のミスを見つけると不安になってじっくり読むフェーズに入り、1/5ぐらいのスピードでしか読めなくなってしまいます。
また私は、初心者向けの技術記事では写経可能性もかなり大事だと思っています。ここでは特に間違いを減らしたいものです。写経で経典が間違ってるとかそもそもやばいし、当該環境に慣れていないと自力で誤りを見つけて乗り越えるのが難しいから。近年は雑誌でキーワードを拾い、その後にネットで調べて深めるフローも多用されます。キーワードが間違っていたら先を調べていくのも一苦労します(Googleのキーワード補正に期待することもできますが、著者側がこれをいうと開き直りにしかなりません)。
そういうわけで、この一覧が誰かの学習の役に立てば幸いです。

特集 第1章 Cyclone V SoCとZynqの比較とFPGA開発フロー

p.10
  • 右段なかほど All Programable → All Programmable
  • 右段下 PowePC → PowerPC
p.12
  • 表3 Contoller → Controller
p.15
  • 左段下見出し Quatrus → Quartus
  • 右段下箇条書き Impliment → Implement
p.17
  • 表6 Programing → Programming
  • 表6 PomGen → PromGen
計7箇所

特集 第2章 VGA表示回路を例にしたMy回路のIPコア開発とライブラリ化

p.20
  • 図3右 Cyclone V Soc → Cyclone V SoC
p.22
  • リスト2冒頭 `define XILIXN → `define XILINX
p.24
  • 右段上 Memoris & → Memories &
計3箇所

特集 第3章 ビルディング・ブロック開発によるMy回路IPコアのFPGAへの実装

p.35
  • 図5 QSys → Qsys
  • 図5 モージュル → モジュール
p.43
  • 左段上 Ummapped → Unmapped (2箇所)
計4箇所

特集 第4章 共通カスタムLinuxディストリビューションの開発

p.46
  • 左段上 Suppor → Support
p.47
  • 図1右上 Yocoto → Yocto
p.49
  • 左段 Ubutnu → Ubuntu (2箇所)
  • 右段下見出し bblayes.conf → bblayers.conf
p.53
  • 左段中 SPLのコンパイルgmakeを → SPLのコンパイルはgmakeを
  • 左段中 ubuntu → Ubuntu
p.54
  • 左段中 Cyclne → Cyclone
  • DTB(Device Tree Bomb) → DTB(Device Tree Binary)*
  • DTS(Device Tree Script) → DTS(Device Tree Source)
DTBが何の略かについては、Linuxのドキュメント(https://www.kernel.org/doc/Documentation/devicetree/usage-model.txt)とDevice Treeのドキュメント(http://www.devicetree.org/Device_Tree_Compiler)で確認しました。
計10箇所

特集 第5章 ついに起動!デュアル・ブートSDカードの作り方

p.61
  • 表4 DTB(Device Tree Bomb) → DTB(Device Tree Binary) (2箇所)
計2箇所

特集 第6章 Linuxデバドラ&アプリケーションの作成と性能評価

p.66
  • 左段上(5行目) ./memwrite 40010000 1 → ./memdump 40010000 1
計1箇所

特集関連(1) Atlas-SoCでアルテラSoC環境を手軽に楽しもう

p.74
  • 左段 SysFs → Sysfs
    • p.80のリスト3キャプション部分表記に揃え。この場合、p.81の4箇所も変更
    • あるいは逆でp.80のリスト3キャプションをSysFsへ揃え
      • 一般的表記はSysfsあるいはsysfsだと思うけれど、一貫性を重視する意味ではp.80のリスト3側を変えるほうが手っ取り早い
  • 右段中 Cyclon → Cyclone
p.76
  • 右段見出し Cyclon-SoC → Cyclone-SoC
p.77
  • Liteweight → Lightweight (11箇所、うちリスト1のコメント中に2箇所)
  • 右段中 LiteWeight → Lightweight
  • リスト1キャプション Liteweigth → Lightweight
p.80
  • 右段中 Liteweight → Lightweight
p.81
  • Liteweight → Lightweight(4箇所、うち本文に2箇所、図5中に2箇所)
p.84
  • リスト7コメント Liteweight → Lightweight
計22箇所

特集関連(2) 使いやすいXillinux&XillybusをTerasic社SoCKit上に導入する

p.85
  • Ubuntsu → Ubuntu(2箇所)
  • 図1絵解き Xilliux → Xillinux
  • 図2 Row Binary File → Raw Binary File
  • 図2 Xilinux → Xillinux
p.86
  • 右段中 Xilliux → Xillinux
p.91
  • 写真4 キャプション Xilliux → Xillinux
計7箇所

USBコミュニケーション・デバイス・クラス対応のUSBターゲット機器の実装

p.94
  • 表1 最上段 「転送速度 フルスピード」がヘッダとして網掛けになっているのはおかしい
    • 仕様表記なので最上段はヘッダ無しでも成立する。網掛けのみが不要
    • あるいは「名称 仕様」などが無難
p.98
  • 右段上 usbCdcDeice → usbCdcDevice
  • 図9 EPOCdc → EP0Cdc
p.99
  • 右段下見出し ディスクリプタの要求動作例 → デスクリプタ~
    • 章内でずっと"デスクリプタ"表記なので、表記ゆれ
p.105
  • 左段中 監視するたけでも → 監視するだけでも
計5箇所

プログラミング言語PythonではじめるFPGA開発入門

p.108
  • 表1 Flase → False
計1箇所

SDKを使ったMicroBlazeのソフトウェア開発手順詳細

p.119
  • 図2(b) Local Project → Local to Project
  • 図3(b) Local Project → Local to Project
p.120
  • 図5(a) "File → New-Application Project" → "File → New → Application Project"
    • 本文での表記にあわせる
p.125
  • 図10(a) 右部分の丸囲みが1段上についている
    • PDF版のみかもしれない
計4箇所

Cyclone V SoCで試す無償版純正PCI Expressコアの使いこなし

なし!

微妙なもの

  • P.15 iSimとISimあたりの表記が揺れている
    • そもそもVivadoではISim使わない? Vivado Simulatorへ変わったような気もする
  • p.53 右段の日本語ぐちゃぐちゃ
  • p.74 右段 Helio → Sodiaかなぁ
    • 本号の執筆段階でHelioを終売してSodiaへ向かうという流れはそれなりに明確だったので
  • p.85 図2 XillyBus IP → Xillybus IP
    • これはどちらの表記でも良いのかもしれない。判断つかず
  • p.101 リスト2(usbCdcROMの定義部分) usbCcdROM.v → usbCdcROM.vではないか。同usbCcdDevice_define.v → usbCdcDevice_define.vではないか
    • この記事は解説とコード抜粋紹介のみで実コードのダウンロード先を記載していないので正しいところは分からない
  • p.127 EPROMの接続例です → EEPROMの接続例です
    • EEPROMもEPROMのうちなので"EPROM表記でも"間違ってはいなさそう。しかし図14キャプションの表記を見ると、ここもEEPROMと記載するのが適切そう
  • p.129 Basysボードに → Basys3ボードに
    • ここまで一貫してBasys3と表記しているので、ここも揃えるべき。しかしBasysシリーズには違いないので、まあ良いのかも
なお、今回のまとめにおいて
OpenEmbedded Projectとは,組み込み機器用の Linux ディストリビューションを作るためのソフトウェア・フレームワークを提供しています(図 3). - p.50
こういう、日本語として(???)な記述はすべて無視しました。技術用語が間違っていたり、明らかに意味が通らないものだけを書きました。

DockerコンテナがフリーズするLinuxカーネル+AUFS起因の問題

2016年3月25日金曜日

.NET Core環境用の操作ツールであるdotnetコマンドをDocker環境上で動作させようと試みたものの、.NET Coreのdotnetコマンドを試そうとしたら試せなかったの通りハングアップしました。一旦issueを立てて置いていたのですが、その後の話を書きます*1
[*1] 少々タイトルの範囲が大きいですね。Docker環境での.NET Core公式イメージ実行時に発生した問題ですが、もっと一般的な問題だとわかったのでこうしました。

現象の概要(おさらい)

  • microsoft/dotnet:latestを実行すると、mkdircdは通るがdotnet newという.NET Core用のプログラムを実行した時点でコンテナがフリーズする
  • フリーズしたコンテナはdocker kill ...で強制終了できない
  • topコマンドでホストマシン状態を見ると、DockerデーモンがひたすらCPUを食いつぶしている
  • 結局Dockerデーモンを再起動するか、ホストマシンを丸ごと再起動するしかなくなる
この現象はMac OS X上に作ったBoot2Docker環境(VMWare Fusionベース)でも、手持ちのDebianサーバ上に作ったDocker環境でも同じように発生しました。
これを受けて前回は、`latest` image freezes immediately after running `dotnet new` #26というissueを立てましたよという話で終えていました。

その後

issueを立てた時点で一番疑っていたのはDocker自体です。ホストとのコミュニケーションを多用するメモリ管理かI/Oのどこかでミスっているのだろうという読みでした。
その後の手元テストにより、この問題はBoot2Docker(b2d)を新しいバージョン(1.10系)へ上げると発生しなくなることがわかっていました。このためひとまずb2d環境でdotnetコマンド群のテストをおこなっておけば良いか、と考えていました。
しかしissueに対してDocker 1.9.1 hanging at build step "Setting up ca-certificates-java" #18180関連の問題では?というコメントをもらいました。

本当の原因(Dockerコミュニティの阿鼻叫喚)

このissueは私が見た時点でクローズ済みでしたが、なかなか壮絶で壮大な戦闘録が残っていました。
結論をまとめると、これはLinuxカーネル内のmm/filemap.cに対する改善(sendfileシステムコールを利用したデータの書き込み処理で、停止シグナルを受けて途中終了できるようにしたもの)と、Dockerが利用するレイヤードファイルシステムのひとつであるAUFSのデータ書き込み部分の一部に従来のカーネル実装へ依存するコードが存在したことで発生した問題でした。2016年2月末時点で主要ディストリビューションでの対応は終わっており、OSアップデートにより解消します。
この問題の解決までを時系列でざっと見てみます。
  • 最初の問題報告が11/24
  • Linuxカーネル4.1.13での変更が関わっているらしいと判明したのが12/4
  • --virtualbox-cpu-countなどによりDockerデーモン稼働VMへ割り当てるCPU数を増やすワークアラウンドが発見されたのが12/5
    • その後、これはあくまでも発生頻度を下げるだけで根本対応とならないこともコメントにて指摘されています
    • この少し前から、Boot2Dockerで利用しているAUFSのバージョン依存の問題では、という推測(ほぼ確信)がいくつか出てきました
      • 「だっから!AUFSなんて!使うのやめろ!!」勢もぽちぽちと出ていました
  • 時間経過と共に影響を受ける人が増えた分か、しばらく再現報告や+1が続き(そう、当時GitHubにはリアクション機能がなかったのです)、12/8にはDockerチームメンバーから「新情報無いのにコメントするのやめーや」というお達しがありつつもしばし事態は硬直
  • 事態は12/21にAUFS内で動作停止に至る具体的な箇所が特定されたのをきっかけに再び動き始め
  • ワークアラウンド版の実装、カーネルバージョンを下げる緊急回避策の確立、AUFSの新版を取り込んだカーネルビルド策での対応を経て
  • b2dを含む各ディストリで徐々に新版AUFSの取り込み作業がおこなわれ
  • Debianが2/29に3.16.7-ckt20-1+deb8u4を出したことで主要ディストリでの問題対応が収束
3ヶ月に及ぶ、長く苦しい戦いだったようです。
まず、https://github.com/docker/docker/issues/18180#issuecomment-161843456にてAUFSとmm/filemap.cへの変更によるものだと突き止めているのすごいなと思いました(小並感)。
そしてこの問題へ取り組み、さっそうとトラブルシュートしつつ更に広い視点からの解決策を模索してAUFSへ潜って本家とやりとりをし、更に主要ディストリ(UbuntuDebian)へfix取り込みの働きかけをおこない、それらの状況をトラッキングしissueへ追記していくAkihiroSuda氏まじぱねぇという感想でした。手練というか、なんかNINJA感がありました。
そういうわけで、私が試した環境2つがたまたま該当してDocker本体の問題と思い込んだのが敗因(?)でしたね。

その後、dotnetコマンドは

普通にうごきました。
立てたissueも先ほどcloseされました。

日経ソフトウエア 2016年5月号に寄稿しました。そして記事の補足

2016年3月24日木曜日

日経BP刊の日経ソフトウエア誌 2016年5月号(3/24発売。今日です!→Amazonで買えます)に「Windowsアプリはなぜ動く ~そのとき、PC上では何が起こっているのか?~」という特集記事を寄稿しました。

  • Windows上でC系の言語から素直に書きだした.exeファイルとC#などの.NET系言語から書きだした.exeファイルの中身の差
  • これらを実行した際のOS側での処理差やランタイム差
  • Universalなアプリ(8.1 Universal, UWP)の登場背景と基本構造
という話を広く書きました*1
[*1] ところどころコラムでMonoへの言及や.NET Nativeの話なども挟まっていますが、いずれも概要のみです。

背景とねらい

話の基盤には同誌2015年9月号に掲載された「特集1 言語はなぜ動く」というものがあります。これは、Windowsプログラムを含めて様々な言語(JSなど)で書いたプログラムが実際に実行されるまでの流れを解説した、なかなかクールなものでした(私が書いたものではありません)。
この流れを一定汲みつつ、よりWindows上そして主にC#で書かれたプログラムへと振ったのが今回の特集です。
もちろん、各分野を専門的にやっている人からするとイントロぐらいの話です。なるべくすらすら読めて、初心者の方でもなんとなく分かる(そして、更に興味を持って書籍やネットで調べたくなる)というのを狙いにしました。
パラッと読んでみて「これざっくり把握するのに良いから読んでみて」と後輩へ渡すようなシーンが生まれると最高に嬉しいです。
内容は分かりやすい範囲で正確性を失わないよう心がけたつもりですが、不正確な点など気付かれましたらmuo [at] muo.jp宛に指摘を頂けると幸いです。
本エントリでは、書いた原稿の中でボツになったものを中心に、記事の補足を記載します。

参考文献

編集過程でなくなっていましたが、実は一番書きたかったのがこれです。
今回の特集では、.NET Frameworkの内部やWindowsの内部処理に関する紹介をおこないました。これらのトピックについては、次に挙げる書籍が大変参考になります。
なかには入手しにくい本も存在しますが、興味が湧いたらぜひ読んでみてください。
  • インサイドWindows 第6版 上巻および下巻 (著: Mark E. Russinovich, David A. Solomon, Alex Ionescu, 訳: 株式会社クイープ)
    • 今回の特集での関連トピック: Windows上でのプログラム実行の仕組みやOSの内部構造について
    • この筋の定番本です
    • 紙版は結構値が張るので、Kindle版をおすすめします
  • プログラミング.NET Framework 第4版 (著: Jeffrey Richter, 訳: 藤原 雄介)
    • 今回の特集での関連トピック: .NETの型システムやメタデータ、セキュリティに関する構造などについて
    • .NETについてより良く知りたい、と感じたらこの本を開くと大体良いトピックが見つかります
  • The Root of .NET Framework (著: 荒井 省三)
    • 今回の特集での関連トピック: .NET Frameworkの構造やCILからネイティブコードへの変換などについて
    • 今回の特集では扱いませんでしたが、SOSを利用してJITコンパイル後のx86コードを追いかけるくだりはシビれます。Windows 10世代では多少事情が異なるポイントもありますが、原則の部分はほとんど通用します
    • 残念ながら絶版本なので、古本在庫が尽きたら異常に値段が上がりそうです。早めに買うことをおすすめします

クラスライブラリのソースコードに迫ってみる

内容の割に長かったためかボツになったのがこれです。
特集の本文で紹介したように、.NET FrameworkではCLRとクラスライブラリの組み合わせによって実行環境で要求される機能を実現しています。
これらの中身にもっと迫ってみることはできないのでしょうか。
Microsoftは2014年の暮れに.NET Frameworkのソースコードを一部MITライセンスという非常に制限の少ない(利用者側の自由度が高い)ライセンスで一般公開しました。この中をたどっていくことで、CLRの実装詳細やクラスライブラリの中身を追いかけることができます。ここでは、.NET Frameworkが提供する機能のソースコードを追いかける例を紹介します。
C#でプログラムを書いていて、プログラムの状態をデバッグ出力することがしばしばあります。Cプログラミングでいうところのprintfデバッグの類です。多く利用される方法はSystem.Diagnosticsパッケージに含まれるDebug.WriteLineメソッドの利用です。この部分はオープンソースで公開されているので、コードを完全に追いかけることができます。
試しに追いかけてみましょう。
以下の説明でのソースコードはMicrosoft社がMITライセンスにてGitHubで公開しているreferencesourceの抜粋です*2
まず、.NET Frameworkが提供するDebug.WriteLineメソッドにはいくつかのオーバーロードがありますが、ここでは
 Debug.WriteLine("Help me!");
というコードを書いた場合のクラスライブラリ側コードを追いかけてみます。
まず、Debug.WriteLineメソッドはSystem/compmod/system/diagnostics/ディレクトリ以下のDebug.csを起点として呼び出されます。この中でstringを受け取るオーバーロードを探すと
 [System.Diagnostics.Conditional("DEBUG")]
 public static void WriteLine(string message) {
     TraceInternal.WriteLine(message);
 }
が見つかります。
TraceInternalクラスは同ディレクトリにあるTraceInternal.csで定義されています。その中で、呼びだされているWriteLineメソッドは次のとおりです。
 public static void WriteLine(string message) {
   if (UseGlobalLock) {
     lock (critSec) {
       foreach (TraceListener listener in Listeners) {
         listener.WriteLine(message);
         if (AutoFlush) listener.Flush();
       }
     }
   }
   else {
     foreach (TraceListener listener in Listeners) {
       if (!listener.IsThreadSafe) {
         lock (listener) {
           listener.WriteLine(message);
           if (AutoFlush) listener.Flush();
         }
       }
       else {
         listener.WriteLine(message);
         if (AutoFlush) listener.Flush();
       }
     }
   }
 }
ここではロックの取得方法によって処理が2パターンありますが、いずれもListenersからTraceListenerを取り出してそれぞれに対する出力処理をしていることがわかります。
同じファイル内を読むと、Listenersはプロパティとして宣言されており、このプロパティを最初に取得するタイミングでデフォルトの内容が作成されることがわかります。ここで実際に生成されるのはDefaultTraceListenerのインスタンスです。
DefaultTraceListenerのコードはDefaultTraceListener.csにあります。WriteLineメソッドの中でWriteメソッドを呼び、その中でinternalWriteメソッドを呼び出しています。
internalWriteメソッドの記述は次のとおりです。
 void internalWrite(string message) {
   if (Debugger.IsLogging()) {
     Debugger.Log(0, null, message);
   } else {
     if (message == null)
       SafeNativeMethods.OutputDebugString(String.Empty);
     else
       SafeNativeMethods.OutputDebugString(message);
   }
 }
デバッガでのログ出力が有効であればメッセージをデバッガへ流し、そうでなければSafeNativeMethodsOutputDebugStringメソッドを呼び出しています。
この実体はSystem/compmod/microsoft/win32/SafeNativeMethods.csファイルの次の部分です。
 [DllImport(ExternDll.Kernel32, CharSet = System.Runtime.InteropServices.CharSet.Auto, BestFitMapping = true)]
 [ResourceExposure(ResourceScope.None)]
 public static extern void OutputDebugString(String message);
ここでWindows用のDLLへのバインドをおこなう処理が登場しました。これで、あとはWindows APIの領域です。OutputDebugString APIのリファレンスはhttps://msdn.microsoft.com/ja-jp/library/cc428973.aspxにあります。
なお、GitHubのreferencesourceリポジトリに存在しないコードでも、.NET Frameworkのライブラリは2008年から閲覧専用のライセンスで公開されています*3。原稿執筆時点では、最新の.NET Frameworkである4.6.1のソースコードが公開されています。
たとえば本文のPart 1で作成した.NET Frameworkでメッセージボックスを表示するプログラムについて、挙動がおかしいとします。その問題を調査したり、フレームワークとの適合度を高めるための用途であれば、ここのソースを読むことができます。しかしコードをコピー&ペーストして利用したり再配布するような行為は禁止されている(GitHubのreferencesourceリポジトリにあるものと違って、ライセンス上の制限がかなり厳しい)ので注意が必要です。
[*2] ライセンスの全文はhttps://github.com/Microsoft/referencesource/blob/master/LICENSE.txtで公開されています。

サブシステムの概念を簡単に補足

特集の中では細かな事情として省くことになったのですが、Windowsの構造について話をしていくと避けて通れないのがサブシステムです。
Windowsはもともと多様なアーキテクチャの実行ファイルを実行できるように設計されています。Windows向けのexeファイルは素直に実行されますが、たとえばUNIXの仕様に基づくPOSIXサブシステム(以前SFUと呼ばれていたもので、最近のWindowsではSUAと呼ばれます)を利用するものはposix.exeというプロセスがホストする形で実行し、psxdll.dllをライブラリとして利用します。
WindowsなのになぜUNIX標準にもとづくプログラムを?と思うかもしれません。これには歴史的な事情があり、1980年代の米国政府が業務に利用するOSの調達要件としてPOSIX.1というUNIX標準への準拠を必須としていたためです。Windows 8の時代まで、Enterprise版のWindowsはSUAをサポートしていました。Windows Server 2012 R2およびWindows 8.1ではSUAが削除されました。代わりにUNIX系の機能をWindows上で利用したい場合にはHyper-Vで仮想化されたUNIX系OSを利用したりCygwinのような仕組みを利用することを推奨するようになりました。
また、Microsoftが2015年の4月に発表した「AndroidアプリをWindows上で動作させる」仕組みであったProject Astoriaは、Windows上にAndroidサブシステムを実装してプログラムを実行するという構造をとっていました。これも残念ながら2016年2月にプロジェクトの終了が発表されました。
このように、Windowsはシステムコンポーネント次第でさまざまな役割を果たすことができるように設計・実装されてきました(ビジネス的に成功しているかとは別に、このような拡張をおこないやすい内部アーキテクチャというのが肝要です)。

補足やセルフツッコミ

以下では、もう少し書きたかったけれど紙面の都合上削ることになったものや、少々蛇足で雑誌媒体には合わずカットとなったものなどを挙げていきます。
原則的に紙面の各文言へセルフツッコミを入れていく形で書いてみます。

p.9 テキストかバイナリか的な話

でも、テキストエディターで記述したソースコードは、あくまでテキスト形式です。このままでは、コンピュータは実行できません。
EICARテストファイルというものがあります(http://www.eicar.org/86-0-Intended-use.html)。これは完全にASCIIの範囲のテキストのみで書かれた、アンチウィルスソフトの動作確認用ファイルです。
COMという(Component Object Modelでないほう)実行形式として正当なものであり、32-bit版のWindowsでは実行可能です(64-bit版のWindowsではCOMファイルを実行できないので、残念ながら最近の多くの環境では実行して試せないのですが)。こういう変わり種もありますが、バイナリ芸のエリアですね。

p.13 フレームワークのくだり

Webアプリを作成できるようなライブラリ群を指します。
もちろんWebアプリに用途を限定しているわけではありません。文の収まりの都合です。

p.13 コラム コンパイルして実行か逐次実行かというくだり

ただし、現在のスクリプト言語では、すべてを逐次実行しているわけではありません。プログラムの起動の高速化や、実行速度の改善のために様々な処理を実行前に施しています。
そういうわけで、JSがJITコンパイルの領域で世界の先端エリアまで行っていたり、PyPyがCPythonより(コード種にもよりますが)何倍も速くコードを実行できる現代に我々は住んでいます。ASP.NET Coreなどでは原則的に事前のCIL生成・アセンブリ生成を捨てて「実行時コンパイルでいいよね?」と来ているわけですし、扱い方の容易さでいうともはやほぼJSです。
個人的には、事前コンパイルをおこなって中間言語コード・アセンブリを出力しておく言語群とそれ以外との切り分けは近年ほぼ意味を成さなくなってきていると思っています。実行までにかかる計算資源をどのフェーズへ押し付けるかという判断にすぎないので(例示するとGolangにはすばらしいgo runというサブコマンドがあり、実行時に一時ディレクトリでバイナリをビルドしてから実行するスタイルもとれるわけです)。

p.13 フレームワークについて

本文のどの場所というわけではないのですが、フレームワークというフレーズは本当に界隈と人によって持つイメージが違うものです。
たとえばiOSだとずいぶん小さめの粒度でフレームワーク(framework)という言葉を使います。インポート可能なパッケージの、パッケージングフォーマットというレベルでの利用です。いわゆるシステムフレームワークはかなり大きな規模で切り分けられたものですが、ユーザが好きに作って配布できるフレームワーク(*.framework)はnpmやgemのパッケージレベルです。

p.15 コラム

「corflags.exe」です
結局本文では使いませんでしたね。dumpbin.exeで事足りるケースも多いのです。書き換えをしたい時にはcorflags.exeを利用します。

p.16 バイナリを読むのはむりという話


こういう人も居るので、世の中面白いのです。

p.18 CILディレクティブとCILオペコードのくだり

CILディレクティブは、コンパイラやランタイムによるコードの最適化に利用されます。処理内容ではありません。
最適化だけではなくてもちろん.entrypointとか、実行するコードパスを決定するのにも利用されます。

p.20 SxSマニフェストのくだり

.NETアプリのプログラムでは、後述するマニフェスト(SxSマニフェスト)もこのセクションに格納します。
SxSマニフェストは.NET固有事情によるものではないのですが、確認した限りはVSからの標準ビルドで常時出力されているのでこのように記載しました。

p.21 マニフェストたち

ややこしい話ですが、マニフェストには2種類あります。
実際は、まだあります。.NETとWindows界隈、便利な言葉ゆえ「マニフェスト」というのを多用します。

p.22 DLLへ処理を切り出す動機のくだり

DLLが多用される理由は、ライブラリによって勝手に再配布することが許されないものがあるからです。
アプリの配布サイズが増えることを許容すればバージョン差異による問題を避けられるのになんで、という話を先回りした話です。たとえばPhotoshopの内部DLLとかを勝手に同梱して配布したら猛烈に怒られますからね…。

p.22 DLLにコードを切り出す恩恵

つまり、メモリーの節約につながるのです
これは主にシステムコンポーネントの話ですね。ユーザコンポーネントだと、Chromeのように大量のプロセスを生成するプログラムでは恩恵大きいはずですが、普通はせいぜい2-3しかプロセスを立ち上げないのでメリットそこまで大きくありません(無理に切り分けるほどでもないの意)。

p.24 カーネルというかntoskrnl.exeの話

「ntoskrnl.exe」 または「ntkrnlpa.exe」
ほかにもntkrnlmp.exeやntkrpamp.exeがあります。なんと、このあたりはWikipediaに分かりやすいサポート特性リストがあります(Ntoskrnl.exe)。
ちなみに手元の64-bit Windows 10環境では ntkrnlmp.exeです。PAE不要ですしね。

p.28 CILコードからWindowsのAPI呼び出しまでの話

この処理は、CLRの内部でWindowsのMessageBoxWというAPIの呼び出しに変換されています。
CLRの内部というのは嘘ではないですが、実際にこの変換をしているのはFX側ですね。
このことはソースを参照するとわかります。この部分のコードは残念ながらGitHubのreferencesourceリポジトリには含まれないので、制限付きのほう(前述の、ソースコードを探っていくくだりを参照してください)で読むことになります。

p.31 なんでWinRTが出てきたのかという話

これらの処理を実装するために、従来のWin32 APIとは異なる新しいAPIセットが必要だったのです(図1)。そこで登場したのが、Windows 8 で導~
アプリライフサイクルが大きく違うというのもありますね。なにぶんモバイルではバッテリ消費をおさえやすい構造にする必要があったのだし。
タブレットでは、うん。その後にWinRT専用タブレットが消滅したのを見ると、WinRT専用へ振り切るのが早すぎたのは間違いありません。Win32APIの重要度というか、「デスクトップが今まで通りにあってOfficeがこれまで通りウィンドウ状態で動く」というあって然るべきUXを激変させるのがキツかったという話かなと。

p.31 2種類のWindows

と、Windows Phone向けの「Windows Phone 8」という2 種類のWindows系OSが存在しました。
もちろんCEというかWEC(Windows Embededed Compact)もね!

p.33 UWPのパッケージングの話

つまり、2 種類のバイナリファイルを生成してパッケージ内に含めることになります。
もっとも、だいたいx64も別にするので3種類ですけれどね!MIPS増えないかなー(増えない)。