24 posts tagged “java”
Jythonの日本語処理がよくわからないについて。
UTF-8環境と仮定して、以下のようにすれば出来ます。
(コマンドプロンプトならCP932とかにすればいいんだけど、こまったことにjythonではcodecsに"cp932"がないのでencodeでSJISバイト列に出来ません。なのでJavaのAPIで出力するのしか出来なかった)
なぜかといえば、
>>> from java.lang import String, System
>>> from com.ibm.icu.text import Transliterator
>>> print Transliterator.getInstance("Fullwidth-Halfwidth").transliterate(String(u"テスト", "UTF-8")).encode("utf-8")
テスト
>>> System.out.println(Transliterator.getInstance("Fullwidth-Halfwidth").transliterate(String(u"テスト", "UTF-8")))
テスト
>>> sys.getdefaultencoding()
'ascii'
>>> System.getProperty("file.encoding")
'UTF-8'だからじゃないかな。
例えば、こんなのでも分かる。>>> "テスト".decode("utf-8")
u'\uff83\uff7d\uff84'
>>> print "テスト".decode("utf-8")
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
その場で作ってすぐ実行するスレッドをThreadを継承した匿名クラスで実装する。このスレッドをdaemon化するのに、次のようなやり方ではIllegalThreadStateExceptionが発生してしまう。startしたらdaemon化出来ないため。
まあ変数に一度入れてsetDaemonしてやればいいのだが、それでは負けた気がするのでちょっと考えたら、initializer使えばいいのに気付いた。
new Thread() {
@Override
public void run() {
setDaemon(true);
System.out.println("hello");
}
}.start();
new Thread() {
{
setDaemon(true);
}
@Override
public void run() {
System.out.println("hello");
}
}.start();
longの下位32bitだけ取り出そうと思って、32bitのマスクを定数で指定したらはまった。
32bitだからintでいいだろう→longと演算する時点で拡張されるから駄目。
さらに、Lつけないとlongにならないのをすっかり忘れてた。
こんなかんじ
出力
long v = -1;
System.out.println(Long.toHexString(v&0xffffffff));
System.out.println(Long.toHexString(v&(long)0xffffffff));
System.out.println(Long.toHexString(v&0xffffffffL));
ffffffffffffffff
ffffffffffffffff
ffffffff
テストなので、java -cp classがあるディレクトリ:hadoop-0.16.3-core.jar hogeとかやって動かしていた。どうせヒストリー使うだけなので。
そのあとにjarを作って、java -cp myapp.jar:hadoop-0.16.3-core.jar hogeで動かすようにした。もともとantでjarは作ってたんだけど…
テストしていたのは、HDFSにファイルを作ったりするプログラムなので、結果をhadoop fs -lsrとかして動作を確認していた。
それが、jarを作って動かすようにしてから、HDFS上に全くファイルが作られなくなった。
プログラム的にはエラーが起こるわけでもないし、ファイル作った直後に読み出すコードを入れてみると正しく読める。なので、別のnamenodeに繋いでしまっているのではないか?と焦る。(2系統あるので)
ここで設定ファイルを確認しようとして気付いた。「classのディレクトリにhadoop-site.xml入れてたんだった…」
デフォルトでは、ローカルファイルシステム上で動くので、hadoop fs -lsrでは確認できなかったという落ちでした。ローカルファイルシステム上にちゃんとファイルが出来ていました。何度も動かしたので大量に。
ということで、使っている設定ファイルとか繋いでいるnamenodeに気をつけないと言う教訓が得られました。
気付いたらlucene java 2.3.0が出てた。MLの流量が多くて後で読もうと思ってたら…。dev-javaでRC3のあとアナウンスの準備してたから、もうすぐだとは思ってたけど。いつのまにかhadoopがトップに上がってた。
大きいインデックスでマージ時間が悩ましいとか、indexingのスピードを上げたい人は、試しに使ってみると良いと思う。
あと、IndexReader.reopenとかは便利。
2.4で期待してるもの。
Lucene-1120 Use bulk-byte-copy when merging term vectors
書いてあるとおり、フォーマットが変わるのでしばらく実験で使ってみよう。私の所でも、TermVectorsのファイルはサイズが大きいので効果がありそう。
Lucene-1121 Use nio.transferTo when copying large blocks of bytes
nioを使うので微妙に盛り上がってる。書いてあるとおりCFSを作るのは速くなりそう(CFSは複数のファイルをまとめただけで、まず元ネタを作ってからCFSにまとめるので)。
ただ、CFSを使うと一時的にディスク使用量が2倍(CFSと元ネタ)になるし、IO負荷も高いので今は避けてる(そのせいで、大量のインデックスをマージするときに、ファイル開きすぎではまったりする)
よくみたら、next(Token result)ってAPIが追加されてたのね。
これ使えば問題なかった。
訂正: ちゃんと使えば問題ありません。
何にも考えずに、Tokenizerで1つのTokenだけ保持して使い回すようにしたら、
今まで検索できていたものが検索できなくなった。
queryをrewrite&toStringしてやっと気付いた。気付くまで時間がかかってしまった。
TokenをつかいまわすTokenizer/AnalyzerをQueryParserで使うと、
Analyze結果をPhraseQueryとかにするために、TokenをVectorに入れて保持してるから、
最終的に取得できるQueryでつかうTermが全部同じになってしまう。
気付けば当たり前なんだけど。
Indexの構築は問題なかった。
いつ出るのかわからないけど、かなり性能が改善しているようなので見てみた。
うれしい改善
- Fieldつかいまわし(LUCENE-963)。これでDocumentも使い回せる。
- Tokenつかいまわし(LUCENE-969)。Tokenizer内でTokenを使い回す。
- IndexReader.reopen()(LUCENE-743)。Searcherを作り直さないで良いなら、かなり便利になる気がする。
Field使い回し
フィールドの数だけFiledのインスタンスを作っておいて、Documentにaddしておく。
あとは、実際にインデックスにつっこみたい文書を、作っておいたフィールドにそれぞれ設定して、
IndexWriterにdocをaddすればいい。docに対してaddするのは最初だけで良い。
final Document doc = new Document();
final Field url = new Field("url", "", Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO);
final Field title = new Field("title", "", Field.Store.NO, Field.Index.TOKENIZED, Field.TermVector.NO);
final Field desc = new Field("desc", "", Field.Store.NO, Field.Index.TOKENIZED, Field.TermVector.NO);
doc.add(url); doc.add(title); doc.add(desc);
for (Content c: clist) {
url.setValue(c.url);
title.setValue(c.title);
desc.setValue(c.desc);
indexWriter.add(doc);
}
Token使い回し
Tokenをコンストラクタで作っておいて、nextが呼ばれるたびにTokenに設定されている値を変えるだけ。Termを書き換えるのに、termBufferを書き換えた方が良いようなのでそうしてみる。
まだベンチマークしてないから、どれだけ効果あるのか不明。
final int len = term.length();
char[] buf = token.termBuffer();
if (buf.length < len) {
buf = token.resizeTermBuffer(len);
}
term.getChars(0, len, buf, 0);
token.setTermLength(len);
今作ってるシステムで、DBにデータを入れてすぐに、そのデータを取得する場面が存在する。
MySQLのレプリケーションは非同期なので、Connection.setReadOnly(false)で、
マスターに強制的につないで、データをとるようにしていた。
c3p0でStatementCacheを使うと、デッドロックするというのが、私のところでも起きていたので、
maxStatementsを0にしていたが、試しに10にしてみたら、デッドロックにはならなかったものの、
うまくデータとれない事象が発生するようになった。検索したら、似たことで困ってる人がいた。
私が困ってるのも同じで、setReadOnly(false)してあるコネクションでのトランザクション内。
ReplicationDriverのコネクションは、表向きは1コネクションでも、
内部で切り替えているから、何にも言わないと、こういう事態が発生しそうなのはわかる。
でも、直前にsetReadOnly(false)したコネクションで、しかも1トランザクション内なんだけどな…
c3p0側のStatementキャッシュ周りを調べてみるかな。
この処理の前に、同じStatementをsetReadOnly(true)で実行しているので、
プールからコネクションを取り出したときに、同じコネクションが回ってくると、キャッシュされてるのが使われそう。
キャッシュを使うときに、コネクションをどう処理してるかを見たら、なんかわかるかな。
適当に書いてるけど。
まあ、プール側は単純に1コネクションとしか思ってないだろうから、なんかしら不整合はありそう。
dbcpでもPrepareStatmentのキャッシュが問題だったし。
キャッシュをしないようにするしか解決策がないんだとすると、結局無駄な作業だったのかな…