ひしだまHPの更新履歴。 主にTRPGリプレイの元ネタ集、プログラミング技術メモと自作ソフト、好きなゲームや音楽です。
ひしだまの変更履歴
特定バージョンのCDHのインストール方法
HadoopをLinuxにインストールしようと思ったらCDHを使うのがお手軽だが、普通は最新版がインストールされる。
そうすると、CentOSのデフォルトだと、CDHのマイナーバージョンが(例えば3u2から3u3に)上がると、自動的にCentOS(yum)の各パッケージのアップグレード対象に入ってしまう。
AsakusaFWなど、特定バージョンでのCDH3しかサポートしていない場合、勝手にアップグレードされるのは困ってしまう。バージョンが上がってもたぶん動くとは思うけど。
そこへ、杵渕さんのWEB+DB Press vol.67 訂正 (追記)という記事を教えてもらった。こういう事がやりたいなーと思いつつ、今までこの情報は見たことが無かったので、非常に有り難い。
自分で試したらちょっとエラーが出たけど、基本的にはこれでばっちり!
1から「10のn乗」までの合計を算出するプログラム
Hadoopで1〜100の合計を算出するプログラムを考えてみた訳だが、冷静に考え直すまでもなく、Hadoopの出番じゃないんだよな(爆)
しかし100程度だからいけないのであって、もっと桁数が大きくなればHadoop向きのサイズになるんじゃないか? intの上限(Javaなら32bit)を超える程度だと「longを使えばー?」と言われてしまうので、longは十進数だと18桁まで入るから、19桁の数、つまり「1から10の19乗」までの合計を出すなら、いける!?
JavaならBigIntegerというクラスがあるので、メモリーの許す限りの桁数の整数は扱えるし。
とは言え、1つのMapTaskで10億回(10の9乗)の加算をやるとしても、10の10乗(つまり100億)個のMapTaskが必要となる計算。1000ノードのHadoopクラスターで、1ノード当たり1千万回のタスクを実行か。10コアのCPUだとして、1コア当たり百万タスク。
やっぱり現実的じゃないかもなー(汗)
同じく現実的ではないが、Scalaなら、BigIntegerに相当するBigIntというクラスがあるので、Intでのプログラムと同様にシンプルに書ける。
//Int版
def f0(n: Int) = (1 to math.pow(10, n).toInt).par.sum
//BigInt版
def f1(n: Int) = (BigInt(1) to BigInt("1" + "0"*n)).par.sum
BigIntには初期値としてStringを渡せる。"0"*nは、"0"をn個並べた文字列になるので、10のn乗を作れる。これでばっちり!
と思いきや、toメソッドにはIntの限界があるようで、エラーになってしまった…。
scala> f1(19)
java.lang.IllegalArgumentException: 1 to 10000000000000000000 by 1: seqs cannot contain more than Int.MaxValue elements.
ちなみに、もっと少ない数でもOutOfMemoryになったよ。何故だー?orz
scala> f1(7)
java.lang.OutOfMemoryError: Java heap space
仕方ないので等差数列の和の公式にしてみたら、ちゃんと出来た。
BigIntはIntと同じく*や+が使えるので、コーディングもしやすい。
def f2(n: Int) = { val m = BigInt("1" + "0"*n); (m * (m+1)) / 2 }
しかし最も短くシンプルなプログラムは、これだと思うw
(自分の中では半端な証明しか出来てないけど、たぶん合ってると思う)
def f3(n: Int) = ("5" + "0"*(n-1)) * 2
この件では、Hadoop君はScalaちゃんに敵わないようだ(笑)
1〜100を合算するHadoopプログラム(Counter編)
1〜100を合算するHadoopプログラムの続き。ネタを思い付いたので、Counterを使ったバージョンを作ってみた(笑)
HadoopのCounterは、メトリクス的な情報(処理件数とかバイト数とか)を各タスクで加算していって最終的に表示するもの。
デフォルトで「Map処理の出力レコード数」というカウンターがあるから、各タスクで1〜100件のレコードを出力してやれば、合計の出力レコード数が1〜100の合算値になる!(爆)
まぁそれだとさすがにあまりに酷いのでw、ちゃんとしたカウンターを使うバージョンも作ってみた。
とは言え、そもそもこういった使い方をするのは疑問だけど^^;
ところで、カウンターは各タスクで加算していき、最終的に実行元のジョブで合算されるのだが、途中経過でも(ブラウザーから)カウンター値は見られる。
で、タスクは投機的実行されたり障害で落ちて別タスクに切り替わったりするので、カウンターが重複して加算されることがないか気になったので、試してみた。
まず、タスクを途中でkillしたら、ジョブのカウンター値はその分だけ減った!
そして再実行タスクが2つ実行されたが、片方が正常終了したらその分だけ最終的に合算されるようだった。
つまり、カウンターが重複して合算されることは無いようなので、気にしなくていいみたいだ。
ちなみに、ジョブやタスクを制御するコマンドはhadoop job系。-listでジョブ一覧を表示し、-killでジョブを強制終了させる。で、-kill-taskでタスクを終了させる。
hadoop job -kill-task attempt_201111010257_0926_m_000000_0
カウンターを表示するコマンドもあるんだけど、上手く表示されなかったorz
(常に0が表示される。つまりカウンター名が間違っている?)
hadoop job -counter job_201111010257_0927 example.Sum100Counter2$MyCounter SUM
1〜100を合算するHadoopプログラム
1〜100を合算するC言語のプログラムに続いて、Hadoopでも作ってみた。さすがに変数1個では無理だったが^^;
しかもこんなもんHadoopでやるなという格好の例だけどね!(爆)
数が超大きくなったらHadoopで分散させる意味もあるかもしれないが、そんなのintの範囲に収まらないし^^;
とは言え、SequenceFileを使ったサンプルとしては真面目に役立つかもしれない。かも。
プログラムの構造としては、SeqFileに1〜100までのレコードを出力し、それをReducerで合算するだけ。
SeqFileも大きくなればHDFS上で分散してくれるはずなので、こういう使い方はアリだと思う。
合算部分はWordCountでよく出てくるIntSumReducerそのものが使える。
また、Mapperでは特に何もせずReducerにそのまま値を渡せばよい為、Mapperクラスそのものを指定している。(新API(Hadoop0.20以降)ではMapperクラスは入力をそのまま出力に渡すようになっている。旧APIのIdentityMapperに相当する。なお、HadoopではReduce処理だけしたくても必ずMap処理を経由する必要がある)
Mapperの入力やReducerの出力はTextFileだろうがSeqFileだろうがMapper/Reducerには関係ないので、Mapper/IntSumReducerクラスがWordCountと同様にそのまま使える。
入力をTextFileにしても構わなかったんだけど、そうするとMapperを自作してTextからIntWritableへ変換したりする必要があるので、TextFileにはしなかった。
同様に出力をTextFileにしても良かったんだけど、ファイルのまま中を見るならTextの方が便利だが、値を取得するならSeqFileでもいいかなーと。
ちなみに、jobにセットするOutputFormatをTextOutputFormatに変えるだけで、後は何も変えなくても自動的にTextFileになる。こういった疎結合の仕組みは楽でいいよね〜。
(TextOutputFormatでは、キーや値がNullWritableやnullだと何も出力されない。今回はキーが常にNullWritableなので、値(がtoString()されたもの)だけが出力される)
(今回のプログラムでは、最後の結果読み込み部分をSeqFile用からTextFile用に変えないといけないけど)
変数を1個だけ使って1〜100を合算するC言語プログラム
今日Twitterで、変数を1個だけ使って1から100までの和を作れというお題(Σ100)が流れていた。そのひとつであるtanakhさんのC言語のプログラムを見て、自分もC言語で作ってみた(笑)
C言語ではmain関数からプログラムの実行が始まるのはみんな知ってるけど、(C言語ってのはひどい言語でw、)mainの引数は省略できるんだよね。
自分が知っている範囲では引数は3つあって、main(int argc, char* argv[], char *envp[])。(4つ目もあったような気がするけど、覚えてない…)
argcは実行時引数の個数、argvはその値の配列、envpは環境変数(「変数名=値」という形式)の配列。引数の個数はargcで渡されるのに、envpの個数は渡されない。envp++でポインターを進めていって、*envpがNULLだったら終わり^^;
ちなみにargvも「argv[argc]==NULL」なのが保証されていたはず。
で、自分が作ったΣ100のプログラムは、このうちの第1引数(argc)をカウンターに使おうという鬼畜な構造(爆)
引数を99個渡してやれば、(必ずプログラム名として1個は渡されるので)argcは100になる。さすがに引数を99個書くのは面倒だったので、UNIXのseqを使ったけど^^;
こういう、シンプルで変なプログラムは意外と好きだったりするのだった(笑)
Oracleサイレントインストール
なんか久々にOracleの話^^;
Oracle(DB)をインストールしようと思ったら、GUIのOracle Universal Installer(OUI)を使う。
しかしsshでしかつなげない(GUIが使えない)環境だと、これではインストールできない。
(接続元がウィンドウ環境のUNIXであればウィンドウだけ自分のところに表示することも出来るはずだけど、それはそれで設定が面倒…というか、やったことないので知らない)
という訳で、Oracleのインストーラーにはサイレントモードというのがある。
Oracleのインストールに必要な設定内容を「レスポンスファイル」というファイルに記述しておき、それを使ってインストールする、というもの。
ただ、レスポンスファイルにどういう設定値を書けばいいかという情報が無くて、どうするんじゃい!
と思ったが、対話モードで入力した内容をレスポンスファイルに出力する方法があった。
つまり、一度GUIが使える環境でOUIを使ってレスポンスファイルを生成し、それを実際にインストールする環境に持っていけばいいということだなー。
CDH3擬似分散環境構築
CDH3を使ってCentOSに擬似分散環境を作る方法をメモ。
擬似分散環境は、最初の頃にWindows上に手動で作った覚えがあるけど、CDH3は擬似分散環境も簡単に作れる。
また、意外と色々なLinuxの機能を使っているので、そういう面でも勉強になる。
Play framework2.0を触ってみた
前から「ScalaでWebアプリを作るとしたらどのフレームワークがいいんだろう?」と思ってたんだけど。
名前を聞いたことあるものだけでもこれくらいある^^;
- Play framework
- Lift
- Scalatra (RubyのSinatraライク)
- Unfiltered
- Spray (Akkaベース)
- BlueEyes
(jugyoさん訳の紹介を見ると、SprayとBlueEyesはWebサービス向けのようだから、ちょっと毛色が違うかも?)
本来だったら全部試して比べてみるのがいいんだろうけど、さすがにそこまで出来ないorz
でも試すとしたらPlayとUnfilteredになるのかな?
Liftはyuroyoroさんのサイトで紹介されているけど、当のyuroyoroさんはScalatraを推してる模様w
でもScalatraはRubyのフレームワークに似ているらしく、ScalaでわざわざRubyっぽいものをやりたいとは思わないので、後回しー。
さらに、自分の周りではPlayを採用していく雰囲気なので、Play framework 2.0を試してみた。
まず、インストールが簡単に出来るのがいいね。
雛形(サンプル)が作られるのでざっと追ってみたけど、分かり易いし。(ビューの呼び出し方は独特なはずだが、簡単に想像できる)
HTMLがそのまま書けて、可変部だけキーワードの様にしてScalaの式を埋め込めるのも便利。つまり、普通のHTMLエディターがそのまま使えるから。
(カスタムタグを使う方式だと、普通のHTMLエディターでは役に立たない。tableタグとかはHTMLエディターが無いとやってられんw)
HTMLに埋め込んだキーワード(Scalaの式)や設定ファイルに間違った記述があると、ブラウザーで画面を開くときにエラー箇所が表示されるので分かり易い。
wiki(翻訳ドキュメント)も分かり易くて便利。
なかなか良いんじゃない?(笑)
Hadoop円周率サンプルのバグ
Hadoopのクラスターを作ったら、まず円周率算出のサンプルを実行して動作確認するんだけど。
MapTaskの個数を9にしたら例外が発生するんだよなー。
$ hadoop jar /usr/lib/hadoop/hadoop-examples.jar pi 9 1000
〜
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1603)
at org.apache.hadoop.examples.PiEstimator.estimate(PiEstimator.java:313)
ソースを追うと単純にBigDecimal#divide()メソッドを呼び出しているだけなので、ちょっと試してみたら、9とか7とか3で割ったときに例外が発生する。BigDecimal r = BigDecimal.valueOf(10).divide(BigDecimal.valueOf(9));
これはBigDecimalのバグか?と思ったが、Javadocを見ると、除算結果の小数部が無限になる場合は例外が発生するとある。
つまり、こういう例外が出ることに対処していないhadoop-exampleのバグだろーなー。
nohupとdisown
UNIXでよく出てくるnohupは、端末を閉じても裏でコマンドを動かし続けたい時に使う。
というのは知っていたけれど、SIGHUP(ハングアップシグナル)を無視するという仕組みだったのか^^;
(環境によっては、nohupを付けなくても(SIGHUPが来なくて)そのままバックグラウンドで動き続けるようだ)
それと、標準出力・標準エラーの内容がファイルに出力されるのも便利。
で、nohupを付け忘れて実行開始してしまった場合、disownで後からSIGHUP無視を出来るようになる。
Ctrl+Zやbgと組み合わせれば、バックグラウンドで実行することすら忘れた場合に便利そう(笑)
他に、標準出力・標準エラーへの出力も後からファイルを指定できるといいんだけど、その方法は見つからないなぁ。
参考: nohupをつけ忘れたときは
| « 前ページ |

