C++11: 関数オブジェクト付き std::accumulate と ラムダ式の実行例
コード
C++11: 関数オブジェクト付き std::accumulate と ラムダ式の実行例
C++11: 最長共通部分列(LCS) と 最長増加部分列(LIS) の実装
C++11 の練習のため、Longest Common Subsequence (LCS) と Longest Increasing Subsequence (LIS) を書いてみる。
最近のコンパイラなら、コンパイルオプション「-std=c++0x」を付けるだけで対応できるはず。
Eclipse CDT なら、プロジェクトの Properties を開き
C/C++ Build -> Settings -> Tool Settings -> お使いのC++コンパイラ -> Miscellaneous -> Other flags
に「-std=c++0x」を追加すればよい。
概要は下記リンク等を参照。
実用性の高そうな機能
for ( for-range-declaration : expression )
[ lambda-capture ]( parameter-declaration-clause ){ compound-statement }
ちなみに、これは C++11 に限定されないが、いわゆる関数型言語の map, filter, fold(reduce) の機能は
C++ ではそれぞれ std::transform, std::remove_if, std::accumulate で代替できる。
(ただし使い勝手は劣る)
例)
std::vector<int> v = {1, 2, 3}
ideone.com で動かしてみる。
実行結果
Ideone.com - sex6kA - Online C++0x Compiler & Debugging Tool
単純な Dynamic Programming で解いたので、ラムダ式は使いどころが無かった。
Python: 辞書内包表記
Python3 および Python2.7 では集合と辞書の内包表記を利用でき、よりシンプルな表現ができるようになった。
>>> d = {1: 'a', 2: 'b', 3: 'c'}
>>> {v: k for k, v in d.items()}
{'a': 1, 'c': 3, 'b': 2}
dict#iteritems は Python3 では廃止され、dict#items でも同等の性能が出るようになった。
>>> {x: x for x in ['a', 'e']}
{'a': 'a', 'e': 'e'}
ちなみに、辞書のキーが全て文字列なら、このような方法で複数の辞書をマージすることも可能。
>>> dict({x: x for x in ['a', 'e']}, **{v: k for k, v in d.items()})
{'a': 1, 'c': 3, 'b': 2, 'e': 'e'}
Docker: 母艦 boot2docker に乗り換える
こちらのブログポストを読み、
Mac のローカルで docker run するためだけに CentOS を立ち上げるのは贅沢すぎると反省。
そして、母艦を boot2docker に鞍替え。
非常に軽快。
そして今まで使っていたコンテナをそのまま新しい母艦へ運搬する。
この感覚がとても新鮮。
Docker: SSH標準装備の開発環境コンテナイメージの作成
Docker コンテナ内で完結する開発環境を作って immutable infra を実現したいというのが動機。
コンテナ内の各種ログを確認したり、ちょっとしたファイルを転送したりするために SSH によるログインも可能としたい。
Docker は 1つのアプリケーションを 1つのコンテナで動かすのが基本的な考え方。
複数のプロセスをまとめてデーモンとして動作させるためにはプロセス管理ツールが必要。
その現時点のデファクトスタンダードが Python で書かれたツール、Supervisor である。
作業ディレクトリ配下に Dockerfile と supervisord.conf、そして必要であればミドルウェア等の設定ファイルを置く。
CentOS ベースのコンテナであれば、各ファイルの内容は以下のような構成になる。
ssh-user という OSユーザを作成し(パスワードも ssh-user)、sudo も可能にする。
FROM centos:6.4 # # SSH # RUN yum install -y --nogpgcheck openssh-server sudo RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key && ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key && sed -i 's/UsePAM.*/UsePAM no/g' /etc/ssh/sshd_config # create user RUN useradd ssh-user && echo 'ssh-user:ssh-user' | chpasswd && echo 'ssh-user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers # # Supervisor # # enable EPEL repository RUN yum install -y --nogpgcheck http://dl.iuscommunity.org/pub/ius/stable/Redhat/6/x86_64/epel-release-6-5.noarch.rpm # install supervisord RUN yum install -y --nogpgcheck supervisor # settings ADD supervisord.conf /etc/supervisord.conf RUN mkdir -p /var/log/supervisor # # Other applications here! # EXPOSE 22 CMD ["/usr/bin/supervisord"]
尚、ADD コマンドの後は常に新しいイメージが生成されるようで、キャッシュが使えなくなる。
Dockerfile を編集して何度もコンテナを作りたい場合は ADD を後回しにした方がいいかもしれない。
[supervisord] nodaemon=true [program:sshd] command=/usr/sbin/sshd -D stdout_logfile=/var/log/supervisor/%(program_name)s.log stderr_logfile=/var/log/supervisor/%(program_name)s.log autostart=true autorestart=true
起動するプロセスを増やしたい場合は、[program:xxx] を増やしてゆけばよい。
sshd だけは autorestart を有効にした方がいいだろう。
例えばお手軽な LAMP環境を使いたい場合は以下のような Dockerfile を作る。
ローカルのみの使用を前提としているので、セキュリティの制限は設けていない。
まず最初に、Dockerホスト側の SELinux を無効化(/etc/selinux/config -> SELINUX=disabled)する。
これをしないと Dockerコンテナ側で getenforce コマンドがエラーになってしまい、SSH 接続ができなくなる。
(getenforce の中で呼ばれている security_getenforce 関数が負数を返し、結果としてパスワード認証後に通信が切断される。このトラブルシューティングで1日を費やしてしまった)
Docker Registry との連携を考えるなら、イメージの名前にはハイフンではなくアンダースコアを使った方がいい。
$ docker build -t local/apache_mysql_php .
Dockerホストのポートとの転送設定を -p オプションで指定する。
$ docker run -d -t -p 50000:22 -p 3306:3306 -p 8000:80 local/apache_mysql_php
この例では、SSH(22) を 50000番に、MySQL(3306) はそのまま、Apache(80) は 8000番に割り当てている。
尚、実際の運用では -v /vagrant/app:/app のようにディスクのマウント設定を行い、
アプリケーションの領域を Dockerホストと連携させると良さそうだ。
(さらに Vagrant の synced folder 機能を使って、自分のPCとDockerコンテナのディレクトリを直接リンクさせることもできる)
2014-03-15 追記
環境によっては、docker run に「-t」オプション(pseudo tty 有効化) が必須となるので付けたほうがいい。
コンテナが起動していることを確認
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c636e5dbaebf local/apache_mysql_php:latest /usr/bin/supervisord 16 seconds ago Up 14 seconds 0.0.0.0:3306->3306/tcp, 0.0.0.0:50000->22/tcp, 0.0.0.0:8000->80/tcp angry_pare
ログイン先は Dockerホストの 50000番ポート。
「Dockerホスト」の箇所は環境に合わせて適宜置換(以下同じ)。ログインパスワードは ssh-user。
$ ssh -p 50000 -l ssh-user Dockerホスト (パスワード入力など) [ssh-user@70a19c076413 ~]$
Docker コンテナの中で適当なPHPページを作る。
$ sudo /bin/bash -c "echo '<?php phpinfo(); ?>' > /var/www/html/info.php"
ブラウザで http://Dockerホスト:8000/info.php にアクセスして、phpinfo() の結果が表示されればOK。
root アカウントで直接操作できるようにしている。
$ mysql -uroot -h Dockerホスト mysql> select host, user from mysql.user; mysql> exit
これで immutable な開発環境のベースができあがった。
VirtualBox: CentOS 仮想ディスクの容量拡張について
VirtualBox で立てた Docker ホストの中でコンテナを作り続けていたら、いつしか仮想ディスクの容量が
full になってしまっていた。
VirtualBox のゲストOSで使っているストレージ容量を増やす手順をメモしておく。
こちらの Dockerfile で構築した CentOS (ベースは opscode 提供イメージ) のディスクを
40GB から 80GB へ拡張する。
尚、物理ディスクの空き容量は十分にあるものとする。
こちらのブログポストの内容に従って作業。
$ vagrant halt
ストレージが VMDK 形式の場合、VBoxManage コマンドで直接その容量を変えることはできない。(※1)
いったん VDI 形式のクローンを作ってから、その容量を拡張する。
以下は packer-centos-6.5-x86_64-disk1 というストレージの場合の例。
$ cd ~/VirtualBox\ VMs/ゲストOSの名前 $ VBoxManage clonehd packer-centos-6.5-x86_64-disk1.vmdk packer-centos-6.5-x86_64-disk1.vdi --format VDI 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% Clone hard disk created in format 'VDI'. UUID: 32e9d2f1-d30c-4dfc-84dd-24ba96e3be31 $ VBoxManage modifyhd packer-centos-6.5-x86_64-disk1.vdi --resize 81920 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
clonehd コマンドの所要時間は実ディスクの使用量やI/O性能に依存。数十分かかる場合もある。
変換し終わったらVMDK形式のファイルは削除してよいが、バックアップとして暫く残しておくことをお勧めする。
VirtualBox の GUI 上で作業した。
設定画面を開いて、IDE Controller 配下の Primary Master ディスクを VDI 形式のものに付け替え。
$ Vagrantfileのあるディレクトリ $ vagrant up $ vagrant ssh
fdisk コマンドで 80GB (+α) が認識されていることを確認。
$ sudo fdisk -l |head -20 ディスク /dev/sda: 85.9 GB, 85899345920 バイト ヘッド 255, セクタ 63, シリンダ 10443 Units = シリンダ数 of 16065 * 512 = 8225280 バイト セクタサイズ (論理 / 物理): 512 バイト / 512 バイト I/O size (minimum/optimal): 512 bytes / 512 bytes ディスク識別子: 0x0004a286 デバイス ブート 始点 終点 ブロック Id システム /dev/sda1 * 1 64 512000 83 Linux パーティション 1 は、シリンダ境界で終わっていません。 /dev/sda2 64 5222 41430016 8e Linux LVM ディスク /dev/mapper/VolGroup-lv_root: 41.4 GB, 41448112128 バイト ヘッド 255, セクタ 63, シリンダ 5039 Units = シリンダ数 of 16065 * 512 = 8225280 バイト セクタサイズ (論理 / 物理): 512 バイト / 512 バイト I/O size (minimum/optimal): 512 bytes / 512 bytes ディスク識別子: 0x00000000
fdisk または parted で実行。以下は parted の例。
最初に unit cyl でシリンダ単位で操作するようにしている。ハイライトした行が parted 内での入力。
$ sudo parted (parted) unit cyl (parted) p モデル: ATA VBOX HARDDISK (scsi) ディスク /dev/sda: 10443cyl セクタサイズ (論理/物理): 512B/512B BIOS シリンダ、ヘッド、セクタ geometry: 10443,255,63. 1シリンダは 8225kB。 パーティションテーブル: msdos 番号 開始 終了 サイズ タイプ ファイルシステム フラグ 1 0cyl 63cyl 63cyl primary ext4 boot 2 63cyl 5221cyl 5157cyl primary lvm (parted) rm 2 警告: WARNING: the kernel failed to re-read the partition table on /dev/sda (デバイスもしくはリソースがビジー状態です). As a result, it may not reflect all of your changes until after reboot. (parted) p モデル: ATA VBOX HARDDISK (scsi) ディスク /dev/sda: 10443cyl セクタサイズ (論理/物理): 512B/512B BIOS シリンダ、ヘッド、セクタ geometry: 10443,255,63. 1シリンダは 8225kB。 パーティションテーブル: msdos 番号 開始 終了 サイズ タイプ ファイルシステム フラグ 1 0cyl 63cyl 63cyl primary ext4 boot (parted) mkpart primary 63 10443 警告: 63cyl から 10443cyl までのパーティションを指定されました。 可能な中で最も近いものは 63cyl から 10443cyl になります。 それでもかまいませんか? はい(Y)/Yes/いいえ(N)/No? y 警告: WARNING: the kernel failed to re-read the partition table on /dev/sda (デバイスもしくはリソースがビジー状態です). As a result, it may not reflect all of your changes until after reboot. (parted) p モデル: ATA VBOX HARDDISK (scsi) ディスク /dev/sda: 10443cyl セクタサイズ (論理/物理): 512B/512B BIOS シリンダ、ヘッド、セクタ geometry: 10443,255,63. 1シリンダは 8225kB。 パーティションテーブル: msdos 番号 開始 終了 サイズ タイプ ファイルシステム フラグ 1 0cyl 63cyl 63cyl primary ext4 boot 2 63cyl 10443cyl 10379cyl primary (parted) set 2 lvm on 警告: WARNING: the kernel failed to re-read the partition table on /dev/sda (デバイスもしくはリソースがビジー状態です). As a result, it may not reflect all of your changes until after reboot. (parted) p モデル: ATA VBOX HARDDISK (scsi) ディスク /dev/sda: 10443cyl セクタサイズ (論理/物理): 512B/512B BIOS シリンダ、ヘッド、セクタ geometry: 10443,255,63. 1シリンダは 8225kB。 パーティションテーブル: msdos 番号 開始 終了 サイズ タイプ ファイルシステム フラグ 1 0cyl 63cyl 63cyl primary ext4 boot 2 63cyl 10443cyl 10379cyl primary lvm (parted) q 通知: 必要であれば /etc/fstab を更新するのを忘れないようにしてください。
設定が終わったら、ssh から抜けてリブート(vagrant halt; vagrant up)する。
pvscan で確認、pvresize で変更。
$ sudo pvscan PV /dev/sda2 VG VolGroup lvm2 [39.51 GiB / 0 free] Total: 1 [39.51 GiB] / in use: 1 [39.51 GiB] / in no VG: 0 [0 ] $ sudo pvresize /dev/sda2 Physical volume "/dev/sda2" changed 1 physical volume(s) resized / 0 physical volume(s) not resized $ sudo pvscan PV /dev/sda2 VG VolGroup lvm2 [79.51 GiB / 40.00 GiB free] Total: 1 [79.51 GiB] / in use: 1 [79.51 GiB] / in no VG: 0 [0 ]
vgdisplay コマンドで状態を確認。
$ sudo vgdisplay --- Volume group --- VG Name VolGroup System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 2 Open LV 2 Max PV 0 Cur PV 1 Act PV 1 VG Size 79.51 GiB PE Size 4.00 MiB Total PE 20354 Alloc PE / Size 10114 / 39.51 GiB Free PE / Size 10240 / 40.00 GiB VG UUID Iv3nnF-GabP-gmv5-sU9W-iGtb-y5yg-9MA68g
Total PE 20354 に対して、Alloc PE が 10114 であることがわかった。
lvdisplay で確認、lvextend で変更。
空き領域を全て使用する場合には -l +100%FREE オプションが便利だ。
$ sudo lvdisplay --- Logical volume --- LV Path /dev/VolGroup/lv_root (略) LV Size 38.60 GiB (略) $ sudo lvextend -l +100%FREE /dev/VolGroup/lv_root Extending logical volume lv_root to 78.60 GiB Logical volume lv_root successfully resized $ sudo lvdisplay --- Logical volume --- LV Path /dev/VolGroup/lv_root (略) LV Size 78.60 GiB (略)
最後に、resize2fs で対象のファイルシステムのサイズを変更。
$ sudo resize2fs /dev/VolGroup/lv_root resize2fs 1.41.12 (17-May-2010) Filesystem at /dev/VolGroup/lv_root is mounted on /; on-line resizing required old desc_blocks = 3, new_desc_blocks = 5 Performing an on-line resize of /dev/VolGroup/lv_root to 20604928 (4k) blocks. The filesystem on /dev/VolGroup/lv_root is now 20604928 blocks long. $ df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 78G 38G 36G 52% /
無事、ファイルシステム上で使えるディスク容量を 40GB 増やすことに成功した。
※1 現バージョンでは対応していないフォーマットだと表示される。
$ VBoxManage modifyhd packer-centos-6.5-x86_64-disk1.vmdk --resize 81920 0%... Progress state: VBOX_E_NOT_SUPPORTED VBoxManage: error: Resize hard disk operation for this format is not implemented yet!
Scala: Docker を使って sbt test を並列化してみた
Docker + CI が巷で流行しているそうなので便乗。
ここに、残念なテストがある。
ローカル上の Redis サーバに接続し、100件のレコードを投入した後で 1件削除。
そのときにレコード数が 99件かどうかを検証するだけの簡単なプログラムだ。
これが全部で 4セットほどある。
このテストは明らかに
というような問題を抱えている。
$ sbt test (略) [info] Passed: Total 4, Failed 0, Errors 0, Passed 4 [success] Total time: 48 s, completed Jan 11, 2014 1:35:59 AM
$ sbt 'set parallelExecution in Test := true' test (略) [info] test1 should [error] x insert 100 records and delete 1 record [error] 'Some(96)' is not Some with value '99' (RedisConnectionSpec.scala:27) (略) [error] Failed: Total 4, Failed 4, Errors 0, Passed 0 [error] Failed tests: [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec1 [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec2 [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec3 [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec4 [error] (test:test) sbt.TestsFailedException: Tests unsuccessful [error] Total time: 14 s, completed Jan 11, 2014 1:37:52 AM
$ redis-cli set 'foo' 'bar' OK $ sbt test (略) [info] test1 should [error] x insert 100 records and delete 1 record [error] 'Some(100)' is not Some with value '99' (RedisConnectionSpec.scala:27) (略) [error] Failed: Total 4, Failed 4, Errors 0, Passed 0 [error] Failed tests: [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec4 [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec3 [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec2 [error] com.github.mogproject.dockersbttest.example.RedisConnectionSpec1 [error] (test:test) sbt.TestsFailedException: Tests unsuccessful [error] Total time: 46 s, completed Jan 11, 2014 1:39:52 AM $ redis-cli del 'foo' (integer) 1
当然ながら、不要な待ち時間を減らしたり、モックを作ったりして状態への依存を極力少なくした
正しいテストを書くのが最もよいアプローチだと思う。
しかし現実にはそれが難しかったり、対応に時間がかかってしまう場合もある。
そこで、Docker を使ってテストクラスの単位で独立したコンテナを立ち上げ
その中で個別のテスト(sbt 'test-only クラス名')を実行したいと考えた。
処理の流れは以下のとおり。
並列処理の多重度は、Docker ホストマシンの CPU数と同じにする。
上記の 2. 〜 6. の処理を行うスクリプトを Python で書いてみた。
docker_sbt_test.py というスクリプトに対して、ベースイメージのリポジトリ名(+タグ名)、テストイメージ内部にある sbt プロジェクトのディレクトリ、ベースイメージに対して行う事前実行コマンドを指定して実行する。
docker_sbt_test.py REPOSITORY[:TAG] [options]
Options:
-h, --help show this help message and exit
-d DIR, --dir=DIR path to the sbt project directory in the container
-s SETUP, --setup=SETUP
commands to be executed in the shell before testing
より詳しい実行例はこちらを参照。
手元 (4CPU, 4GB MEM の VirtualBox環境) で実行してみた結果はこちら。
それぞれのテストが並列で実行され、全て成功。sbt test-only は14〜15秒ほどで完了した。
ログ中の starting sbt と finished sbt の間で 30秒ほどかかっているように見えるが、
これは docker コンテナの起動/終了処理と sbt 自体の起動処理にかかる時間が含まれるためと考えられる。
(ちなみに、単体実行時でも sbt の起動には 5秒程度かかっていた。)
VirtualBox 環境でなければもっと速いかもしれない。
$ ./docker_sbt_test.py local/sbt-test --dir /workspace/docker-sbt-test/example --setup " \ rm -fr /workspace/docker-sbt-test && \ git clone --depth 1 https://github.com/mogproject/docker-sbt-test.git /workspace/docker-sbt-test \ " 2014-01-11 07:24:53,272 [INFO] Creating test image (local/sbt-test:test-1389425093.27) ... (略) 2014-01-11 07:25:13,073 [INFO] Compiling... (略) 2014-01-11 07:25:41,707 [INFO] Getting test class names... 2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec1' ... 2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec2' ... 2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec3' ... 2014-01-11 07:25:48,859 [INFO] Starting sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec4' ... 2014-01-11 07:26:13,257 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec3' ... Starting redis-server: [ OK ] (略) [info] RedisConnectionSpec3 [info] [info] test3 should [info] + insert 100 records and delete 1 record [info] [info] [info] Total for specification RedisConnectionSpec3 [info] Finished in 39 ms [info] 1 example, 0 failure, 0 error [info] [info] Passed: Total 1, Failed 0, Errors 0, Passed 1 [success] Total time: 15 s, completed Jan 11, 2014 2:26:13 AM 2014-01-11 07:26:13,600 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec4' ... (略) [info] Passed: Total 1, Failed 0, Errors 0, Passed 1 [success] Total time: 14 s, completed Jan 11, 2014 2:26:13 AM 2014-01-11 07:26:13,911 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec2' ... (略) [info] Passed: Total 1, Failed 0, Errors 0, Passed 1 [success] Total time: 14 s, completed Jan 11, 2014 2:26:13 AM 2014-01-11 07:26:14,035 [INFO] Finished sbt 'test-only com.github.mogproject.dockersbttest.example.RedisConnectionSpec1' ... (略) [info] Passed: Total 1, Failed 0, Errors 0, Passed 1 [success] Total time: 14 s, completed Jan 11, 2014 2:26:13 AM 2014-01-11 07:26:14,080 [INFO] *** SUMMARY *** 2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec1 -> OK 2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec2 -> OK 2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec3 -> OK 2014-01-11 07:26:14,081 [INFO] com.github.mogproject.dockersbttest.example.RedisConnectionSpec4 -> OK 2014-01-11 07:26:14,081 [INFO] *************** 2014-01-11 07:26:14,081 [INFO] Removing test image (local/sbt-test:test-1389425093.27) ... (略)
こちらに dstat の結果を載せたが、複数のコンテナを同時に起動すると
思ったよりメモリリソースの消費が激しいようだ。
スワップが起こるような状況であれば、もちろん並列化しない時よりもずっと遅くなる。