LinuxでGPUの情報を取得する

LinuxGPUの情報を取得する方法を調べました。以下の表示例およびコマンド例は、すべてUbuntu 18.04 + GTX 1070という環境で試しました。OSによって差異があるかもしれません。

1. lspciを使う

lspciは、PCIバイスのリストを表示するコマンドです。以下のように表示されました。

$ lspci | grep -i VGA
01:00.0 VGA compatible controller: NVIDIA Corporation GP104 [GeForce GTX 1070] (rev a1)

lspciが入っていない場合は、

$ sudo apt install pciutils

としてください。

2. glxinfoを使う

glxinfoは、OpenGLなどに関する情報を表示するコマンドです。

$ glxinfo

としたときの表示の一部を以下に示します。

...(略)...
OpenGL renderer string: GeForce GTX 1070/PCIe/SSE2
OpenGL core profile version string: 4.6.0 NVIDIA 440.33.01
OpenGL core profile shading language version string: 4.60 NVIDIA
...(略)...

glxinfoが入っていない場合は、

$ sudo apt install mesa-utils

としてください。

3. lshwを使う

lshwは、ハードウェアのリストを表示するコマンドです。オプション-C displayを追加すると表示する情報をdisplay関係だけに限定できます。sudoをつけずに実行すると「出力が不完全または不正確な可能性がある」と警告が出ますが、私の場合はつけてもつけなくても同じでした。

$ sudo lshw -C display
  *-display                 
       詳細: VGA compatible controller
       製品: GP104 [GeForce GTX 1070]
       ベンダー: NVIDIA Corporation
       物理ID: 0
       バス情報: pci@0000:01:00.0
       バージョン: a1
       幅: 64 bits
       クロック: 33MHz
       性能: pm msi pciexpress vga_controller bus_master cap_list rom
       設定: driver=nvidia latency=0
       リソース: irq:138 メモリー:f6000000-f6ffffff メモリー:e0000000-efffffff メモリー:f0000000-f1ffffff IOポート:e000(サイズ=128) メモリー:c0000-dffff

lshwが入っていない場合は、

$ sudo apt install lshw

としてください。

4. nvidia-smiを使う

NVIDIA社のGPUを使っている場合はnvidia-smiが定番です。

$ nvidia-smi
Thu Feb 27 21:41:45 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.33.01    Driver Version: 440.33.01    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| 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 1070    On   | 00000000:01:00.0  On |                  N/A |
|  0%   36C    P8     8W / 151W |    628MiB /  8116MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
...(略)...
+-----------------------------------------------------------------------------+

参考URL

Docker ImageからContainerを作るコマンドのまとめ

いまさらながらDockerの使い方を勉強中です。この記事では、Dockerを使うための環境構築方法と、誰かが作成したDocker ImageをもとにDocker Containerを作成して動かす方法についてまとめます。

環境構築

以下ではUbuntu 18.04にDocker Community Editionをインストールする場合について説明します。

Docker Community Editionのインストール

現時点でのインストールの流れは以下のとおりです。

  • "Uninstall old versions"の節に従い、既にインストール済のdockerを(もしあれば)削除
  • "SET UP THE REPOSITORY"の節に従い、aptを使ってDocker CEのstableをインストール

動作確認

現時点で以下のバージョンが入りました。

$ docker --version
Docker version 19.03.5, build 633a0ea838

正しくDockerをインストールできていれば、sudo docker run hello-world によりHello from Docker!というメッセージが表示されます。

また、sudo docker image lsで、ローカルに取得されたイメージが表示されるはずです。

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              fce289e99eb9        13 months ago       1.84kB

非rootでもdockerを使えるようにする

インストールしただけだとrootしかdockerを使えません。非rootユーザでもdockerを使えるようにするには、 * Post-installation steps for Linux | Docker Documentation の "Manage Docker as a non-root user" に従います。

成功すれば、docker run hello-worldでメッセージが表示されるようになります。以降の説明ではこの設定を行ったものとします。

使い方

Dockerのバージョンを調べる

docker -vで簡易版のバージョン情報を、docker versionで詳細版のバージョン情報を表示できます。以下は私の環境での例です。

$ docker -v                            
Docker version 19.03.5, build 633a0ea838
$ docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea838
 Built:             Wed Nov 13 07:29:52 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:22 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

Docker Imageの取得

まず、誰かが作成したDocker Imageを取得するところから始めます。Docker Imageには"Base Image"と"Child Image"の2種類があります。

Base Imageは親を持たないImageで、例えばubuntu, debian, busyboxなどのOSが代表的です。 Docker Hub にて大量のBase Imageが提供されています。 以下では、Docker Hubから、Base Imageの一つであるubuntuを取得して使ってみます。

$ docker pull ubuntu

とすると、Docker Hubにあるubuntulatestタグがついたimageが取得できます。

タグを明に指定することで特定のバージョンを取得することも可能です。例えば

$ docker pull ubuntu:20.04

とすると、現時点では開発版であるバージョン20.04のUbuntuが取得できます。

これまでに取得したimageの一覧はdocker image lsまたはdocker imagesで確認できます。

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              ccc6e87d482b        3 weeks ago         64.2MB

Docker ImageからDocker Containerを作る

取得したDocker ImageからDocker Containerを作るにはdocker runコマンドを使います。

$ docker run ubuntu

上記コマンドを実行すると一見何も実行されないように見えますが、実際には以下が順番に行われます。

  • ubuntuという名前のDocker Imageがローカルにあるかを探す。もしなければDocker HubからDocker Imageを取得
  • ubuntuというDocker Imageから、Docker Containerを作成
  • Containerの中でコマンドを実行

さきほどの例ではコマンドを与えなかったので、ただコンテナが作成されただけでした。次の例では、コマンドls -alFを与えてみます。コンテナの中にあるルートディレクトリ以下のファイル一覧が表示できます。

$ docker run ubuntu ls -alF
total 72
drwxr-xr-x   1 root root 4096 Feb 11 08:51 ./
drwxr-xr-x   1 root root 4096 Feb 11 08:51 ../
-rwxr-xr-x   1 root root    0 Feb 11 08:51 .dockerenv*
drwxr-xr-x   2 root root 4096 Jan 12 21:10 bin/
drwxr-xr-x   2 root root 4096 Apr 24  2018 boot/
drwxr-xr-x   5 root root  340 Feb 11 08:51 dev/
drwxr-xr-x   1 root root 4096 Feb 11 08:51 etc/
drwxr-xr-x   2 root root 4096 Apr 24  2018 home/
drwxr-xr-x   8 root root 4096 May 23  2017 lib/
drwxr-xr-x   2 root root 4096 Jan 12 21:10 lib64/
drwxr-xr-x   2 root root 4096 Jan 12 21:09 media/
drwxr-xr-x   2 root root 4096 Jan 12 21:09 mnt/
drwxr-xr-x   2 root root 4096 Jan 12 21:09 opt/
dr-xr-xr-x 367 root root    0 Feb 11 08:51 proc/
drwx------   2 root root 4096 Jan 12 21:10 root/
drwxr-xr-x   1 root root 4096 Jan 16 01:20 run/
drwxr-xr-x   1 root root 4096 Jan 16 01:20 sbin/
drwxr-xr-x   2 root root 4096 Jan 12 21:09 srv/
dr-xr-xr-x  13 root root    0 Feb 11 08:51 sys/
drwxrwxrwt   2 root root 4096 Jan 12 21:10 tmp/
drwxr-xr-x   1 root root 4096 Jan 12 21:09 usr/
drwxr-xr-x   1 root root 4096 Jan 12 21:10 var/

コンテナの中に入ってインタラクティブに操作を行うには、-itを使います。exitで終了です。

$ docker run -it ubuntu bash

ここで作られるコンテナは使い捨てです。試しに、上記でコンテナの中に入って適当にファイルを作成してコンテナを終了すると、次にコンテナを作成したときにはそのファイルは存在しないことが分かります。

ここまでの作業で、コンテナを使ったあとの残りカスが出ています。docker psしても何も表示されませんが、

$ docker ps -a

とすると、例えば以下のように、停止状態のコンテナが一覧になって表示されます。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
45f580404989        ubuntu              "bash"              16 minutes ago      Exited (0) 14 minutes ago                       clever_rhodes
8231b2e245c4        ubuntu              "bash"              18 minutes ago      Exited (0) 18 minutes ago                       sharp_moore
1aa25f7fc213        ubuntu              "ls -alF"           20 minutes ago      Exited (0) 20 minutes ago                       awesome_proskuriakova

停止状態のコンテナを完全に削除するには、

$ docker rm (コンテナID)

とします。

停止状態のコンテナをまとめて一斉に削除するには

$ docker container prune

とします。

コンテナを使い終わるたびに削除をするのは面倒です。コンテナを使い終わったあとに自動で削除されるようにするには、runコマンドに--rmをつけて実行します。

$ docker run --rm -it ubuntu bash

例:GitLabを使う

"Child Image"の利用例として、GitLabをローカルマシンで動かすことを試してみます。

GitLab Docker images | GitLab にある手順書を従ってGitLabをインストールすることもできます。 しかし、これから紹介するDockerを使った方法であれば、もっと簡単にGitLabを動かせます。

GitLab Docker images | GitLab にあるコマンドを少し変えて、以下のコマンドを実行します。

$ docker run --detach \
  --publish 10443:443 --publish 10080:80 --publish 10022:22 \
  --name gitlab \
  --restart always \
  --volume /srv/gitlab/config:/etc/gitlab \
  --volume /srv/gitlab/logs:/var/log/gitlab \
  --volume /srv/gitlab/data:/var/opt/gitlab \
  gitlab/gitlab-ce:latest

--detachはコンテナをバックグラウンドで動かすという意味です。少し待った後、ブラウザでlocalhost:10080にアクセスすると、GitLabが立ち上がっていることがわかります。他の引数の意味は以下の通りです。

  • --name: コンテナに付ける名前の指定
  • --publish: コンテナのポートとホストのポートとの対応付けの指定。10443:443は、コンテナのポート443をホストのポート10443に対応付けるという意味
  • --restart always: システムをリブートしたあと、このコンテナを自動でrestartする
  • --volume: GitLabのデータの保存先の指定。この例の場合、すべてのGitLabデータは/srv/gitlab/以下に保存される

コンテナを停止状態にするにはdocker stop gitlabとします。GitLabで遊び終わったら、/srv/gitlab/にGitLabが保存したデータ一式を消しておきましょう。

参考URL

Santa's Workshop Tour 2019 参加記

10日前まで開催されていたKaggleのSanta's Workshop Tour 2019に参加していました。結果は1620チーム中の142位。Kaggleで初めてのメダルであるブロンズメダルを獲得できました。最適化系のコンテストなのに問題のサイズが小さく、コンテストの序盤から最適解にたどり着く人が徐々に増えていく展開。しかも最適解にたどり着いた人は皆Gurobi や CPLEXなどの商用最適化ソルバを使っているらしいことがわかり、途中で戦意喪失しました。

問題概要

5000個のファミリーをday1からday100までのいずれかに割り振り、あるコストを最小化する最適化問題です。

各ファミリーには以下の情報が与えられます。

  • 家族を構成する人数: 2人から8人。4人が一番多い
  • 希望日: 各家族はchoice_0からchoice_9までの希望日を持っている。

コストは以下の2つの和として計算されます。

  • preference cost
    • 家族の割りあて方に対してつくコスト。各家族は、choice_0に割り当てるとコストが小さく、choice_9に割り当てるとコストが大きい。choice_0からchoice_9以外の日に割り当てるとさらに大きなコストとなる。
  • accounting penalty
    • 各日に割り当てられた人数の連続性に対してつくコスト。ある日に割り当てられた人数と、その次の日に割り当てられた人数とのずれが大きいほど大きなコストとなる。
    • accounting penaltyは具体的には下式で与えられる。ちょうど125人のときにpenaltyが0になることに注意。

f:id:minus9d:20200127211351p:plain

やったこと

基本的には、以下の繰り返しでした。

  • 強いNotebookが発表されて自分の順位がガタ落ちする
  • 強いNotebookの出力を初期値として、自分の実装した最適化手法を適用し、すこし良い値を出す

最終スコアである69410.61には以下のようにして到達しました。

  • Manual to improve submissions | Kaggle で提案された、特定の日を強制的に125人にするノートブックを流用。day37, day44, day51, ...を強制的に125人とした
  • それにより得られた解はいったんスコアがとても悪くなるが、以下の最適化を繰り返すと、最終的に69410.61に到達した
    • 2家族をランダムに選び、割り当て日をスワップ
      • スコアが悪化する場合でも、少しの悪化であれば確率的に受け入れる
    • N家族をランダムに選び、そのN家族について割当を総当りし、もっともよいスコアのものを選ぶ
      • もしある家族が今choice_Kを選んでいるとしたら、choice_0からchoice_Kまでを総当り
      • Nを大きくするほどあたりを引く可能性が大きくなるが、計算時間も大きくなる

焼きなましで最適解に近づけないかと試行錯誤しましたが、うまくいきませんでした。最適解の割当結果を見るとちょうど125人である日がもう一日多かったのですが、そのような分布を焼きなましで作ることは相当難しいようでした。

Windows Subsystem for Linux (WSL) でscreenを動かす

現象

Windows Subsystem for Linux (WSL) で動かしているUbuntu 16.04にてscreenを動かそうとすると以下のエラーが出ました。

$ screen
Cannot make directory '/var/run/screen': 許可がありません

解決策1 (推奨)

screenが使用するディレクトリを環境変数で指定する方法があります。例えば~/.screendirをそのディレクトリとして指定するとします。以下のコマンドでディレクトリを用意します。

$ mkdir ~/.screendir
$ chmod 700 ~/.screendir

そして、以下に示す環境変数の指定を、.bashrcや.zshrcなどに書けばOKです。

export SCREENDIR=$HOME/.screendir

解決策2 (非推奨)

ベストではない解決策としては/var/run/screenディレクトリを作成する方法があります。

$ sudo mkdir /var/run/screen
$ sudo chmod 777 /var/run/screen

この方法で一時的にscreenを使うようになりますが、OSを再起動すると/var/run/screenディレクトリが消えてしまうので、根本的な解決にはなっていません。

参考URL

Windows Subsystem for Linux (WSL) のホームディレクトリの場所

Windows 10で導入されたWindows Subsystem for Linux (WSL) を使ってインストールしたLinuxのホームディレクトリの場所を調べるには、コンソールでホームディレクトリに移動したあと

$ explorer.exe .

とすればよいです。

WSLで導入しているUbuntu 16.04から上記コマンドを打ったところ、

\\wsl$\Ubuntu\home\(ユーザ名)

エクスプローラーで開きました。

WSL Update Lets You Access Linux Files in Windows Explorer - OMG! Ubuntu! によると、この機能はWindows 10 Version 1903から加わったもので、9P protocolという仕組みを使って実現されているそうです。

Windowsのバージョンを調べるには、Win + Rで出てくるウィンドウにwinverと入力してください。

robocopyの使い方メモ

robocopyというコマンドを使って、Windowsでタイムスタンプを保持してフォルダまるごとコピーするときの自分用メモです。

基本的な使い方

基本コマンドは以下の通り。(src)と(dst)にはそれぞれコピー元フォルダ、コピー先フォルダを入れてください。

robocopy  (src) (dst) /E /COPY:DAT /DCOPY:DAT /r:3 /w:1 /LOG:copy.log /xd "$RECYCLE.BIN" /xd "System Volume Information"

他のオプションの意味は以下の通り。

  • /E: 空のディレクトリもコピー
  • /COPY:DAT: ファイルのデータ、属性、タイムスタンプをコピー
  • /DCOPY:DAT: ディレクトリのデータ、属性、タイムスタンプをコピー
  • /r:3 /w:1: コピー不能なファイルがあったとき、3回リトライ。リトライの間隔は1秒。デフォルト値の設定だとリトライが終わらない。
  • /LOG:copy.log: ログをcopy.logファイルに保存
  • /xd "$RECYCLE.BIN": ごみ箱はコピー対象から外す
  • /xd "System Volume Information"": 隠しシステムフォルダはコピー対象から外す

バッチファイルの名前に関する注意点

robocopy.batというバッチファイルを作って、その中でrobocopyを呼び出すと、robocopyコマンドではなくrobocopy.batが呼び出されてしまうので、無限ループに陥ってしまいます。バッチファイルの名前を変えましょう。

コピー先のフォルダが見えなくなる?

コピー元をドライブ、コピー先をフォルダとすると、なぜかコピー後にコピー先のフォルダが見えなくなる現象が発生しました。以下にそのときの状況を記します。

使ったコマンドは以下です。ここで、 コピー前にはC:\dir1が存在していてC:\dir1\dir2が存在していない状態でした。

robocopy  E:\  C:\dir1\dir2  /E /COPY:DAT /DCOPY:DAT /r:3 /w:1 /LOG:copy.log /xd "$RECYCLE.BIN" /xd "System Volume Information"

コピーは行われたのですが、なぜかC:\dir1\dir2フォルダに隠し属性がついていて、エクスプローラーから見えませんでした。恐ろしいことに、エクスプローラーのオプションで「隠しファイル、隠しフォルダー、および隠しドライブを表示する」を有効にしていたとしても、コピー先のフォルダは見えません。エクスプローラーのオプションで「保護されたオペレーティング システム ファイルを表示しない(推奨)」を庵チェックすることでようやくコピー先のフォルダが見えるようになります。

ちなみにCygwinからは何もしなくてもコピー先のフォルダが見えました。

どうも After Robocopy, the copied Directory and Files are not visible on the destination Drive - Stack Overflow で説明されているように、あるドライブのルートを、あるフォルダにコピーしようとしたときに、コピー先のフォルダが見えなくなることがあるようです。

この現象を回避するには、robocopy

/A-:SH

というオプションをつければOKです。

すでにコピーが終わってしまったあとでも手はあります。コピー先のフォルダに対して

attrib -h -s C:\dir1\dir2

とすれば、隠しフォルダを普通のフォルダに戻せます。

参考リンク