blog - yuokada/presto-sample-udf GitHub Wiki

Prestoでhivemallのtokenize_ja()なものを作る

自分が扱ってるPrestoクラスターのリソースが余ってたのでhivemallにあるtokenize_jaライクなUDFを作ってみた話。

Tokenizer · myui/hivemall Wiki

hivemallのtokenize_jaとは

Javaの形態素解析ライブラリであるkuromoji(https://github.com/atilika/kuromoji)を使ったhiveのUDFで日本語文を引数にとって形態素解析した結果を返す関数です。 実行結果は下のようになります。

select tokenize_ja("kuromojiを使った分かち書きのテストです。第二引数にはnormal/search/extendedを指定できます。デフォルトではnormalモードです。");
["kuromoji","使う","分かち書き","テスト","第","二","引数","normal","search","extended","指定","デフォルト","normal","モード"]

see: https://hivemall.incubator.apache.org/userguide/misc/tokenizer.html

Prestoで実装してみる

辞書、モードなどを考慮せずにおおざっぱに実装するとこんな感じになります。

      @ScalarFunction("kuromoji_tokenize")
      @LiteralParameters("x")
      @SqlType("array<varchar(x)>")
      public static Block kuromojiTokenize(@SqlNullable @SqlType("varchar(x)") Slice sentence)
      {
         String input = sentence.toStringUtf8();
         if (sentence == null || input.isEmpty()) {
             return VARCHAR.createBlockBuilder(new BlockBuilderStatus(), 0).build();;
         }

         Tokenizer tokenizer = new Tokenizer();
         List<Token> tokens = tokenizer.tokenize(input);

         BlockBuilder blockBuilder = VARCHAR.createBlockBuilder(new BlockBuilderStatus(), 32);
         for (Token token : tokens) {
             VARCHAR.writeSlice(blockBuilder, utf8Slice(token.getSurface()));
         }
         return blockBuilder.build();
     }

ただ、このコードには1つ欠点がありもの凄く遅いのです。 関数が呼び出されるたびにTokenizerインスタンスが生成されるのですがインスタンスの生成に数秒程度の時間がかかります。 100行ぐらいのレコードを処理するのに2~3分ぐらいかかります。これでは全く実用性がありません。

(Hiveについて詳しくないのでまた聞きですが) hiveのUDFの実装をそのままPrestoに持ってくると上に上げた例のようにパフォーマンスが非常に悪いUDFが出来上がります。

今回はパフォーマンスを上げるためにGoogle guavaを使ってTokenizerインスタンスをキャッシュして使いまわすことでパフォーマンスを向上させることにしました。 実装はこちら

今回の実装ではユーザー辞書をサポートしているので辞書と分かち書きのモードの2つを引数をキーにしてインスタンスをキャッシュする実装としています。 キャッシュのサイズは32とかなり小さめですが自分の用途的にこれぐらいキャッシュしてくれれば問題なかったのでこの値にセットしています。

UDFは設定ファイルで調整出来ないのでキャッシュサイズが調整出来るようになれば変更出来るようにしても良いかなという感じです。

⚠️ **GitHub.com Fallback** ⚠️