カテゴリー別アーカイブ: language

gradle daemonを無効にする

javaのバックグラウンドを長く持つ自分にとって最近のお気に入りの言語はgroovyです。

もともと自分は言語の習得にコストを掛けたくなく、新しい言語が少なからず生み出される現状に少なからず嫌悪感を持っているところがなくはないです。
歴史的な経緯で高級言語が生み出されるようなことは好ましいのですが、同時代に構文だけが異なる似たような言語や、今ままでの低級言語のAPIから何故かひどく乖離した構文を提供する言語は疎ましくも思います。

もともとjavaは広く普及していたので自然と使わざるを得なかったのですが、広く使われているだけあってかなり洗礼されています。
javaはやはりLLと比較した時に型付き言語であるためコンパイル時点でかなり品質を担保できます。
またそれでいてCやC++などのレガシーな言語ほどコストを掛けずに開発できることから自分の中では攻撃力/防御力ともに優れた言語、と言った印象でした。
もっというと優れたIDEがあるのがよいですね。

なんですが欲を言うとjavaはその防御力の高さや歴史的には比較的古くから生み出された故にそのAPIに一部不満がある点もなくはないのです。
java8などの新しいAPIでは新しい流れをかなり汲み取っているようで不満があるかというとそういうわけでもないですが、いかんせん他の流れが早すぎるのだと思います。
新しい言語と比べると記述がかったるいことがあります。

このgroovyという言語はjavaというバックエンドを全く損なうことなく、実装速度をぐっとお仕上げてくれるようなものです。
というわけですごく気に入っているのです。

が、良いことばかりかなというと深い部分に行ったりするとちょっと不明な挙動を示したりすることもあるんですが、今回取り上げるのは gradle daemon と呼ばれる仕組みです。

groovyは様々なビルドシステムを利用することができますが、その中でも有名なものの一つにgradleと呼ばれるものがあります。
gradleはgroovyで開発されており、javaやgroovyのビルドツールとして利用できます。

ビルとツールによりgroovyソースはコンパイルされjavaのclassファイルへと変換されたりと様々な事が行われるのですが、その際に多くのオーバーヘッドを含みます。
そのためgradleではそのビルドコストを少なくするためにgradle daemonという仕組みを提供しています。
このdaemonは常駐することでビルドにかかるコストを削減してくれます。

今回この gradle daemon によりちょっとした不都合が起こりました。
現在個人として開発中のシステムで、ものすごくメモリを食うプロセス(javaではない)を起動しなければなく、しかしメモリを食いすぎるゆえに oom-killer によってプロセスが殺されることが続きました。

はじめはそのプロセスのメモリの消費量を抑えることを考えていたので、それが非常に困難であることがわかり、発想を変えて他のプロセスの精査を行うことにしました。

で、TOPコマンドで見てみるといるじゃないですか。でかいのが。

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
15032 z   20   0 3495m 433m  14m S 130.3 23.1   1:09.40 java

上記のプロセスの実態は gradle daemon です。

メモリを大量に消費していますね。物理メモリのおよそ25%をgradle daemonが抱えていることになります。
上記はコンパイル時の状態なんですが、daemonはその目的達成のために各種リソースを抱えたまま常駐します

これはとんでもないということで即見直しの対象として検討しました。
使用されなければswapに吐き出されるんですが、swapに吐き出されるとしてもswapを消費してしまって結構邪魔だし、そこまで頻繁にビルドしない。
そして結局 gradle daemon も oom-killer に殺されているという始末。
というわけでdaemonを起動しないようにしました。

実装としては gradle.properties に下記のように記述を加えました。

org.gradle.daemon=false

これにより無事にメモリを確保することができ、目的のプロセスを起動することができました。

gradleは便利さ故に色々できるのですが、マニュアルがかなり膨大です。
しかも失礼ですが和訳は結構わかりにくいです。おそらく、原文を読んだほうが良いでしょう。

便利なものですが気をつけて使わないとトラブルの温床となりますね。


javaのクラスローダの仕組みについて

javaのクラスローダに関して下記のようなコードが存在するときの内部動作がよくわからなかったので調べ所を残すことにしてみました。

Class clazz = App.class;
ClassLoader cl = clazz.getClassLoader();

クラスとクラスローダについて

さてこの getClassLoader というメソッドは何なのか、ということですが文字通りクラスローダを取得することができます。
このメソッドは Class クラスのメソッドですが、例えばアプリケーションを実装していて同プロジェクトに B というクラスと C というクラスが作成したときに下記のようなコードが存在したときに、この違いはあるんでしょうか?

ClassLoader clB = B.class.getClassLoader();
ClassLoader clC = C.class.getClassLoader();

先に答えを言ってしまうと、この2つで返却されるクラスローダは同じものになります。

理由ですがこの getClassLoader() を呼び出すとクラスは自分の参照するクラスローダを返却します。
すべてのクラスは必ず一つのクラスローダへの参照を持っており、それは自クラスをロードしたときに使用されたクラスローダを参照するようになっています。

そのため B, C は同プロジェクトのアプリケーションとして実装されているので、この2つは同じクラスローダによってロードされるため上記のような場合は大抵は同じクラスローダが返却されます。

クラスローダによるクラスの解決について

またクラスローダは自分の解決するクラスの領域というものが決まっており、階層構造を持ちます。
これはDNSで各ネームサーバが自分のゾーンだけを管理して、解決できない場合は親ネームサーバへと処理を移譲する仕組みと同じようなものです。

javaのデフォルトパッケージを解決するクラスローダのことを「ブートストラップクラスローダ」と呼び、これは一番親のクラスローダとして位置します。
さらにその下にアプリケーションのクラスを解決するための「システムクラスローダ」というクラスローダが存在します。
そんなに複雑でないシステムの場合はこの「システムクラスローダ」がアプリケーションクラスのクラスローダとして機能すると考えていて問題ないと思います。

クラスローダはクラスロードの依頼を受けた際にはまず親のクラスローダに対して解決を移譲します。
もしクラスが解決できないようであれば自分自身の管理する空間を探索します。

例えば下記のようなサンプルコードを用意して実行してみましょう。
ちなみにこのコードはgroovyにて記述してあります。

class Main {
    static void main(String[] args) {
        ClassLoader cl = Main.class.getClassLoader()
        println cl
    }
}

この実行結果は下記のように出力されました

sun.misc.Launcher$AppClassLoader@18b4aac2

「システムクラスローダ」というのは一般的な機能に対する名称のようで、クラス実態としては sun.misc.Launcher.AppClassLoader というクラスが実態のようです。
通常はこのシステムクラスローダがすべてのアプリケーションクラスのクラスローダとして機能します。

異なるクラスローダが用いられるアプリケーションはあるのか

主にJ2EEサーバなどにおいてはwarをおいただけで異なるアプリケーションをホストできる仕組みを提供しているため、各種ウェブアプリケーション単位でシステムクラスローダが異なるような形になると思われます。
設置した複数のwarファイルの中で同じ名称のクラスが存在する際に、うまく解決できないようではアプリケーションとして致命的ですので、このような場合にうまく機能してくる仕組みだと思われます。

通常はひとつのプロジェクトが固有の一つのクラスローダを持つ、、的な理解で問題ないと思います。

参考ページ

https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ClassLoader.html
http://www.nminoru.jp/~nminoru/java/class_unloading.html


javaアプリケーションのcron起動時の文字化けに対応する

概要

javaアプリケーションの起動を行った際に、ある部分で文字化けが起こったので対応について調査する。

具体的には不具合が起こった箇所はjavaアプリケーションから外部プロセスを起動する部分や、メールを送信する機能の本文にVelocityのテンプレート機能を利用しており、これらを利用する箇所でそれぞれ日本語箇所が文字化けしていた。

原因

まず注目したいのは文字化けが起こっているケースがcronによるバッチからの起動に限定されることである。
バッチとは別にwebプロセスも動作しており、これはfat jarを作成してjavaコマンド経由でbashインタプリタ経由で起動している。が、こちらのwebプロセスは特に問題はない。

また先程メールが文字化けすると述べたが、実は文字化けするのは本文だけで、タイトルに関しては文字化けしていなかった。両者の違いは本文は外部テンプレートファイルから読み込んでいるのに対して、タイトルはソースコードに日本語をハードコーディングしている。

このことから大きく予想できるのはcron経由で外部ファイルを読み込むときに何らかの不具合が生じていることである。
cronはenvなどが特殊なんだろうかと当たりをつけて軽くウェブを検索するとこちらの記事を発見

どうやらcron経由でのコマンドはユーザの環境変数は一切適用されない模様。

解決

わかってしまえばなんてことはない。今回は参考サイトに則ってcrontabの記述の上にenv設定を行うことにします。
今回の場合はjavaがおそらく外部ファイルの文字コードを認識できていないのでcrontabの上部に下記のようにファイルエンコーディングを追記しました。

_JAVA_OPTIONS=-Dfile.encoding=UTF-8

無事にアプリケーションが動きましたとさ


TensorFlow GET STARTED その1

この記事は TensorFlow > Develop > GET STARTED > Getting Started With TensorFlow を自分なりに和訳しながら理解しようとしたメモです。

正確な情報は公式ページを確認してください。
このページの情報に不備があったとしてもいかなる責任も負えません。

TensorFlowを始める

低レベルAPI -> 研究者などの細かいレベルで制御したい人向け
高レベルAPI -> 共通した処理を内包してくれているので、ユーザプログラムは同じようなものになる
contrib と名のつくものはまだ開発中のAPIだそうな。正式に採用されているAPIではないため今後廃れていく可能性がある。

このドキュメントでは 低レベルAPIと高レベルAPIの2つの側面から確認する。
それによって内部的にどういう処理がなされているかということをイメージすることができるようになる。

Tensors

Tensorとはベクトル概念の一般化。
というわけではよくわからないのでもうちょっとイメージを高めると
ものすごく大きなものでもその様子に線形性があるときには、わざわざ別に全部の情報を記載する必要はなく、基底の情報だけを格納すればデータとしては十分である。
その正規化された情報のことをテンソルと呼ぶ。

tensor-flowにおいては特定のデータの集合のことをテンソルと呼び、任意の次元の配列で表現された値の集合で構成される。
テンソルは rank という概念を持つ。これは次元の深さに近い
たとえば
3 -> rank 0
 ただのスカラ値であるため次元を持たない
[1., 2., 3.] -> rank 1 (shape [3])
 要素3個で構成される配列
[[1., 2., 3.], [4., 5., 6.]] -> rank 2 (shape [2,3])
 (要素3個で構成される配列)を持つ(要素2個で構成される配列)
[[[1., 2., 3.]], [7., 8., 9.]]] -> rank 3 (shape [2,1,3])
 (要素3個で構成される配列)を持つ(要素1個構成される配列)を持つ(要素2個で構成される配列)

チュートリアル

import

TensorFlowをimportする際には下記の構文を用いる

import tensorflow as tf

computational graph

内部の計算処理をオペレーションノードとして表現し、その処理の流れを連結してできたグラフのことをcomputational graph(コンピュータグラフ)というような呼び方で表現する
各ノードは0個以上のテンソルをinputとしてうけとり、1個のテンソルをoutputとして出力する。

定数もテンソルとして表現される。
定数テンソルは0個のinputをうけとり、定数をoutputとして出力するノードとして表現できる。
定数テンソルを定義するコードは例えば下記のようになる。

import tensorflow as tf

node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0)

print(node1, node2)

ここでこのコードを実行したときには下記のようになる。

$ python hoge.py
(<tf.Tensor 'Const:0' shape=() dtype=float32>, <tf.Tensor 'Const_1:0' shape=() dtype=float32>)

printはノードの保持するテンソルの値(この場合3.0とか4.0)を出力しない。
テンソル値は評価されるときに処理される。

computational graphを実行するには session を用いる。
session は tensorflow の実行時の内部状態を保持して computational graphを実行するためのインタフェースである。
先程の computational graph を実行するコードは下記のようになる

import tensorflow as tf

node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0)

sess = tf.Session()
print(sess.run([node1, node2]))

その実行結果を下記に示す。
node1, node2のテンソル値が表示されることが確認できる。

$ python hoge.py
[3.0, 4.0]

また node Tensorノードが提供するオペレーションを利用してより複雑なノードを構成することができる。
例えば下記のように node1, node2 を結合して 新しいノード node3 を作成することができる。

import tensorflow as tf

node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0)

node3 = tf.add(node1, node2)

sess = tf.Session()
print(sess.run([node3]))

これは実行すると下記のようなnode1とnode2の結合されたテンソルの値を表示する

$ python hoge.py
[7.0]

この例はあまり面白くない。
というのも定数値を利用したら常に同じ結果しか表示されないためである。
次に ノードのinput値を変数として取り扱う placeholders という概念を取り上げる。

import tensorflow as tf

a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b  # + provides a shortcut for tf.add(a, b)

sess = tf.Session()
print(sess.run(adder_node, {a: 3, b: 4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))

placeholder はあとでパラメータを受け取ることを宣言する。
イメージ的には関数やラムダに近い。
また ノードに対するオペレータ a + b は tf.add のエイリアスとして機能する。

結果は下記のようになる。
任意のパラメータをinputとして取り扱えていることが確認できる。

$ python hoge.py
7.5
[ 3.  7.]

また更に複雑なノードも表現することができる。

import tensorflow as tf

a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b  # + provides a shortcut for tf.add(a, b)

add_and_triple = adder_node * 3.

sess = tf.Session()
print(sess.run(add_and_triple, {a: 3, b: 4.5}))

結果は下記のようになる

$ python hoge.py
22.5

公式ページに掲載されている TensorBoard による computational graph を確認すると下記のようになっており、確認すると
adder_node * 3 という表現を用いたときに内部的には 3 という表現を 定数ノードとして表現する。
add_and_triple ノードは adder_node ノードのoutputテンソルと 定数3 のノードの出力テンソルを入力として受け取るノードとして表現されていることが見て取れる。

機械学習においては典型的には入力値は様々な値を取る。
モデルを訓練可能にするためには graph を同じ入力から新しい output を得るために更新する必要がある。
これを実現するために variables を用いることで、訓練可能なパラメータをグラフ上に表現することが可能になる。

tf.constant はコールされた際に初期化されるのに対して、 tf.Variable はコールされた時点では初期化されない。
session.run を実行する前に tf.global_variables_initializer() をコールして variables を初期化する必要がある。

試しにパラメータを追加した線形モデルを構築する

import tensorflow as tf

W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b

sess = tf.Session()
init = tf.global_variables_initializer()

sess.run(init)
print(sess.run(linear_model, {x:[1,2,3,4]}))

実行結果は下記のようになる

$ python hoge.py
[ 0.          0.30000001  0.60000002  0.90000004]

ここまででモデルを作成することに成功した。
だけれどもこのままではまだこのモデルが正しいかどうかを評価することができない。
したがって 求めるべき答え y と、誤差関数を実装する必要がある。

誤差関数は提供されたデータから現在のモデルがどのくらい正確なものかということを評価する。
ここでは linear_model を評価するための誤差関数として線形回帰のための標準的なものを使用する。
提供されたデータと現在のモデルの誤差を自乗して加算する、いわゆる自乗誤差を用いる。

import tensorflow as tf

W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
linear_model = W * x + b

squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)

sess = tf.Session()
init = tf.global_variables_initializer()

sess.run(init)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))

ここで y を定義して目的関数の評価をしてみると下記のようになる

$ python hoge.py
23.66

手動で w や b の定義を変更すると誤差関数を 0 に導くことができる(当たり前だが)
だが機械学習の目的は自動的に適切なパラメータを学習してくれることだ。
次にどうやってこれを達成するかを示す。

tf.train API

tensorflow では誤差関数を最小化するために variable を少しずつ変更する optimizer と言うものが存在する。
シンプルな optimizer として gradient descent(最急降下法)これは誤差関数の導関数から傾きを算出し、誤差が小さくなるように parameter を更新していく手法です。
ふつう導関数を手動で計算するのはうんざりするほど面倒くさい作業で、かつ間違いやすい。

そこで tensorflow では tf.gradients 関数を用いるだけでモデルの記述のみから導関数を自動的に生成することができる(すげえ)
コードはシンプルに下記のようになる

import tensorflow as tf

W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
linear_model = W * x + b

squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

init = tf.global_variables_initializer()
sess = tf.Session()

sess.run(init)
for i in range(1000):
    sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-3]})

print(sess.run([W, b]))

学習された W, b の結果は下記のようにほぼ正解に収束されていることがわかる。

$ python hoge.py
[array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)]

tf.contrib.learn API

tf.contrib.learn は高レベルのAPIで機械学習の実装をシンプルに行うことができます。その内容は下記を含みます。
・training loops を実行する
・evaluation loops を実行する
・データセットの管理
・feeding の管理(?)
また tf.contrib.learn は共通のモデルを多く定義しています

また既存のモデルだけしか使用できないかというとそういうわけでもなく、モデルをカスタムして用いることもできます。

(※サンプルコードに関しては割愛します)


javaにおけるURLとURIの違い

javaにおいて似たような(これはわかってる人からしたらおしかりを受けそうな表現ではあるが)URLとURIについて、実は結構その違いについてうやむやにしたままやってきた部分があるので、この際ですしその違いについて調べてみます。

概要

javaにおけるURL(java.net.URL)と URI(java.net.URI)の違いとかについて述べます

調査

調べましょう。と言っても実はjavaDocsにかなり詳細が書いてありますのでそちらを参照していただけば早い話です。

URLおよびURIを確認しましょう。

かいつまんで解説すると

両者を比較しながら解説しましょう。
※下記の内容は javaの公式 に含まれる内容なのでより正確な情報を確認したい方は参照元を確認してください。

URI(Uniform Resource Identifier)はその名の示すとおり、リソースが指す場所を一意に示すための識別子です。

これには当然おなじみの http://example.com/hoge/fuga.html などのウェブ上のURLなんかを含みます。
幾つか例を上げてみましょう。

http://java.sun.com/j2se/1.3/
docs/guide/collections/designfaq.html#28
../../../demo/jfc/SwingSet2/src/SwingSet2.java
file:///~/calendar

でこれをちょっとした表現で示すと下記のようになっています。

[スキーム:]スキーム固有部分[#フラグメント]
ここで、角括弧 […] は省略可能なコンポーネントを表し、文字 : と # はその文字自体を表します。

さらにスキームが指定してあるものを 絶対URI と呼びます。また絶対でないURIは 相対URI と及びます。
さらにさらにスキーム固有部分がスラッシュで始まらないものを 不透明URI と呼びます。

不透明URIには例えば下記のようなものがあります

mailto:java-net@java.sun.com
news:comp.lang.java
urn:isbn:096139210x

とまあ、こういったようにリソースの識別子を文字列で表現したものがいわゆる URI となります。
不透明URIという表現が示すように、割りと固有の表現もありますが、そこら辺はあくまでそのドメインが解釈するものとして「これもURIですよ!」みたいな感じで(割りと)ゆるい定義をしています。

というわけで上記の定義に則っていればなんだってURIになります。
java実装面の話をしますとURIは単なるリソースを識別するための文字列なんで、それ以上それ以下でもありません。

URI の役割は、あるURIと他のURIが指しているものが一緒かどうか?とか、あるベースURIとある相対URIをくっつけて(これを解決するといいます)新しいURIを作成するとか、そういう責務がメインになります。

それに対して URL はもっと具体的なことを表現します。
Uniform Resource Locator という名前が指すように、何かしらのリソースが存在することを表現します。

またまたjava実装の話になりますが URL は URI で行っていた単純な文字列表現だけでなく、そのリソースにアクセスする手段などを合わせて提供します。
もっというとjavaのURLクラスに関しては ウェブスキーム(http)限定のリソースを表現する実装として機能しているようです。

やや抽象的な感じも残りますが、より詳細が知りたい方は合わせて

RFC2396RFC2732 を参照してみてください。


aws g2インスタンスでtensorflowをGPUサポートで動作させる

概要

機械学習をやる機会があり、巡り巡って こちら の word-rnn を tensorflow 上で動作させるものにめぐりあいました。

学習をするにあたって入力データがそれなりの量になってくると学習に時間がかかり、現実的な時間では終了しません。
そこで色々調べてみると tensorflow には GPUをサポートする機能があり、それによって処理の高速化が図れるようです。
GPUは通常名前のごとくグラフィック処理用の演算装置ですが、その演算処理が機械学習にも応用できるということですね。

今回はそれをaws環境で実行するべく、グラフィックボードを持つg2タイプのインスタンスで動かすことを目標としました。

先人たちの遺産について

一年くらい前から同じようなことをやろうとしていた人たちの記事が結構色々出てきます。
日本でのドキュメントもそこそこ出てきます、が初めから断言しておきますと、かなりの記事がもはや古くなりすぎていて参考になりません

このあたりの流れが非常に早く、現在では随分具合が異なるようです。

またこの記事も将来的に現実と乖離してくると思いますので、その点は十分注意してください。

インストールしてみる

色々参考にしたところ皆さん Ubuntu14.04 のAMIでやられていたので、自分もそれに倣うことにしました。
インスタンス起動後、まずは様々な記事でも導入されているように、必要なモジュールをインストールします。

$ sudo apt-get update
$ sudo apt-get upgrade -y
$ sudo apt-get install -y build-essential python-pip python-dev git python-numpy swig python-dev default-jdk zip zlib1g-dev ipython

二つ目のコマンドを実行するとGRUBローダーの設定を迫られたりしますが、自分の場合は何も選択せずに次に進み
次の選択肢では install package maintainers version を選択しました。

また参考記事の通りに実行していきます。

$ echo -e "blacklist nouveau\nblacklist lbm-nouveau\noptions nouveau modeset=0\nalias nouveau off\nalias lbm-nouveau off\n" | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
$ echo options nouveau modeset=0 | sudo tee -a /etc/modprobe.d/nouveau-kms.conf
$ sudo update-initramfs -u
$ sudo reboot

ここで一旦再起動

$ sudo apt-get install -y linux-image-extra-virtual
$ sudo reboot
# Install latest Linux headers
$ sudo apt-get install -y linux-source linux-headers-<code>uname -r</code>

ここまで来たら tensorflowの公式ページ に従ってインストール作業を進めましょう。

以下は公式サイトの引用ですが、現在では Cubaのバージョンを8.0、cuDNNのバージョンを5をインストールすれば問題ないようです。

Download and install Cuda Toolkit
https://developer.nvidia.com/cuda-downloads
Install version 8.0 if using our binary releases.
Install the toolkit into e.g. /usr/local/cuda.
Download and install cuDNN
https://developer.nvidia.com/cudnn
Download cuDNN v5.

この2つのモジュールをインストールしたあとで 公式ページに則ってpip経由でインストール作業を行います。

自分の場合は python2系 を対象としたかったので下記のように実行しました。

$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.0rc0-cp27-none-linux_x86_64.whl
$ sudo pip install --upgrade $TF_BINARY_URL

アプリケーションコードを実行したところ下記のように正常にモジュールライブラリを実行できているようです。
(幾つか警告のようなものもありますが)
またGRID K520というg2インスタンスで搭載しているグラフィックボードの名前も確認できます。

I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.so locally
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:936] 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_device.cc:885] Found device 0 with properties:
name: GRID K520

処理自体もCPUでの処理と比べて高速化できているようでしたので、これにてインストール作業を完了しました。

参考記事

http://qiita.com/h860a/items/294262d98e1223008252


osx brew で easy_install が正常にインストールされない

概要

python を使用する機会があり、brew経由でインストールを行った。
ついで pip をインストールしようとしたところ下記のようなエラーがでてしまって pip のインストールができない。
これについて対応してみる。

$ easy_install
Traceback (most recent call last):
  File "/usr/local/bin/easy_install", line 9, in <module>
    load_entry_point('setuptools==29.0.1', 'console_scripts', 'easy_install')()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 357, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 2394, in load_entry_point
    return ep.load()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 2108, in load
    entry = __import__(self.module_name, globals(),globals(), ['__name__'])
  File "build/bdist.macosx-10.11-intel/egg/setuptools/__init__.py", line 10, in <module>
  File "build/bdist.macosx-10.11-intel/egg/setuptools/extern/__init__.py", line 1, in <module>
ImportError: No module named extern

調査する

brew を用いてインストールしたのだが、一度消してまた入れてみる

$ brew install python
==&gt; Downloading https://homebrew.bintray.com/bottles/python-2.7.12_2.el_capitan.bottle.tar.gz
Already downloaded: .../Caches/Homebrew/python-2.7.12_2.el_capitan.bottle.tar.gz
==&gt; Pouring python-2.7.12_2.el_capitan.bottle.tar.gz
==&gt; Using the sandbox
Warning: The post-install step did not complete successfully
You can try again using <code>brew postinstall python</code>
==&gt; Caveats
Pip and setuptools have been installed. To update them
  pip install --upgrade pip setuptools

You can install Python packages with
  pip install &lt;package&gt;

They will install into the site-package directory
  /usr/local/lib/python2.7/site-packages

See: https://github.com/Homebrew/brew/blob/master/docs/Homebrew-and-Python.md

.app bundles were installed.
Run <code>brew linkapps python</code> to symlink these to /Applications.
==&gt; Summary
🍺  /usr/local/Cellar/python/2.7.12_2: 2,948 files, 39.8M

すると読み飛ばしていたのだがインストール後の処理が正常に完了していないようなことが表示されていた。

手動で実行してみると

$ brew postinstall python
==> Using the sandbox
Error: Permission denied - /usr/local/lib/python2.7/site-packages/sitecustomize.py20161201-35997-9nuclf

すると権限がないようなことを言われる。これが原因か?

対応

brew は sudo で実行することはできないので(詳しくないができるかも。できても推奨していないが。)
権限で文句を言われている lib/python2.7 に直接一般ユーザでの権限を与える

$ sudo chown -R <user>:<group> /usr/local/lib/python2.7

そして再度後処理を実行してみる

$ brew postinstall python
==> Using the sandbox
==> /usr/local/Cellar/python/2.7.12_2/bin/python -s setup.py --no-user-cfg install --force --verbose
==> /usr/local/Cellar/python/2.7.12_2/bin/python -s setup.py --no-user-cfg install --force --verbose
==> /usr/local/Cellar/python/2.7.12_2/bin/python -s setup.py --no-user-cfg install --force --verbose

原因はよくわからないが動いた。easy_installも動作することが確認できました。
なんてことはありませんでした。


scalikejdbcでmysqlとの通信時のCommunicationsExceptionに対処する

概要

scalaアプリケーションを作成している際に下記のような例外が発生しているケースがありました。

[CommunicationsException: Communications link failure

The last packet successfully received from the server was 15,048 milliseconds ago.  The last packet sent successfully to the server was 10 milliseconds ago.]

今回はこのエラーの原因とその対処についてまとめたいと思います。
ちなみにタイトルにはscalikejdbcと記述してありますが、原因分析についてはmysqlを使用していれば例外なくほぼ同じことが原因であることが多いです。
またアプリケーションについてもscalaといわず、javaを用いていれば同じような対応で解決できます。
(言わずともscalaってjavaのラッパーみたいなものなので)

また私の環境ではscalikejdbcというライブラリを使用したコネクションプールを使っており、検証にそれを用いることとしますし、特にその点については詳しく述べます。

原因

初めに原因だけ端的に記述しておきましょう。
原因としてはアプリケーションが確立したmysqlとの接続、いわゆる「コネクション」が途切れているにもかかわらずアプリケーションではその使えなくなったコネクションを利用してmysqlとの通信をしようとしているからである。

ネットなどで検索するとよくあるのが、一日立ってからアプリケーションが動き出そうとしたタイミングで一回だけ発生する。などの記述が少なくない。

これはアプリケーション(クライアント)が認識しているコネクションの有効性と、mysql(サーバ)が認識しているコネクションの有効性が食い違っているからである。

クライアントでは通常一度作成したコネクションに関してそのまま使い回す。
サーバでは一度作成されたコネクションをいつまでも有効にしていると、新しいクライアントとのコネションを作成する際の邪魔になってしまう。
そのため一定時間アイドル状態(通信が発生していない)であったコネクションをクローズする仕組みが存在する。

そのためサーバ側では閉じたはずのコネクションに対して、クライアントから通信が行われ、結果通信ができずエラーとなるのである。

コネクションプール

さて更にここで掘り下げてコネクションプールを用いている場合にはどうなっているのかということについてより詳しく記述する。
私の環境ではscalikejdbcというライブラリのコネクションプール機能を利用しているが、まあ他のアプリケーションでもコネクションプールを利用している場合はそんなに変わらないでしょう。

コネクションプールとはコネクションを確立する際のコストを減らすため、予め確立しておいたコネクションをすぐに使えるように準備する(プールする、という)仕組みである。

コネクションプールを用いた場合にも先の原因で説明したように全く同じことが言える。
プールされたコネクションは特に読み取り専用のコネクションであれば、一回使用した後もコネクションはクローズされずに何度も使いまわされる。
そして通信がしばらく行われなかった場合、プールされていたコネクションはサーバ側のタイムアウト設定により、使えなくなってしまうのである。

コネクションプールの設定にもよるが、プールされていたコネクションはサーバ側でコネクションが破棄されていたとしてもずっとプールされ続けることがある(というか特に設定しないかぎりは通常だとそうなる)
そのため同様にしばらく(サーバのタイムアウト設定以上)に通信が行われなかった後、再び実行しようとすると同じエラーを引き起こす。

対応

対応としてはいくつかあるが、適切なものとしては通信を行う前にコネクションの検証を行うsqlを発行することで回避できる。
scalikejdbcのような内部でDBCPを利用しているようなコネクションプールであれば、公式にサポートされている機能がある。

設定パラメータに存在するvalidationQueryという項目に適当なsqlを設定する。
例えばselect 1 as oneなど。
そうすることでコネクションを利用する前にその検証クエリを毎回発行するようになり、コネクションが切れていた場合は自動的に再接続を行なってくれるようになる。
コネクションプールを利用していない場合については、自身で実装を工夫する必要がある。

検証

ここまでの話を実装レベルで検証してみよう。

mysqlのタイムアウト値

mysqlの実装におけるコネクションのタイムアウト値はwait_timeoutという項目により設定される。
その項目を確認してみよう

show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

デフォルトでは28800秒である。これは8時間に値する。
すなわちよくあるケースではアプリケーションが最後にデータベースと通信をおこない、その8時間以上経過してから再度データベースと通信を行うような処理を実行しようとしたところでエラーが発生する、というケースである。

検証する上で流石に8時間も待てないので、この時間を変更する

/etc/my.cnfを編集してタイムアウト時間を更新する
今回は下記のように設定することで10秒に指定した

wait_timeout=10

アプリケーションでエラーが発生することの確認

さて、ここでscalikejdbcを用いたplay scalaアプリケーションにて同エラーが発生することを確認する。
テスト用に作成したアプリケーションを用いることとする。
同じ状況が再現できれば、別にアプリケーションはなんでもよい。

application.conf

コネクションプールを利用するためdefaultという接続子を用いて下記のような設定を行う。

db {
  default.driver=com.mysql.jdbc.Driver
  default.url="jdbc:mysql://localhost/test"
  default.username=user
  default.password=password

  default.poolInitialSize = 10
  default.poolMaxSize = 10
}

アプリケーション

検証のため適当なコントローラに下記のような記述をする。
純粋にループしながらselectを行う。ループ中では15秒のwaitを入れる。

def index = Action {
  scalikejdbc.config.DBs.setupAll()

  var name: Option[String] = None

  (0 to 10).foreach(_ => {
    println("try read")
    name = DB readOnly { implicit session =>
      sql"select name from t order by id desc limit 1".map(rs => rs.string("name")).single().apply()
    }
    println(name)
    Thread.sleep(15000)
  })

  Ok("ok")
}

このアプリケーションを実行すると先のエラーが発生した。
予測はあっているようである。

さらに対応としてvalidationQueryを設定してみる。
scalikejdbcにおけるvalidationQueryの設定は下記のようになる。

db {
  default.driver=com.mysql.jdbc.Driver
  default.url="jdbc:mysql://localhost/test"
  default.username=user
  default.password=password

  default.poolValidationQuery="select 1 as one"
  default.poolInitialSize = 10
  default.poolMaxSize = 10
}

この設定を用いてアプリケーションを実行してみると、今度は無事にエラーが発生しないで実行できる。

またこの際のquery.logを確認してみる

...
2016-05-15T23:36:57.488754Z   71 Query	select 1 as one
2016-05-15T23:36:57.489167Z   71 Query	set session transaction read only
2016-05-15T23:36:57.489512Z   71 Query	SET autocommit=1
2016-05-15T23:36:57.490026Z   71 Query	select name from t order by id desc limit 1
...

目的のsqlの直前にvalidationQueryが存在していることが確認できる。
これによって無事に対応することができた。


nginxでbasic認証をhtaccessライクに設定できるモジュールを作成しました

今回nginxにてbasic認証を取り扱うファイルを公開ディレクトリに設置することで解釈してくれるモジュールを作成しました。
githubにて公開してあります。

このモジュールの振る舞いは端的にいうとapacheでいう.htaccessファイルに等しいです。
(機能的には認証のみに限定しておりますが)

インストール方法や設定ファイルのシンタックスなどについてはgithub上に公開してありますので参考にしてみてください。
設定ファイルなどはnginxの公式モジュールである、ngx_http_auth_basicと同様のものになるので、こちらも合わせて参照すると良いと思います。

nginxってbasic認証などを設定するときにはconfigurationファイルに設置してnginxを再起動するしかないんですね。
それだと結構手間になることも多いし、もしかしたら企業などにしてみたらnginxなどのミドルウェアの再起動を行える人間っていうのが限られてくることも少なく無いと思います。
ファイル設置でbasic認証を認識してくれるのであれば、多くの側面から取り回し軽くてよいかなと思って作成しました。

なおそもそも公式の見解としては、主に速度面からこのような設定ファイルをリクエスト毎にチェックするということを推奨していません。
当然そこら辺のボトルネックについては増加するということで、検証環境や開発環境などで利用すると良いかと思います。


nginx拡張モジュールをアップデートしました

先日作成しましたnginxの拡張モジュールを更新しました。
(前回の更新はこちらになります)

前回の拡張ではunixのshared memory segment上にデータを展開するようにしました。
これによって各プロセス間でユーザからのアクセス情報を共有できるといったものでした。

今回の対応では更にネットワーク上で構成された複数のnginxサーバでもデータを共有することを目的として、ネットワーク上のストレージ(今回の対応では実装としてはmemcachedを対象)に対応するようにしました。

一見ストレージとして memcached に格納できる様になっただけですが、内部的なコードはまた別の側面が改良されました。
当初は特定のストレージ、具体的にはプロセスのheap領域や前回対応したosの共有メモリセグメントにデータを置くという想定でしたが、ngxinのconfigurationファイルでストレージを指定できる方式にし、コードをほぼすべて書きなおしてあります。

これによって今現在指定できるストレージであるshmemとmemcachedに関してはソースコードが完全に分離できる形でインターフェースを切り分けています。
nginxが起動した時点でこのモジュールはconfigurationファイルを読み取り、指定されたストレージに基づいた初期化を行います。

実装としてはこのインタフェースは下記のような関数ポインタを定義した構造体として定義しており、このモジュール内部でのライフサイクルを定義しています。
例えばinit関数ポインタには各種ストレージを初期化する関数ポインタが代入されることを期待しており、update_entryでは各種ストレージ内部での更新処理を行う関数ポインタが代入されることを期待しております。

handler側ではこの構造体を元にライフサイクルを初期化〜破棄に関するまで定めており、適宜それぞれの箇所で関数を呼び出します。
ストレージ側の実装ではこれらのインタフェースを元に各種イベントにおける処理を実装することになります。

/**
 * function pointers. these behave like interface.
 */
typedef struct {
	int (*init)(ngx_cycle_t *cycle, ngx_http_access_filter_conf_t *afcf);
	void* (*get_entry)(char *key, ngx_http_access_filter_conf_t *afcf);
	storage_entry_t* (*get_data)(void *entry_p);
	void (*free_entry)(void *entry_p);
	int (*add_count)(char *key, void *data, ngx_http_access_filter_conf_t *afcf);
	int (*set_banned)(char *key, void *data, ngx_http_access_filter_conf_t *afcf);
	int (*update_entry)(char *key, void *entry_p, ngx_http_access_filter_conf_t *afcf);
	int (*create_entry)(char *key, ngx_http_access_filter_conf_t *afcf);
	int (*fin)(ngx_cycle_t *cycle, ngx_http_access_filter_conf_t *afcf);
} storage_accessor;

つまりこのnginxモジュールの中で更にストレージをモジュールとして扱い実装しており、これによりストレージは任意のものに差し替えられる拡張性を手に入れることになります。
これはすなわち、今後他のストレージを使用したいというような場合において、追加で実装したい時にはこのインタフェースで期待する関数を実装することだけで可能になります。