Akata Works

東京エンジニア。主にRuby,Go,たまにAWSとiOS。ゲーム音楽が好きです。連絡はTwitterかakata.onen@gmail.comまで

未変更のファイルが変更扱いになっていた問題(サブモジュール編)

最近、git addしてもgit checkoutしても、変更扱いされているファイル(以下hoge)がありました。
僕としてはhogeをまったく変更した記憶がないんですが・・

同じような現象としてファイルモードの問題があったので、
下記URLを参考に解決しようと思ったのですが、
あんまり関係ありませんでした(ノ゚゚Д゚゚)ノオラァッ!!


その後・・

たまたまgit submodule initgit submodule updateしたときにhogeに対して下記エラーが発生しました。

No submodule mapping found in .gitmodules for path hoge

なんかサブモジュールになってる・・
工エエェェ(´д`)ェェエエ工

どうやら「インデックスに紐付いていないサブモジュールがあるぞい」というエラーらしいです。

サブモジュールはファイルモードが160000になっているため、
以下コマンドでインデックス一覧を確認できます。

git ls-files --stage | grep 160000

インデックス一覧と".gitmodules"ファイルを比べたときに一覧にしかないインデックスがあったので、git rmすることで無事に解決しました。
--cachedオプションを付けなければファイルごとサヨナラします。注意しましょう。

git rm --cached hoge

サブモジュールェ・・


追記

実はhogeは拾ってきたZshプラグインで、こいつがコミット時にディレクトリエントリとみなされたことが原因でした。
git cloneしたものをリポジトリに含むときにはちゃんとサブモジュールにしましょう。

参考URL

Git - サブモジュール

Macでトラックパッドのスピードを限界突破したい

最近全然更新してなかった割にはしょぼい記事ですが・・

Macのトラックパッドのスピードが遅いです。
Better Touch Toolを使って速くしているつもりですが、それでも遅いです。

すごいイライラします´д` ;

そこで、defaultsコマンドを使ってみました。

こいつはシステムやアプリの設定値を読み書きできるツワモノです。

$ defaults read -g com.apple.trackpad.scaling
8

現在、BTTで設定できる最大値の8になっているのですが、
あまりにも遅いので倍の16にしました!!

$ defaults write -g com.apple.trackpad.scaling 16

再起動後、めちゃくちゃ速くなりました。

しかし、BTTが勝手に設定値を書き換えるので併用できない・・

でも、BTTが使えないのは困る´д` ;


結局いい解決案は思いつかなかったんですが、defaultsコマンドが便利でいろいろできるので紹介させていただきました。

(この前までBTTで8以上に設定できたような気がするんだがなぁ・・)

BTTを使っていない人はこの手を使ってください。

でも、使ってない人はさっさと入れたほうがいいと思います_φ( ̄ー ̄ )カリカリ

参考URL

Magic trackpad tracking speed changes | Apple Support Communities

ScalaのArrayとApplyについて調べた

初、Scalaの記事です。

Scalaは言語仕様にオマジナイ的なものが少なくて、
勉強していて「なるほど、ここはこうなってんだ〜〜」て思うことが多いです。

さて、そんなScalaの配列ですが、定義方法だけでも結構あります。

val num: Array[Int] = new Array[Int](5);    // 型と要素数で定義
val num             = new Array[Int](5);    // 型推論
val num: Array[Int] = new Array(5);         // 型推論
val num             = new Array(5);         // Array[Nothing]
val num: Array[Int] = new Array[Double](5); // error: type mismatch;

上記はコンストラクタを用いた初期化です。

型と要素数をきっちり定義してもよし、型推論に身を任せてもよしです。
ちなみにブラケットはジェネリックです。
C#あがりの自分にとってはなつかしいものです(-_- )シミジミ


またクラスやオブジェクトにはApplyメソッドという特別なメソッドを定義することができ、定義されていればクラス名(オブジェクト名)のみで呼び出せます。

ArrayオブジェクトではArrayクラスのコンストラクタを呼び出すように実装されているので、 インスタンスの生成は以下のように記述することもできます。

val num: Array[Int] = Array[Int](1, 2, 3, 4, 5);    // 型と初期値で定義
val num             = Array[Int](1, 2, 3, 4, 5);    // 型推論
val num: Array[Int] = Array(1, 2, 3, 4, 5);         // 型推論
val num             = Array(1, 2, 3, 4, 5);         // Array[Int]
val num: Array[Int] = Array[Double](1, 2, 3, 4, 5); // error: type mismatch

Applyメソッドあなどれん・・

こんなかんじでオブジェクトをファクトリーにして同名クラスのインスタンスを生成させていることが多いです。
ちなみに、オブジェクト名とクラス名が同じものをコンパニオンオブジェクトといいます。

また、Applyクラスでは要素の取得・代入するように実装されているので、 要素の取得・代入は以下のように記述することができます。

val num = Array(1, 2, 3, 4, 5);
num(0)       // 1
num.apply(0) // 1
num          // 1, 2, 3, 4, 5
num(0) = 6
num.update(1, 7)
num          // 6, 7, 3, 4, 5

引数がふたつのときはUpdateメソッドを呼び出すように実装されています(こっちはオーバーロード)

うーん、Scalaはおもしろいですねー^_^

補足

実際のコードはこんなかんじです。

Arrayオブジェクト

def apply[A: ClassManifest](xs: A*): Array[A] = {
  val array = new Array[A](xs.length)
  var i = 0
  for (x <- xs.iterator) { array(i) = x; i += 1 }
  array
}

CentOS 6.4にTorch7をインストールしてみた

最近あまり記事書いてないなーと思いながら投稿・・

巷で噂のディープラーニングをやろうと思ってTorch7をインストールしてみました。
CentOS 6.4でやりました。

ディープラーニング系のライブラリは導入が割とメンドクサいらしく日本語の記事が少ないですね。
H2OとかCaffeとかいろいろあるのですが、 H2OはR言語なのがなんとなく嫌で、Caffeは導入で死ぬことが多いっぽかったのでTorch7でいっかーくらいの気持ちでTorch7にしました。


こちらに導入手順があるのですが、僕のCentOS 6.4だと依存ライブラリ解決スクリプトが、
/etc/os-releaseファイルがないという理由でちゃんと動かなかったので、
依存ライブラリの一覧だけ引っ張ってきて、普通にyumコマンド叩いてインストールしました。

sudo yum install -y cmake curl readline-devel ncurses-devel \
  gcc-c++ gcc-gfortran git gnuplot unzip \
  nodejs npm libjpeg-turbo-devel libpng-devel \
  ImageMagick GraphicsMagick-devel fftw-devel \
  sox-devel sox SDL2-devel zeromq3-devel \
  qt-devel qtwebkit-devel sox-plugins-freeworld

あと、OpenBlasとかいうライブラリも必要っぽかったので入れました。
これに関してもスクリプトから引っ張ってきただけです。

cd /tmp/
git clone https://github.com/xianyi/OpenBLAS.git
cd OpenBLAS
if [ $(getconf _NPROCESSORS_ONLN) = 1 ]; then
  make NO_AFFINITY=1 USE_OPENMP=0 USE_THREAD=0
else
  make NO_AFFINITY=1 USE_OPENMP=1
fi
RET=$?;_
if [ $RET -ne 0 ]; then
  echo "Error. OpenBLAS could not be compiled";
  exit $RET;
fi
sudo make install
RET=$?;_
if [ $RET -ne 0 ]; then
  echo "Error. OpenBLAS could not be installed";
  exit $RET;
fi

最後にPythonとipythonを入れたら依存関係は完了です(ipythonのバージョンが古いとダメみたいです)


git clone https://github.com/torch/distro.git ~/torch --recursive
cd ~/torch; ./install.sh

後は上記コマンドを実行して、シェルスクリプトを再読み込みしたらいけました。

vagrant:^_^[~]$ th

  ______             __   |  Torch7
 /_  __/__  ________/ /   |  Scientific computing for Lua.
  / / / _ \/ __/ __/ _ \  |  Type ? for help
 /_/  \___/_/  \__/_//_/  |  https://github.com/torch
                          |  http://torch.ch

th>

う~ん、なんかざっくりですがインストールできました。

近々、社内の勉強会でディープラーニングの発表をしないといけないのでさわっていこうと思います。


追記

あとから確認したんですが、SDL2-develとsox-plugins-freeworldがインストールできていなかった・・

どちらもサウンド系のライブラリみたいなんですが、音声解析とかさせるときに使うのかな??
入れる場合はソースから入れる必要があるかもしれません。

DBIxでN+1問題に直面した・・其の壱

いにしえのコードを修正していたらDBIxでN+1問題に直面している部分を発見した!!
なので解決策をサクッと書こうと思います(/・ω・)/

初めにN+1問題とは

PerlのDBIxのようなORMは最適化のために余分なカラムは読み込まないようになっていることが多いです。
そのため、読み込まれていないカラムを表示しようとすると、
本来のSQL(1)に加えて、各レコードごとに1回のSQL(N)が実行されてしまいます/(^o^)\ナンテコッタイ

つまり、無駄にSQLが発行され、表示に時間がかかります。

Rubyあまり触ったことないんですが、参考にした記事がRubyなのでRubyにもいえる話なんでしょうね・・('A`)


たとえば以下のようなn_plus_one_problemデータベースがあって

authorテーブル

author_id name
1 hoge
2 huga

bookテーブル

book_id title author_id
1 aaaa 1
2 bbbb 1
3 cccc 2
4 dddd 2

authorテーブルとbookテーブルに1対多のリレーションが貼られているとします。


本の情報を表示したいとき、

Controller側は

sub index : Path :Args( 0 ) {
    my ( $self, $c ) = @_;

    my $book_rs = $c->model( 'Master::Book' );
    $c->stash( book_rs => $book_rs );

    return;
}

View側は

<table border="1">
  <tr>
    <th>Book ID</th>
    <th>Book Title</th>
  </tr>
  [% WHILE ( book = book_rs.next ) %]
  <tr>
    <td>[% book.book_id %]</td>
    <td>[% book.title %]</td>
  <tr>
  [% END %]
</table>

とすればいいですね。

すると、クエリログはこうなります(DBIx::QueryLogモジュール)

[2015-06-13T06:57:56] [DBIx::Class::Storage::DBI] [0.000502] SELECT COUNT( * ) FROM book me JOIN author author ON author.author_id = me.author_id at..
[2015-06-13T06:57:56] [DBIx::Class::Storage::DBI] [0.000452] SELECT me.book_id, me.title, me.author_id FROM book me JOIN author author ON author.author_id = me.author_id at..

While文を回すためのカウントSQLは例外として、本体のSQLで1回のクエリが走っているのが分かります。


そこに著者の情報も表示したくなったとき、

Controller側は

sub index : Path :Args( 0 ) {
    my ( $self, $c ) = @_;

    my $book_rs = $c->model( 'Master::Book' )->search( undef, { join => 'author' } );
    $c->stash( book_rs => $book_rs );

    return;
}

View側は

<table border="1">
  <tr>
    <th>Book ID</th>
    <th>Book Title</th>
    <th>Author ID</th>
    <th>Author Name</th>
  </tr>
  [% WHILE ( book = book_rs.next ) %]
  <tr>
    <td>[% book.book_id %]</td>
    <td>[% book.title %]</td>
    <td>[% book.author.author_id %]</td>
    <td>[% book.author.name %]</td>
  <tr>
  [% END %]
</table>

とすればいいですね。

すると、クエリログはこうなります。

[2015-06-13T07:00:28] [DBIx::Class::Storage::DBI] [0.000439] SELECT COUNT( * ) FROM book me JOIN author author ON author.author_id = me.author_id at..
[2015-06-13T07:00:28] [DBIx::Class::Storage::DBI] [0.000330] SELECT me.book_id, me.title, me.author_id FROM book me JOIN author author ON author.author_id = me.author_id at..
[2015-06-13T07:00:28] [DBIx::Class::Storage::DBI] [0.000434] SELECT me.author_id, me.name FROM author me WHERE ( me.author_id = '1' ) at..
[2015-06-13T07:00:28] [DBIx::Class::Storage::DBI] [0.000213] SELECT me.author_id, me.name FROM author me WHERE ( me.author_id = '1' ) at..
[2015-06-13T07:00:28] [DBIx::Class::Storage::DBI] [0.000601] SELECT me.author_id, me.name FROM author me WHERE ( me.author_id = '2' ) at..
[2015-06-13T07:00:28] [DBIx::Class::Storage::DBI] [0.000196] SELECT me.author_id, me.name FROM author me WHERE ( me.author_id = '2' ) at..

わ~~お!!なんかたくさん走ってますね!!

さらに、SELECT句をダンプするとこうなります。(Data::Printerモジュール)

select               [
    [0] "me.book_id",
    [1] "me.title",
    [2] "me.author_id"
]

JOINはしたものの結合先のテーブル情報はロードされていないことが分かります。
しかし、Viewでauthorテーブルの情報を表示しようとしているため、
リレーションを便りにクエリが実行され、テーブル情報を取ってこようとします。

結果、N+1回クエリが実行されてしまいます。


少し古い情報ですが、Catalystのオレンジ本ではN+1問題を解決するために、別テーブルのカラムを参照する際にはJOINではなくPREFETCHを推奨しています。

実際、JOINではなくPREFETCHを用いてみると、

[2015-06-16T06:16:31] [DBIx::Class::Storage::DBI] [0.000633] SELECT COUNT( * ) FROM book me JOIN author author ON author.author_id = me.author_id at..
[2015-06-16T06:16:31] [DBIx::Class::Storage::DBI] [0.000285] SELECT me.book_id, me.title, me.author_id, author.author_id, author.name FROM book me JOIN author author ON author.author_id = me.author_id at..

なんかいけましたね(。-`ω´-)ンー.

select               [
    [0] "me.book_id",
    [1] "me.title",
    [2] "me.author_id",
    [3] "author.author_id",
    [4] "author.name"
]

このようにauthorテーブルの情報がすでに読み込まれていることが分かります。
PREFETCHは結合したテーブルのカラムを自動的に読み込んでくれます。
デメリットとして無駄なデータの読み込みは発生しますが、このほうがミスは少なそうですね。

もしRubyのORMにPREFETCHに相当するものがあるのであれば、それを使えばいいかもしれません(調べてないけどねw)
しかし、DBIxのPREFETCHはクセの強いやつで、そういう訳にもいきません。

少なくとも僕の使ったバージョンでは・・

その辺の話は次回にさせていただく予定です(まとめるのつらい・・)

参考URL

スタブでオリジナルのメソッドを呼び出す

Test::Mock::Guardモジュール便利ですよね。シンプルで使いやすいので、よくこれでメソッドの動作を変えたりしてます。

しかしこの前、かなりシビアな条件でのみ起こり得るバグに遭遇してしまい、
Forkしたりスリープ埋め込んだりして、
なんとか再現しようとしたら見事に再帰りました(¨)( :)(..)(: )ゴ-ロゴロ

#!/usr/bin/env perl

use strict;
use warnings;

use utf8;

use Test::More;
use Test::Mock::Guard 'mock_guard';

run_test() if $ENV{ HARNESS_ACTIVE };

{
  package Test;

  sub add {
    return $_[0] + $_[1];
  }
}

sub run_test {
  my $guard = mock_guard( 'Test', +{ add => sub { print "hoge\n"; sleep 1; shift->add( 1, 2 ); } } ); # オリジナルのTest->addメソッドを期待していた

  is( Test->add( 1, 2 ), 3 );

  done_testing;

  return;
}

これを実行するとこうなる。

akata:^_^[~/perl_test/mock_test]$ prove -v main.pl
main.pl ..
hoge
hoge
.
.

( ゚д゚)ポカーン

モックされたメソッドが永遠に呼び出され続けています。


仕方がないのでTest::MockModuleモジュールを使って、内部からオリジナルのメソッドを呼び出すことで解決出来ました。

sub run_test {
  my $module = Test::MockModule->new( 'Test' );
  $module->mock( 'add', sub { shift; print "hoge\n"; sleep 1; $module->original( 'add' )->( @_ ) } );

  is( Test->add( 1, 2 ), 3 );

  done_testing;

  return;
}
akata:^_^[~/perl_test/mock_test]$ prove -v main.pl
main.pl ..
hoge
ok 1
1..1
ok
All tests successful.
Files=1, Tests=1,  2 wallclock secs ( 0.02 usr  0.00 sys +  0.03 cusr  0.00 csys =  0.05 CPU)
Result: PASS

うーん。いけたけどこれしかないのかな(。-`ω´-)ウーム

ScalaをインストールするためのAnsible-Roleを書いた

ちゃんとgit pullした後にpullされているか確認しましょう。
ひどい目にあいますよ\(^o^)/

はい、今回はScala(ついでにsbtも)です。結論:めっちゃ簡単だった

akatakun/scala · GitHub

Yumリポジトリの追加をget_urlモジュールでやりました。
あと、yumモジュールにURLを指定するとrpmファイルをlocal installできます。
localhostオプションは非推奨らしいのでこっちを使いましょー!!

とまあ、このへんがポイントでしょうか??

短いからソースコードも貼っとこう(これで安心だ)

---
- name: Install dependent repositories
  get_url:
    url=https://bintray.com/sbt/rpm/rpm
    dest=/etc/yum.repos.d/bintray-sbt-rpm.repo
    mode=0644

- name: Install dependent libraries
  yum: name={{ item }} state=present
  with_items:
    - java7
    - http://downloads.typesafe.com/scala/2.11.4/scala-2.11.4.rpm
    - sbt

local installだとyumモジュールの冪等性チェックに時間がかかるような気がします。
ダウンロードとインストールを分けたほうがいいかもしれませんね。


おまけ

git pullされてなかったのはこれと同じっぽい

submoduleの闇にはまりつつあるな・・

参考URL