ビルド職人になるために覚えたコマンドメモ

ここ2年ぐらいffmpegとかopencvとかRuby + CUDAみたいなやつとか たまーにビルド職人になることがあって上手くコンパイルするために各種コマンドを使うことがあるのだけど、 使い方はおろか、普段あんまり使わないのでコマンド名すら忘れることが多々あるためコマンド名とか使い時を覚えている限りざっくりメモしとく。 あとはmanを読めば良い。

pkg-config

インストール済みのライブラリをコンパイルに利用するときに必要なコンパイルオプションを返してくれるやつ .pcファイルを元に返してくれる。 PKG_CONFIG_PATHを指定して利用したりする。

ldd

.so(Shared Object)ファイルが動的リンクで依存しているライブラリへの依存関係を表示してくれる。 何かをコンパイルした結果、共有ライブラリがリンクできているかを調べるのに使える。

$ ldd `which ffmpeg`
        linux-vdso.so.1 =>  (0x00007ffeaa3c0000)
        libavdevice.so.56 => /usr/local/lib/libavdevice.so.56 (0x00007fda9b4f4000)
        libavfilter.so.5 => /usr/local/lib/libavfilter.so.5 (0x00007fda9b1c5000)
        libavformat.so.56 => /usr/local/lib/libavformat.so.56 (0x00007fda9ae0a000)
        libavcodec.so.56 => /usr/local/lib/libavcodec.so.56 (0x00007fda996e2000)
        libpostproc.so.53 => /usr/local/lib/libpostproc.so.53 (0x00007fda994c4000)
        libswresample.so.1 => /usr/local/lib/libswresample.so.1 (0x00007fda992ac000)
        libswscale.so.3 => /usr/local/lib/libswscale.so.3 (0x00007fda99037000)
        libavutil.so.54 => /usr/local/lib/libavutil.so.54 (0x00007fda98ddc000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fda98bbf000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fda988bd000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fda984fb000)
        libz.so.1 => /lib64/libz.so.1 (0x00007fda982e4000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fda980e0000)
        liblzma.so.5 => /usr/lib64/liblzma.so.5 (0x00007fda97ebc000)
        /lib64/ld-linux-x86-64.so.2 (0x0000557f457af000)

ldconfig

ldがリンクするときに共有ライブラリ探すための情報をキャッシュするために使用する。 共有ライブラリをインストールした直後に期待通りに動かなかったら叩いてみたりする。 -pでキャッシュ済みのライブラリのリストを見られるのでリンク失敗するときにみる。

nm

.soとか.oとか実行バイナリとかのシンボル一覧を見るのに使う。 シンボル一覧見つつリンクがうまくいっているかどうか見る。 リンクが失敗してundefined symbolみたいなエラー出たときに何が足りないのか見極めるときに使う。

適当な間違ったコードを書いたときにprtfなんていう存在しない関数でオブジェクトファイルを作ったときに、 nmコマンドの表示結果がprtfに対してUとなっているときはこの時点ではprtfが実際には未定義状態であることを表す。 こういう情報を元にソースコード上にタイポがあってビルドうまくいかないとかそういうのを探した記憶がある。

$ cat a.c
int main () {
  prtf("aaaaaaaaa");
}
$ gcc -c a.c
$ nm a.o
0000000000000000 T main
                 U prtf

nm自体は多分もっといろいろ使えると思う。
nmコマンドでC/C++のシンボルテーブルを見る、C++の名前マングリング、"C"リンケージ、あるいはリンカに関するメモ - 百日半狂乱

objdump

Macでlddしたいときに最初から入ってた気がしたので代わりに利用できる。 実際にはotoolが良いらしい。

rpmbuild

名前の通り、RPMパッケージを作るときに使う。 オプションがややこしいので毎回ググりながら使っていたがモダンな日本語情報を見つけるのが難しいので、 自分が参考にしたページリストを貼る。

cmake

cmakeはmakeで使うMakefileの生成するためのメタなやつで、CMakeLists.txtに生成ロジックが書いてある。 make単体で使うときは再ビルドするときにmake cleanしてたけど、cmake使うときはビルド中のファイルが吐き出されるのは 自分が指定したディレクトリになるのでビルド結果を入れるディレクトリに移動してからコマンドを叩いてディレクトリごと消せば良い。

$ cd /path/to/src_root
$ mkdir build
$ cd build
$ cmake ..
$ make

tar

xxx.tar.gzじゃなくて xxx.tar.bz2 を解凍するときは tar -xvfz みたいな4つ入りオプションじゃなくて、 tar -xf のみで良いとのこと。-zgzip解凍用のオプションとのこと。よく忘れて困るのでメモ。

CUDA, cuDNN, nvcc

NVidiaGPUを使うためのSDK。そこそこ特殊環境なので油断するとハマる。 CUDAを使うときは、とにかく公式の手順通りにインストールすると入る。 各OSごとにインストール用のパッケージが用意されている。(ディープラーニング用のcuDNNは、NVidiaのサイトにアカウント登録しないとダウンロードできないので注意。) docs.nvidia.com

/usr/local/cuda-8.0/lib64以下に共有ライブラリが配置されたりして標準のパスだけではビルド通らないので注意。 -I-Lでパスを追加してコンパイルする。.cuファイルをビルドするときはg++とかの代わりにnvccを利用する。

mkmf

library mkmf (Ruby 2.4.0)

コマンドではないけどRubyのC拡張作る時に使う標準ライブラリ。 いわゆるextconf.rb。ext/以下にdependファイルをおくとextconf.rbで生成するMakefileの末尾にそのdependファイルが追記されるのでそこを駆使すると便利。 ruby ext/hoge/extconf.rbで実際にMakefileを生成してみてデバッグもできる。

xcode-select install

Macでなんかコンパイルするときに#include <..>の探索パスの一覧を出すコマンドが clang -x c -v -E /dev/nullで、これを見たときに /usr/local/include がない場合はヘッダーが見つからないエラーに陥る。 xcode-select installすると以下の様に正しく設定される。

$ clang -x c -v -E /dev/null
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.12.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name null -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-cpu penryn -target-linker-version 274.2 -v -dwarf-column-info -debugger-tuning=lldb -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0 -fdebug-compilation-dir /Users/namai/src/github.com/ruby-dlib/ruby-dlib -ferror-limit 19 -fmessage-length 185 -stack-protector 1 -fblocks -fobjc-runtime=macosx-10.12.0 -fencode-extended-block-signature -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o - -x c /dev/null
clang -cc1 version 8.0.0 (clang-800.0.42.1) default target x86_64-apple-darwin16.4.0
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
# 1 "/dev/null"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 330 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "/dev/null" 2

他に便利なのあれば教えてください。

新婚旅行をしてきた、現地集合・現地解散で。

2016年12月20日〜2017年1月5日まで、17日間の新婚旅行をしてきました。 以前記事に書いた通り(結婚した&妻を追って海外に行く - ainameの日記)、 現在海外に滞在している妻に会うために年末年始の休み+会社の福利厚生の結婚休暇(5日)+有給を活用してイギリスまで単身で乗り込んだ。

f:id:ainame:20170123000849j:plain:w400

一応去年結婚して以来、初めての長期旅行なので新婚旅行という事になるが、それにしても現地集合・現地解散で新婚旅行する人が世の中に一体どれだけいるのだろうか??

せっかくヨーロッパまで行くのだから単にイギリスだけを観光するんじゃなくて、 ヨーロッパの各都市をぶらぶら巡ってヨーロッパ生活の雰囲気を感じてこようという気持ちも持って臨んだ。 実際に行った都市は上のマップ通りで陸路・空路それぞれ活用して6カ国巡った。 3日1回ぐらいは移動していて移動日が入る感じになる。

今回初めてヨーロッパまで行った時の感想とちょっとしたtipsをまとめたい。

ヒースロー空港は怖い

新婚旅行の集合場所はイギリスのヒースロー空港だった。まず入国審査をしなければいけない。 ググったら分かるけど、ヒースロー空港は異常に入国審査が厳しい空港らしい。

f:id:ainame:20170124030504p:plain:w380

実際に強制送還までされた人がいるとか・・・。そんな感じで事前に散々脅されたので、 適当に「サイトシーイング」とか答えるだけでは全然ダメかと思ったかなりビビっていた。

今回の場合、既婚者で指輪を手につけているのに一人で観光しようとしていて怪しいし、 ロンドンでの宿泊先は妻の友人の友人宅(住所知らない)でランディングカードに滞在先の住所が書けなくて、 質問されたらなんて答えたらいいか、英語で正しく答えられるのか・・・??

あわわわわわってなって事前に心配していたとはいえなんも準備せずに空港まで着いてしまって、 いざ入国審査ってなると強制送還という最悪のパターンが頭によぎって(3ヶ月ぶりに妻に会いに来たのに会えずに強制送還ってどんな罰ゲームなの!???うわー)めちゃくちゃ緊張してたし、 いきなり審査の列に並んだらいざという時に話す内容思い浮かばないと思って、 空港のWiFi使って小一時間まだ見ぬ妻とLINEして相談したりして喋る内容決まったぞ!!!ってなって覚悟を決めて列に並んだ。

んで実際、緊張しすぎてもはやなんて聞かれたかあんまり分かんなかったけど、とりあえず自分の第一声は「サイトシーイング」って言った気がする。 あーもうこれ以上質問しないでくれー頼むーうわーなんて思っていたら、なんとそのまま職員のおっちゃんはにこやかに微笑んで軽く通してくれて、 さっきまでの俺の心配を返せ!!って思ったけど、そのあと妻と再会できたときはただ単に久しぶりにあった以上の感動があった気がするよ。

そんな感じで無事合流できたのでした。

ヨーロッパでは英語が通じる

当たり前かもしれないけど、ヨーロッパの人々はかなり英語喋れる人が多いので英語が話せれば困ることはなさそうだった。 とはいえ、全員が喋れるわけではなくフランスとかイタリアではお店の店員さんとかで英語が苦手って感じの人がたまに居て、 返答に困って英語喋れる上司を呼びつけて対応してもらうなんてのもあり、日本人が英語喋れなくてあたふたしているのと似ていてなんだか親近感が湧いた。

今回の旅では、英語がペラペラ喋れる妻が一緒にいるものの厳しい教育方針によって、 飲食店や店での買い物をする際には全て自分が無理やり頑張って話して事を進めて行った。 とはいえ、事前準備をして居たわけではないので必要なフレーズは妻から随時聞き出して学んでいった。

お会計の時に「Can I get the bill?」ってひたすら言ってた。これによって「Can I 〜〜?」的なフレーズの使い方がすごいしっくりくるようになった。 同様に「Do you have〜〜?」的なフレーズもそこそこ使った。「Do you have free WiFi?」とか「Do you have any recommendation?」とか。 そんな感じで本当に初歩的な英会話を体験する良い機会になった。(WWDCでサンフランシスコ行ったときはもっと適当な英語でごまかしていた)

観光しているのは日本人だけでは無い

これも当たり前だけど、観光しているのは自分たちだけじゃなく他のアジア人だってそうだし他のヨーロッパの人が自分の国以外に観光していることも普通にある。 街中歩いていると、アジア人以外は現地の人と観光客の違いはパッと分からなかったけど(ロンドンはとにかく人が多かったので比率で言えば観光客の割合少なかった多分)、 アムステルダム、パリ、ミラノあたりは、あからさまに観光客が結構居たりした。(カメラ持ってるとかね。)

面白かったエピソードが一つあって、ベルギーからフランスへ移動する時に高速バスに乗ったのだけど、 途中休憩のためにバスが停車する際に車内アナウンスがあったのだけどフランス語でしかアナウンスされなくて、 何時まで外ぶらつけるのか全然分からなくて困って、隣の席の何人か分からないけどカップルに英語で聞いて見たら、 「俺たちもフランス語はわからな¯\(ツ)/¯」みたいな感じでさらに困る。他にもフランス語話せない人は結構居て困って居たけど、 そんな感じでワタワタして居たら、¯\(ツ)/¯のカップルの後ろの人たちがフランス語分かるらしくて、英語で時間教えてくれて事なきを得た。

敵の敵はまた味方というか、旅行中現地の言葉が分からないのは日本人だけじゃなくてヨーロッパの人も結構わかってないんだなーという学びがあった。 旅行者同士、お互いの言葉わからないこともあるけど、困ったら困ったもの同士助け合おうみたいな気持ちが生まれた。

イギリスの飯は不味く無いしフランスの飯は不味かった

これは、イギリスに住むかもって周りに言った結果、散々イギリスの飯はまずいまずいって脅されたので、 必要以上にイギリスのご飯にビビっていたのだが、結果的には店を選べば美味いもの食えるし問題ないって分かった。 特にDishoomというインド料理屋は事前に調べていて、実際に行って見たらとても美味くてビックリした。 今まで食べたインドカレーの中で一番美味いし店内がめちゃくちゃオシャレだった。

逆にフランスで歩き疲れてお腹すいてどこでも良いから早くご飯食べようってなって入った適当なカフェのボロネーゼは、 給食のソフト麺のようなグダグダに茹でられたスパゲティでソースも水っけが多くて味薄いし酷かった。 ググってみるとフランス人はみんなグダグダになるまで茹でているみたいなので、スパゲティは2度と注文したくないと思った・・・。 (フランスで食べたものだと、ここの店のラビオリはとても美味しかった。)

という具合で、国ごとに個性はあるけど、店や食べるべき料理の選び方を間違わなければよっぽど不味くて辛い思いをすることはないと思った。 しかし外食は全般的に高いので、住み始めたら気軽には出来なさそうだなーと思った。(どの国もレストランで普通に二人で飲み食いすると6、7千円ぐらい)

ところで、チップに関してはヨーロッパはアメリカほど強気に要求される所はほとんどなく、 会計の時にサービス料数%が既に組み込まれている所があったり、クレカリーダーにクレカを差した時に額を入力させられる時があったら、 適当に料金の10%程度入れておけばやり取りは済んだので、心配はあまりない。

博物館・美術館すごい

ヨーロッパの国の文化・歴史はとてもすごい。今回、妻の趣味もあって美術館とか博物館などを結構多めに周ってとても文化的な観光をした。 グリニッジ天文台は結構変わり種的な感じだけど、エンジニアとしては普段は手を焼くことの多い時間の取り扱いにも、 そこに至るまでに色々みんな苦労して、正確な時間が測れて使えてみんなと共有できるようになったんだなと分かり面白かった。

  • グリニッジ天文台(正確な時間の測り方が安全な航海を実現させて植民地拡大を助けたという話が面白かった)
  • 大英博物館(無料・巨大・展示品もレア度高い・何度も通いたい)
  • アムステルダム国立美術館レンブラントって名前聞いたことあるな、ぐらいの気持ちで見た。夜警かっこいい。)
  • ゴッホ美術館(ゴッホも最初からあの画風を勝ち取ったわけじゃなくていろんな作品からインスパイアされていってあの作品が生まれたという)
  • アムステルダムセックスミュージアム(名前の通りの施設だけど、19世紀のゲイの写真みたいな時代背景を考えると写真が残っていること自体すごいのまであったとか)
  • 最後の晩餐が観れる教会(異常に厳密に壁画が守られていて同時に25人しか壁画のスペースに滞在できない)
  • ダビンチ科学技術博物館(ダビンチの天才さが感じられたついでに、他の展示も結構面白かった。)

イタリアのアサシンクリード感が異常

アサシンクリードを遊んだことがある人は、一度は感じたことがあるはずの「あ、この壁登れそう」感。 アサシンクリード2遊んだ経験があるのでイタリアではフィレンツェとか行ったらもっと楽しかったかもしれないけど、 今回はミラノだったのでゲーム中に登場した建物がなかったけど、ミラノのスフォルツェスコ城はアサクリの中でよく現れる、 衛兵が見回っている城って感じの建築様式?設計?ですごい感動した。

ちょうど実写版アサシンクリードが、ヨーロッパでは先に公開されていていろんな箇所でポスターを見かけた。 特にイタリアではポスターの扱いもかなりデカかったよ。さすがイタリア。

アプリのオフライン機能が便利

今回、旅行のためにWiFiを事前準備したり、現地でSIMを購入せずに旅行した。 これは準備している時に妻がそんなの無くても(駅とか空港とかカフェでWiFi使えるから)大丈夫大丈夫って主張してきたからで、 しばらくどうしようか悩んで海外旅行によく行く人に聞いたりしたけど、結局郷に入っては郷に従への気持ちで妻のやり方についていくことにした。

インターネットがないと呼吸できない人種の人間としては最初はすごい不安だったけど、 WiFiルーターなりSIMカード持ってたとしても遅くて品質の悪い3G回線だったら逆にイライラするだけだなと思って、 割り切って考えたら、持ってないのは不便かもってのは置いといてまぁ良いかって気持ちになった。

着いてから最初は妻が空港とか宿に着くたびに次行く箇所の移動方法を調べてスクリーショット撮ってメモして移動するっていうのを、 やっていてなんとかなっていた。本当に困ったら現地の人に聞いて乗り換え方法聞けば確かになんとかなる。 途中から知ったのがGoogleマップやTripAdvisorのオフライン機能でこれはとても便利で、より快適に移動できるようになった。

これは事前にWiFi環境でダウンロードして準備しておくと良い。こんな感じ。

TripAdvisorで観光スポットを検索 -> Google MapでTripAdvisorからコピった店名・住所で検索 -> 地図通り移動する

ということが全部オフラインで簡単に出来るようになる。とても便利なのでみんな使えばいいと思う。

中国国際航空(AirChina)

今回、中国国際航空の乗り継ぎを駆使して5万でイギリスまで行った。

イギリス行きの往復航空券をたった5万円でゲットする方法 - ainameの日記

中国国際航空は不満点は2点あった。それ以外はそんなに悪くない。 せっかくiPhone 7plus買ったので機内でスマホは使いたい。次回は中国国際航空使わないかもしれない。

  • 機内モードだとしてもiPhoneとかスマホの利用はダメ(でもなぜかiPadはOKだとか)
    • 行きはNetflixダウンロードコンテンツをかなり溜め込んで臨んだけど怒られて全然見られず
    • 帰りはしょんぼりしながらKindleで漫画買いまくって読んでた(中間管理職トネガワ面白かった)
  • 乗り継ぎに使うハブ空港が北京国際空港で次の飛行機を待っている間、人間的なインターネットが出来なくて息苦しい(中国なので)
    • Twitter, Line, Facebook, mixi, instagramなど一通り全部ダメだった
    • モンストは遊べたのでひたすらモンストしていた

まとめ

他にも色々面白かったことはあるけど、書いていてだんだん大変になってきたので一旦この辺で打ち切りたい。

当初の趣旨としてヨーロッパ住めそうか見てくるってのは達成できたとは思っていて、結論としてはどの国でも住めそうだと思った。 どの国が一番良かったかと聞かれるとどの国も良かったし良さ・悪さはあったので1番っていうのは難しいけど、 やっぱり英語習得のことだけ考えたら目に入る文字全部が英語のイギリスが一番だなぁと思った。 (どの国も魅力はあるけど、逆に絶対にここに住みたいって決め手もない)

あとは、今回巡った場所は本当に一部のエリアでしかないので今後ヨーロッパに住んだら他の国とか都市とかも 気軽に旅行に行けるようになるのでいろんな場所に行って見たいなと思うようになったのが良かった。 (旅行前はあまり興味なかったけど、旅行後には興味が生まれた)

最後に、現地集合・現地解散というのは集合は嬉しさがあるけど、解散はとても寂しさが伴うのであんまりオススメしない。 でも、こんな感じの夫婦だからこそ今回のような面白旅が出来たと思うので妻には感謝。

おまけ

海外旅行行くときは、必ず冷蔵庫の扉が閉まっているのを確認してから出発しましょう。

Rubyで並列処理をやっていく #AdventCalendar

mixiグループアドベントカレンダー2016 1日目です。

今回は、自分が今まで利用したRubyでの並列処理を書くためのgemとか知見を紹介します。

機運

先日のRubyKaigi 2016で、Ruby3ではGuildという新しい並列処理のモデル*1が、導入されるというセッションがあったり、concurrent-rubyというgemの開発が流行り初めて居たりと、Ruby界隈でも何となく並列処理がブームきているように感じます。

マルチプロセス/スレッド

しかしRubyで並列処理するのは言語の仕様としてそれなりに制限があり、他の言語のようにThreadをバンバン立ててマルチコアで計算!爆速化!!みたいなのは難しいです。 というのも、Ruby1.9からネイティブスレッドは導入されたものの多くのC拡張を使ったgemのスレッドセーフ性が問題となるため、GIL(Global interpreter lock)と呼ばれる仕組みが存在しており、RubyやC拡張の処理自体が1つのプロセス上で同時に実行されることがありません。(逆にいうとGILのおかげでスレッドセーフ性について何も考えずにRubyが書けている!)

それでも、並列に処理を実行したいときは普通にRubyでアプリケーションを開発していると訪れて、何とかやっていく必要があります。 その場合、以下のような実現方法が考えられます。

  • forkしてマルチプロセス化(PerlとかPHPではこっちが多い)
    • メモリ消費大 (Copy on Writeという仕組みがあるので一定は共有される)
    • スレッドセーフ性考えなくて良くてシンプル
    • Rubyでの計算処理でもCPUのコア数使いきれる
  • マルチスレッド化 + IO多重化
    • メモリ消費小
    • バグなく動かすためスレッドセーフ性が必要
    • Rubyでの計算処理ではCPU使いきれないのでIO処理と組み合わせる

並列処理を書くのは難しい

上述のようなプリミティブな実現方法があったとしても、自分で一から全て正しく書くのはとてもハードルが高いことです。 他の言語でもThreadを直接使うよりを抽象化されたモデル(Future, Actor, async/awaitなど)を利用することが多いです。 Rubyの場合は用途に特化した便利な実装がすでにあるのでこの辺を使う所から始めると良いと個人的に思うので紹介と、これまで得た知見を共有します。

  • Parallel
  • Sidekiq

最初に触れたconcurrent-rubyを使うと他の言語で利用で利用されているような非同期処理の抽象化モデルが利用できますが今回は省略します。

Parallel

ループ処理をめちゃくちゃ簡単に並列化できるライブラリ。 プロセスモデル・スレッドモデル両方採用できます。

サンプルコード。デフォで、マシンのCPU数を調べてその数だけマルチプロセスを起動して処理してくれます。 記法も簡単で、Parallel.mapの引数に配列を渡すだけ。各do〜end内の処理が複数のプロセスやスレッドで処理が行われるようになります。 mapを利用すればRubyArray#mapのように処理した結果を配列で受け取ることも可能です(もちろん引数で渡した配列の順番も保持してくれます)。

# 2 CPUs -> work in 2 processes (a,b + c)
results = Parallel.map(['a','b','c']) do |one_letter|
  expensive_calculation(one_letter)
end

README.md#usage

こういうとき使ってる

  • サーバー1台で実行する程度のスクリプト(集計とか)の処理を早くしたい時
    • 仕様クラスみたいなものを作ってテーブル全件調べて対象データのみ抽出とか
    • 1台で実行すれば処理のアウトプット先を1箇所にまとめるのが簡単
  • 並列に画像などのデータをダウンロード・アップロードする
class FooTargetUserSpecification
  def satisfied_by?(user)
    # ...
    # 重めの判定ロジック
  end

  def satisfied_users(&block)
    User.includes(:some_associations).find_in_batches do |gruop|
      Parallel.each(group, in_processes: 4) do |user|
        @reconnected ||= User.connection.reconnect! || true
        block(user) if satisfied_by?(user)
      end
      User.connection.reconnect!
    end
  end
end

file = File.open('target_users.csv', 'rw+')
spec = FooTargetUserSpecification.new
spec.satisfied_users do |user|
  file.puts(user.id)
end
file.close

知見

  • マルチプロセスで実行するとメモリ食うのでこういうバッチスクリプトをCronで回す場合はちゃんと安定して実行できるかどうか本番データと同じ規模で検証必要
  • 実行内容によってスレッドベースでやると良いのかプロセスベースでやると良いのかは考える(メモリ効率・スレッドセーフ性など)
  • ActiveRecordと組み合わせて使う場合には、connection_pool周りで問題が起きるのでgemのREADME.mdに書いてある再接続処理使うと良い

Sidekiq

いわゆる非同期Jobキューの実装として有名。Sidekiqのプロセスを起動しておいて、RedisにJobを積むと積んだそばから Sidekiqのプロセスが随時ワーカーを起動してJobを消化していってくれます。

スレッドモデルで並列化をしているので、IO処理などでブロッキングされる場合に効果が出るので、 アプリのPush通知を送ったり、メールを送ったり、DBのレコードを更新したりに向いてます。 同様のgemにResqueというものがあるが、あちらはプロセスモデルで実現しているのでメモリを余分に食ったりする。 Sidekiqでもプロセスモデルで実行した方が良い重い計算処理を行いたい場合は並列数1で複数のプロセスを立てれば良いです。

作者の努力によってバージョンが上がるごとにスループットがめちゃ上がったり、gem自身の依存関係が減ったり、エンタープライズ向けの機能も用意されていて徳が高いです。

こういうとき使ってる

  • HTTPリクエスト内では処理しりきれない遅い処理を非同期に実行するJobキュー
    • Push/メール通知
    • 遅延させてUpdateクエリを発行させたい時
    • 動画のエンコード処理
  • 細かい大量の処理を一気にスケールアウトさせて処理したい時
    • 新たなサムネイルを事前作成し、キャッシュを温めたい時
  • S3からDBに登録された画像をダウンロード -> 解析処理 -> DBに永続化
class UserFooWorker
  include Sidekiq::Worker

  sidekiq_options(queue: :default, retry: 3)

  def perform(user_id)
    user = User.find_by(id: user_id)
    return unless user # userが取得できなかったら終了しとく

    user.do_something
  end
end

# 呼び出し側
# perform_asyncを呼ぶとRedisにJobが積まれる
UserFooWorker.perform_async(user.id)

知見

  • Workerのコードを書くときはとにかく冪等制!冪等制!冪等制!と3回ぐらい唱えてからコードを書いてそして読み直す
    • 2回以上同じ処理を実行しても良い処理を書く
    • 1度実行した処理かどうかをチェックできるようにする
    • 仕様として2回実行されてしまうのを一部許容する
  • Workerの要件によって適切にretry回数・条件・間隔を設定する
    • 1度失敗して再度実行したら成功するのかどうか?
      • 通信系はエラーになりやすいので何回かリトライさせる
      • 存在しないデータへの処理は2度と成功しない場合が多い
    • exponential back-offによって1週間後とかに再実行されて嬉しいの???
    • Worker内のコードはできるだけシンプルにしてどこでエラーが発生していつリトライされるのかが分かりやすくなるよう心がける
  • リリースが失敗した場合のリカバリー手段を考えておく
    • 作りが甘くてぬるぽバグとかで大量にリトライJobを出してしまってretryもすぐ消化してしまったとき
      • バグを修正したのちに影響範囲分を再度積み直すとか
  • 1 Workerの粒度を小さくする
  • Queueを意識する
    • リクエスト時に非同期で行うJobを積むQueueと、バッチ処理的に一気にJobを積む際のQueueは分ける(非同期側が詰まる)
    • CloudWatchなどにQueueのサイズをメトリクスとしてPutしておく(AutoScalingに利用できる)
  • Redisを意識する
    • backtraceオプションは便利だが、有効にしたJobを大量に積んで全部こけるとほとんど同様のbacktraceがストレージに書き込まれてものすごい容量を食うので注意
    • XXX.perform_asyncを1万回ループするより、Sidekiq::Client.bulk_pushのAPIを利用して一括で詰むことで負荷もかけずに速く詰める↓のように
# Jobを積む時はRedisに優しくするためにbluk_push使うと、一気に大量のJobを積めて良い
User.select(:id).find_in_batches do |group|
  args = group.map {|user| [user.id] }
  Sidekiq::Client.bluk_push('class' => UserFooWorker, 'args' => args, 'backtrace' => false, 'queue' => 'user_foo_worker_queue')
end

まとめ

Rubyでも簡単に取り入れられる並列処理の書き方について紹介しました。 既存処理を並列化して高速化出来ると気持ち良いので、ぜひ試して見てもらいたいです。 来年はconcurrent-rubyやRxRubyを試してみたい。

iPhone7 Plusを1ヶ月半ぐらい使ってみた感想

Apple StoreのオンラインでiPhone 7 plusのシルバー 256GBを購入して9月25日に届いて、1ヶ月半ぐらい使ってみての感想。 今後海外に行くことも考えてSIMフリー版を購入した。ちなみに以前はiPhone6のゴールド 64GBを利用していた。

良いところ

iOS10の良いところも混じっている

  • TouchIDの指紋認証が素早い&iOS 10のスマホを持ち上げたら画面が点灯するのが便利でロック解除のストレスがほぼフリー
  • ポートレート撮影が面白くて意外とおしゃれな写真になるので撮影が楽しい
  • 6と比べて画面サイズデカくてステレオ音声なので映像見るのが楽しいAbemaTVとかNetflix便利
  • CPUの性能が格段に上がったのを感じる。具体的にこのアプリ使ってる時、というよりはすべてのアプリの初期化処理が早く感じる。
  • メモリ1GB -> 3GBに増えてゲームアプリ -> Safariでちょっと検索やらLINEで返信など -> ゲームアプリに戻るという動作をする際にゲームアプリのメモリが解放されづらくなっててとても便利
  • ストレージ256GBはすごい(全然減らないのでいくらでも写真・動画の撮影できそう)ストレージの余裕は心の余裕
  • Apple Payは普通に便利

悪いところ

  • 値段が高い
  • 重い(慣れた)
  • 片手で持つのに不安(↓こういうの使っていてまずまず使い心地は良い)

Apple Payについて

iPhone 7系の一押し機能はやっぱりApple Payだと思う。 Visa系のカードとViewカードを持っていてQUICPaySuicaオートチャージが出来ている。 コンビニでの支払いは基本iPhone経由で行うようになったし、交通機関もすべてiPhone。 昼飯時にはチェーン店系ならQUICPayでいける(ココイチとか)けど、そんなに利用頻度は高くない。 たまにSuica支払いのできる店なら利用する程度。

近所のスーパーのマルマンストアとかマルエツプチでは電子マネー支払いができなくて現金だよりだけど、 系列によってはスーパーでも普通に使えそうなので次、国内で引っ越しを検討するときはその辺も考慮に入れたい。

Apple Pay経由でクレカを利用することでMoneyTreeにどんどん利用履歴が残るようになったけど、 何を購入したかまでは残らないのでちょっと不満がある。こういうログがPOSレジ側じゃなくて 消費者側にもセキュアに残せるようになったらとても便利だと思う。

というわけで生活の半分ぐらいは現金からiPhoneでの決済になった。 コンビニがすすめてくる既成の各種電子マネーは使える店舗が限られてしまうしポイント溜まるみたいシステム鬱陶しくて全く要らなかったけど、 交通系の電子マネーも含めて電子マネー系がApple Payに集約できるのはとても魅力的。メタ的な存在でかっこいい。

ApplePayのおかげでQUICPayという単語を知ったけど、レジにFelicaリーダーが置いてあるところは大体使えるイメージはある。 iDでは支払ったことないけど使えるかどうかイマイチわからん。

QUICPayの使えるお店|QUICPay 「iD」が使える主なお店|ドコモのiD

総合するとiPhone7 plusはとても満足度高い。

自宅でTOEICの問題を解いてみた

先日海外に行くなどという記事を書きつつも、具体的にどう英語が出来るようになっていくと良いのかというイメージというかロードマップがなくて*1、 英語の勉強を先延ばしにしていた感があるけど、せっかく海外に行くまでにまだ時間があるので少しずつ勉強してみることにした。

推測するな計測せよという言葉がある通り、少なくとも今の自分の力量が分からないことには、勉強しようがないなと思ってTOEIC受けようと思った。 しかし、今から申し込んでも次に受けられるのが来年1月で、よくよく考えると別にTOEICの結果をどっかに提出しなきゃいけないという事情も無いので、 手っ取り早く本屋に行ってTOEICの公式問題集を買ってきて、帰宅後に解いてみた。

CDプレイヤーはないのでMacbook ProにBDドライブ繋いでCDをiTunesで取り込みヘッドホン繋いで聞けるようにし、 iPhoneのタイマーで時間を正確に図りつつズルとか無しで本番さながらの感じで解いた。 リスニング45分、リーディング75分。 問題形式とか時間配分とかそういうTOEIC向けの対策は全く覚えてなくて、 途中ゆっくり読んでしまったりしたけど残り4問残しぐらいで時間を終えた。

TOEICの最終的な点数は他の参加者とかの統計的な情報で決まるらしいので、 問題集だけでは正確な点数はわからないけど、参考スコアで495〜665点のレンジで中間が580点。 TOEICを最後に受験したのは学生の頃就活前の5、6年前ぐらいで、当時は500点満たなかった程度だったと思う。 今回は自分で思ってたよりは出来ていた気がするけど、海外で仕事で使うには全然足りない。最低限の生活するのは何とかなりそうかも。

400点~495点(英語で書かれた看板を見て理解できる) 500点~595点(英語で簡単な質問が理解できる) 600点~695点(英語のメモが理解できる、ゆっくりとしたスピードで話された場合の道順の説明が理解できる) 700点~795点(英語で書かれた社内文書や仕事の進め方について理解できる) 800~895点(Webから英語で書かれた情報の収集、同僚との議論を理解できる) 900点〜990点(英語で書かれた高度な専門書を理解できる、ネイティブの議論を理解できる) 出典 TOEIC受験前に知っておきたい!スコアと難易度の目安|大学生の困った解決マガジン

matome.naver.jp

結果を振り返ると、

リスニング

  • 単語は前より聞き取れることが多かったような気がするので1人が喋っているときは結構聞き取れた
  • 2人で会話するセクションではコンテキストが理解できず何の話をしているのかあまり想像つかなかった

リーディング

  • 時間配分気にしすぎた上に瞬時に回答を判断できなかったせいで前半の穴埋めがかなり適当に答えてしまった
  • 後半の長文問題(?)は時間かけて解いた分結構正解できてたけど時間かけすぎて全問解けてなかった

という感じだったので、これからしばらく単語・熟語やりつつ、 実際の英語での会話のやり取りを動画とか音声で学習してみようかと思った。

とりあえず今月は移動中とか昼休みに単語・熟語学ぶのを普通にやって、 家帰ったら毎日1時間ぐらいNetflixで英語字幕つけたり字幕なしで 繰り返し海外ドラマ(ゲットダウンが気になる)見続けて、日常会話を聞き取る練習してみたりする。

問題集はまだ一回分の試験が残っているので、1ヶ月後にもう一回解いてみて650〜700点ぐらいを目指したい。 仕事で使うには800点以上ないと厳しいとのことなので、少なくとも来年実際にTOEICを何回か受ける際には800点ぐらい取れていると嬉しい。 最終的にはどっかで会話も練習しないといけないけど、DMM英会話など利用する前に英語話せる妻との会話で練習するという手はありそう。 結婚は便利。

*1:日本企業で完全フルリモートするような状況になったとしたら仕事では家にこもって、英語は少しづつ現地で学べばいいんじゃないかとか、会社辞めたら何ちゃら島で1ヶ月ぐらい語学留学したら何とかなるんじゃないかという甘えた案もあった