谷本 心 in せろ部屋 このページをアンテナに追加 RSSフィード

2016-01-04

[]続けて、JavaNode.jsとGoで外部ライブラリを使わないベンチマークをしてみた。

前回の記事では、DynamoDBを呼び出す処理の簡単なベンチマークを行いました。

http://d.hatena.ne.jp/cero-t/20160101/1451665326


ライブラリを伴ったときの初回起動ではJavaが不利な感じの結果になりましたが、ライブラリを使わなければどのような差が出るのか、改めて確認してみました。


今回のベンチマークは、フィボナッチ数列の38番目を取るというものです。

ソースコードは前回と同じリポジトリに追加しています。

https://github.com/cero-t/lambda-benchmark


結果
回数JavaNode.jsGo
1回目2.9199.284.14
2回目2.6789.3024.141
3回目2.5799.3964.14

時間はいずれも秒。ミリ秒より下の精度は切り捨て。


今回はいずれもメモリ128MBで、同じ性能の環境で実施しました。

なおBilled Durationは、ほぼこの結果の2倍程度(=ウォームアップのための実行分)になっていて、オーバーヘッドはあまり感じられませんでした。


メモリの実使用量は

Java : 12MB

Node.js : 9MB

Go : 10MB

でした。


Python書けないマンなので、Pythonは計測していません。

他のサイトのベンチマークを見る限りは、数十秒から数分ぐらい掛かりそうな気がします。

考察

Javaが一番早く、Goはその1.5倍ぐらい、Node.jsJavaの3倍ぐらい時間が掛かるという結果でした。

ウォームアップをもうちょっと取ればJavaJITコンパイルが効くのかも知れませんが、Lambdaで動かすという性質上、本来ならウォームアップなしで動かしたいぐらいなので、これぐらいの比較で十分かと思います。


ちなみに僕の手元のMacBook Pro(Late 2013 / Core i5 2.4GHz)だと、Javaで0.28秒ぐらい、Goで0.33秒ぐらいでしたから、メモリ128MBのLambdaの性能はその1/10ぐらいということになりますね。


もちろん今回は対象がフィボナッチ数の計算という一つの処理なので、結果的にJavaが良かっただけかも知れません。様々なアルゴリズムで言語のベンチマークを行っているサイトでは、Goの方が有利な結果もいくつか出ています。

https://benchmarksgame.alioth.debian.org/u64q/go.html


ということで、ライブラリのローディングさえなければ、Javaだって悪くないということが分かりました。

まぁ実際、ライブラリを使わないことなんて、ほぼ考えられないですけどね!

2016-01-01

[]AWS LambdaでJavaNode.jsとGoの簡易ベンチマークをしてみた。

あけましておめでとうございます!

現場でいつも締め切りに追われデスマーチ化している皆様方におかれましては、年賀状デスマーチ化していたのではないかと憂慮しておりますが、いかがお過ごしですか。

エンジニアの鑑みたいな僕としましては、年始の挨拶はSNSでサクッと済ませ、年末年始は紅白など見ながらAWS Lambdaのソースコードを書いていました。


ということで、Lambda。

Lambdaを書く時に最初に悩むのは、どの言語を選択するか、なのです。


まず手を出すのは、サクッと書けるNode.js

ただNode.jsの非同期なプログラミングになかなか馴染めず、わからん殺しをされてうんざりしていました。みんなどうしているのよとtwitterで問いかけたところ @ からPromiseを使うべしと教わり、なるほど確かにこれは便利ですナと思いつつも、これが標準で使えないぐらいLambdaのNode.jsが古いところにまたうんざりしました。


では手に馴染んでるJavaを使ってみたらどうかと思ったら、メモリイーターだし、なんとなくパフォーマンスが悪い感じでした。詳しいことは後ほどベンチマーク結果で明らかになるわけですが。


それなら消去法で残ったPythonなのですが、僕マジPython触ったことないレベルであり、これは若者たちに任せることにしているので、Pythonも選択肢から消えて。


本来、Lambdaみたいな用途にはGoが向いているはずなのだけど、いつ使えるようになるんだろうなぁなどと思って調べてみたら、Node.jsからGoを呼び出すというテクを見つけて、こりゃいいやとなりました。

http://blog.0x82.com/2014/11/24/aws-lambda-functions-in-go/


ただGoを呼ぶにしてもNode.jsを経由するためのオーバーヘッドはあるわけで、じゃぁ実際にどれぐらいパフォーマンス影響があるのか、調べてみることにしました。


処理の中身

簡単に試したいだけだったので、数行で済む程度のごく簡単な処理を書いてベンチマークすることにしました。


1. 引数で渡されたJSONパースして、中身を表示する

2. DynamoDBから1件のデータを取得する


当初は1だけだったのですが、あまり性能に差が出なかったので2を追加した感じです。そんな経緯のベンチマークなので、実装も雑ですが、とりあえずソースをGitHubに置いておきました。

https://github.com/cero-t/lambda-benchmark


実行結果
回数Java(1)Java(2)Node.jsGo
1回目218006700900600
2回目13007300800500
3回目19000500200500
4回目10001300200400
5回目500200400500
6回目400100200500
7回目400300400500

時間はいずれもミリ秒のBilled duration。


Java(1) : メモリ192MB。処理中にAmazonDynamoDBClientを初期化

Java(2) : メモリ192MB。処理前に(コンストラクタで)AmazonDynamoDBClientを初期化

Node.js : メモリ128MB

Go : メモリ128MB


メモリの実使用量は

Java : 68MB

Node.js : 29MB

Go : 15MB

でした。


考察など

正味の話、Lambdaで行っている処理などほとんどないので、性能的には大差ないかと思っていたのですが、思ったより特性が出ました。これは処理時間というよりは、関連ライブラリのロード時間な気がしますね。


Javaは最初の数回が20秒、実装を改善しても7秒とかなり遅かったのですが、ウォーミングアップが終わった後は200msぐらいまで早くなりました。呼ばれる頻度が高い処理であれば良いのでしょうけど、たまに呼ばれるような処理では、この特性はネックになる気がします。


ちなみにJavaだけは192MBで計測しましたが、最初にメモリ128MBで試したところ、30秒ぐらいで処理が強制的に中断されたり、60秒でタイムアウトするなど、計測になりませんでした。こういう所を見ても、Javaを使う時にはメモリを多め(CPU性能はメモリと比例して向上)にしなくてはいけない感じでした。


Node.jsも少しはウォーミングアップで性能が変わりますが、最初から性能は良い感じです。


Goを使った場合は性能が安定しており、ウォーミングアップしても性能が変わりません。メモリ使用量が少ないのも良いですね。Node.jsから呼び出す際のオーバーヘッドがあるせいか、性能的にはウォーミングアップ後のJavaNode.jsに一歩劣る感じでした。


なお、Pythonの実装をしてくれる人がいらっしゃれば、プルリクしていただければ、データを追加したいと思います。


結論

繰り返しになりますが、雑なベンチマークなので特性の一部しか掴んでないと思います。

そもそもJavaだけメモリが1.5倍になっている(=CPU性能も1.5倍になっている)ので公平ではないですし。


ただ「たまに行う処理」を「少ないリソース」で行うという観点では、Goで実装するのが良さそうです。GoはそのうちLambdaでも正式サポートされそうですしね。

きちんとしたベンチマークは、また別の機会にじっくり行ってみたいと思います。


ということで、Goを使う大義名分ができたベンチマークでした!

See you!

2014-12-15

[][]SqlTemplateっていうJdbcTemplateのラッパーを作ってみました。

「SQLが書きたいんや!」という想いのもと、

Spring Bootと組み合わせて簡単に使える、

JdbcTemplateのラッパーライブラリを勢いで作ってみました。


GitHubに置いています。

https://github.com/cero-t/sqltemplate


JdbcTemplate / NamedParameterJdbcTemplateをベースにして、

 1. SQLファイルが使えること

 2. Date and Time APIに対応すること

 3. publicフィールドに対応すること

 4. APIが今風であること

の4つを目的にして作りました。


それならMirageでいいんじゃね? という想いは消えませんが、

Spring標準機能のみを使うことによる、政治的な使いやすさを取りました。


作りましたって言っても、ただのラッパーですので

ソースコードはすっごく小さくて、空行とコメントを入れても600行ぐらいしかありません。

ジェバンニでなくとも一晩でやってくれるぐらいのサイズです。


利用イメージ

exampleのプロジェクトも作っておきました。

https://github.com/cero-t/sqltemplate/tree/master/sqltemplate-example


使う側のソースコードは、こんな感じになります。

@Component
public class SampleProcess {
    @Autowired
    SqlTemplate query;

    public void process() {
        List<Emp> emps = query.forList("sql/selectAll.sql", Emp.class);
        emps.forEach(e -> System.out.println(e.ename));

        Emp emp = query.forObject("sql/selectByEmpno.sql", Emp.class, 7839);
        System.out.println(emp.ename);

        Map<String, Object> condition = new HashMap<>();
        condition.put("deptno", 30);
        condition.put("job", "SALESMAN");
        emps = query.forList("sql/selectByCondition.sql", Emp.class, condition);
        emps.forEach(e -> System.out.println(e.ename));
    }
}

forObjectで1件検索、forListで複数件検索。

第一引数がSQLファイル名で、第二引数が戻り値の型、

第三引数以降がSQLにバインドするパラメータです。


ちなみにIntelliJを使っていると、ファイル名にカーソルをあわせて

Ctrl (Command) + クリックでSQLファイルを開けるのが嬉しいですね。


SQLファイルは、こんな感じになります。

select
    *
from
    emp
inner join dept
    on emp.deptno = dept.deptno
where
    dept.deptno = :deptno
    and emp.job = :job

第三引数に指定したMapやEntityの値を、SQLのパラメータとしてバインドします。

内部的にはNamedParameterJdbcTemplateに処理を委譲しているだけです。


名前を指定せずに ? を使うこともできます。

select
    *
from
    emp
where
    empno = ?

第三引数以降に指定した任意の数の基本型(String、Date、Number)をバインドします。

こちらは内部的にJdbcTemplateに委譲しているだけです。


使い方

使うための設定は一つだけ。

@Configurationアノテーションをつけたクラスに

SqlTemplateを返すメソッドを作り、@Beanアノテーションをつけます。

@Bean
SqlTemplate sqlTemplate(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
    return new SqlTemplate(jdbcTemplate, namedParameterJdbcTemplate);
}

この初期化の仕方は @ がpullリクエストで教えてくれました。

ありがとう!


ちなみにSqlTemplateのコンストラクタの第三引数には、

SQLファイルを読み込む際のテンプレートエンジンを指定することができます。

たとえばここで2-way SQLパーサーを指定すれば、

2-way SQLにも対応できるというスンポーです。


現時点でもFreeMarkerを使うことができるようにしているんですが、

一度も動作確認してないので、動くかどうか分かりません。てへぺろ。


いまあるAPI一覧

用意したメソッドの一覧は、以下になります。

<T> T forObject(String fileName, Class<T> clazz, Object... args)
<T> T forObject(String fileName, Class<T> clazz, Map<String, Object> params)
<T> T forObject(String fileName, Class<T> clazz, Object entity)

<T> List<T> forList(String fileName, Class<T> clazz, Object... args)
<T> List<T> forList(String fileName, Class<T> clazz, Map<String, Object> params)
<T> List<T> forList(String fileName, Class<T> clazz, Object entity)

int update(String fileName, Map<String, Object> params)
int update(String fileName, Object entity)
int update(String fileName, Object... args)

戻り値をMapにする「forMap」とか

実案件では欠かせない「batchUpdate」は、まだ作っていません。

委譲するだけなので、作っちゃえばいいんですけどね。


制限事項的なやつ

とりあえずコンセプト実証した程度なので、色々できません。

 1. 上に書いた通り、forMapとbatchUpdateがありません。

 2. JSR-310を使っているので、Java8でしか動きません。

 3. publicフィールドのないgetter/setterベースのJavaBeansは使えません。

 4. パッケージ名が変です。

 5. mvnリポジトリに置いてないです。

 6. README.mdちゃんと書け。


ひとまずは「こんなコンセプトでサクッとできたよ! 」っていう位置づけです。

ご自由に参考にしてください!

2014-12-14

[][]MyBatisをやめて、JdbcTemplateを使うわ。

以前のエントリーで、DBアクセスにはMyBatisを選んだと書きました。

http://d.hatena.ne.jp/cero-t/20141212/1418339302


そしたら渋谷JavaのLTで @ さんに拾ってもらっちゃいました。

http://www.slideshare.net/yukung/j-ooq-shibuyajava9


そんなこともあってMyBatisイチオシなエンジニアに思われたかも知れませんが、

ごめんなさい、

あの記事はあくまでも伏線で、僕、もうMyBatis使ってないんです!


MyBatisを使って半月ぐらいして、

どうにも我慢できなくないことが出てきました。


1. Spring Bootとの連携がイマイチ

Spring BootでMyBatisを使おうとすると、

前回のエントリーで書いた通り、ちょっと設定ファイルが必要になったり、

その設定ファイルの読み込みに失敗して、謎の無限ループが起きることがあるなど、

やや不可解なことがあります。


設定ファイルの問題というなら、設定ファイルを使わず、

ソースコードだけで設定できれば良いのですが、その方法も、結局よく分かりませんでした。


まぁMyBatisAutoConfigurationとかができてからが本番というか、

ないなら自分で作るゾ、ぐらいの勢いで挑む必要があるように思いました。


2. XMLにSQLを書くと、インデントががが。

じゃぁMyBatisAutoConfigurationを作れば良いわけですが、

そういう気になれなかったのは、やっぱり、

XMLファイルにSQLを書くのが嫌だったから、でした。


だって、標準的なフォーマッタでフォーマットした瞬間、

インデントが全部消えるじゃないですか。


自動フォーマットをこよなく愛する僕としては、

フォーマットする手段がないというのは、いただけませんでした。


3. そこでJdbcTemplateですよ

じゃぁ何を使ってるのか?

で、結局、Springに標準でついているJdbcTemplateを使っています。


無設定で使えて、値のバインドは適切にできて、

NamedParameterJdbcTemplateならSQLに変数が使えて、

Entityにはアノテーションとか付けなくて良くて、

余計な機能はなくて、シンプルに使える感じでした。


ただ、もちろん、JdbcTemplateも欠点だらけです。

 ・publicフィールドに対応していない

 ・Java8のLocalDateなど、Date and Time API (JSR-310) に対応していない

 ・RowMapperを求めるAPIになっているなど、APIに古くさいものが混ざっている

 ・そもそも、SQLファイルを読み込む機能なんてない!


なので、これらを補うような

独自ラッパーでラッピングして使うことにしました。


詳しい紹介はまた改めて書くとして、モノはここに置いています。

https://github.com/cero-t/sqltemplate


独自ライブラリではなく、Spring標準のJdbcTemplateを

ちょっとだけラッピングして使っているだけなので、

政治的な意味で使いやすいかな、と思っています。

2013-09-30

[]俺様とJavaOne 2013(中編)

JavaOne 3日目、自分のセッション当日は

やっぱり直前まで資料準備&練習でバタバタしていました。

この性格、死ぬまで治らない予感!


Day 3 : 解析ツールのセッションは人気

3日目、僕のセッションは夜7時半からなので、

朝イチにあった面白そうなセッションに参加していました。


[CON5092] Diagnosing Your Application on the JVM

元BEAのStaffan Larsenのセッション。

朝イチにも関わらず、満席になる人気セッションでした。


内容は、診断・解析ツールについて、デモを交えて次々と紹介するセッションで

主に7u40から使えるようになった(7u4から使えてたものもあるけど)

「jcmd」(旧jrcmd)を中心に紹介されていました。


ちょっと列挙しますと・・・

jps : Javaプロセスの一覧を列挙する

jcmd : 引数なしならjpsと同じ

jcmd <pid> VM.uptime : Javaプロセスの起動後の経過時間

jcmd <pid> Thread.print : スレッドダンプ。jstackと同じ。

jcmd <pid> GC.heap_dump : ヒープダンプ。jmapと同じ。

jcmd -gcnew <pid> 1s : 毎秒のGC領域のサイズを見る

jcmd <pid> PerfCounter.print : JVM内部で保持している様々なカウンタを取得

などなど。


自分的に衝撃だった事と言えば、jcmdコマンドの引数で pid に 0 を指定すると

全てのJavaプロセスの情報をまとめて取れる、というところ。

実際に使う機会があるかどうかは分かりませんが、良い事を知った感がありますね(笑


またセッション後半は、このような解析ツールをリモートから実行するために、

jstatdやJMX Remoteを利用するという話や、その裏側の仕組みが説明されました。


正直、他の作業をしながら聞いていたので、かなり聞き逃してしまったのですが

リモートから診断・解析をする時に、何ができるか・できないかを判断するための

背景となる知識が得られる良い内容でした。まさにJavaOneらしい内容だったと言えます。


特に解析する機会が多いとか、開発ツールを作る立場であるとか、

そういう人は、このセッションの資料をきちんと読むべきだと思いますね、

っていうか、私も、きちんと読み直します!


ところで、セッションの後に

こんな風にスピーカーのStaffanさんに、お礼なのか挑発なのか分からないツイートをして

私のセッションに来て頂きました。我ながら、強引なことをしたもんです。


そんなわけで、夜には自分のセッションがあったわけですが

それについては、前後の話も含めて、またきちんと別エントリとして投稿します。


Day 4 - Lambda、Lambda、JFR

おはよう世界。

自分のセッションが終わった開放感からか、倒れ込むように寝てしまい

これは昼まで寝るかなと思ったら、意外と5時間睡眠ぐらいで目が覚めてしまい

時差ボケの威力を実感した早朝でした。


そんなわけで、朝から元気にセッションに参加します。

そう、僕のJavaOne参加はここから始まったわけです。


[CON2055] Programming with Lambda Expressions in Java

Agile Developer, Inc.の社長、Venkat Subramaniamによる

軽妙でウィットに溢れたLambdaのセッションでした。


内容的には、外部イテレーターから内部イテレーターの書き方の移り変わり、

Lambdaの文法やstreamの使い方や効果などを紹介するという

比較的、初級者向けのセッションなのですが、その語り口調が面白すぎて

本当に笑いの絶えないセッションでした。


直接的な表現よりも、間接的な表現を軽妙に語る事で面白さを増す感じでしたね、たとえば

  • 汚い → とても子供に見せられない。しっ見ちゃいけません!
  • 危険 → 何をやろうとしているんだ、家に帰って考え直せ!
  • 素敵 → これは食欲をそそる!

などなど。

って私はJavaOneに来て何を学んでるんですかね。


もう少し実用的なところをフィードバックすると、

やはりLambda時代にはAPIデザインが少し変わるということでしょうか。


たとえば自分で比較するユーティリティメソッドを書く際には、

isPriceLessThan(500, value) と書けるようなAPIを提供するのではなく、

isPriceLessThan(500).test(value) と書けるようなAPIを提供することで

Lambda式として利用できるようにしていました。


ちょっとこの辺り、自分でも消化しきれていないので

日本に帰ったら資料を見ながら復習しようと思います。


ってよく考えたら、このセッション、

テキストエディタだけで話してたから、資料ないんだった (^^;;


[CON7942] Java 8 Streams: Lambda in Top Gear

続けてのLambdaセッション。

Paul Sandozと、Lambdaの神Brian Goetzのセッションです。


streamのAPIは、集計処理などにおいて、

うまくparallel化ができるもの、できないものがあったり、

処理を途中で中断しても良いもの、全ての要素を走査するものがあるなど、

APIは、いくつかのカテゴリで「分類」することができます。


この分類次第で、parallel化した時のパフォーマンスなども変わってくるため

streamを使う際には、この分類をきちんと押さえておかなければいけない、

ということが説明されていました。ちょっと自分にはなかった視点でした。


この辺りは、資料をダウンロードして学び直す必要があるので、

ボロが出ないうちに、説明をこの辺で切り上げましょう (^^;


[CON5091] Java Flight Recorder Behind the Scenes

3日の朝イチに解析ツールの紹介していた、Staffan Larsenのセッション。

前半こそFlight Recorderの紹介だったのですが、

後半はFlight Recorderの設計の話が展開され、かなり興味深かったですね。

そんな後半の話だけピックアップして紹介します。


1. Thread buffers

Flight Recorderが取得した情報は、スレッドローカルのThread buffersに貯めてから、

共有のGlobal buffersに書き出します。

こうすることで、Global buffersへの書き込みが衝突することを抑えています。

この辺りは、Flight Recorderがメイン処理に影響を与えないようにするために

欠かせない、いわば当たり前の設計でしょう。


2. Flight Recorderは永久には情報を取り続けない。

メモリリークへの対策として、情報は一定期間かサイズごとに消すか上書きしています。

これも当たり前のことですが、私は過去にちょっとやらかした事があります (^^;


3. Flight Recorderは、アプリケーションのクラスやオブジェクトへの参照を持たない。

これも、メモリリークを防ぐうえで不可欠のポリシーです。


4. クラス名はIDに変換する。

クラス名(文字列)を、int程度の数値に変換することで、

ファイルやメモリの空間効率を向上させます。

また、そのクラス名と数値のマップをFlight Recorderの出力ファイルに

持たせておくことで、互換性や移植性にも配慮しています。

こういう細かいところも、きちんと工夫しているんですね。


5. クラス一覧自体も定期的にリセットする。

クラス一覧がメモリリークの原因にならないよう、

一定期間ごとにファイルに出力して、クラス一覧をクリアしてしまいます。

(このタイミングを「チェックポイント」と読んでいました)

最後にクラス一覧をマージするかどうかは、ちょっと分かりませんでした。

マージしないと重複が出てしまって、ファイル効率がよくない気がします。


6. スタックトレースのpoolを作る。

同じスタックトレースが何度も表れることが多いため、

スタックトレースのプールを作っておいて、(全く)同じスタックトレースが

発生した場合には、前のスタックトレースへの参照を使うだけにします。

なるほど、勉強になります。


というような、ENdoSnipeの開発者的にありがたい情報がたくさんありました。

もちろんアプリケーション開発をするうえでも、このようなメモリやデータの

効率化の仕組みを「発想」することは、とても大切だと思います。

まだまだやるべき事があるのだなと、改めて思い知らされた感じです。


[CON2959] Modular JavaScript

Luminis Technologies社のSander Mak、Paul Bakkerのセッション。

なんかJavaとの連携もありそうな感じのアジェンダが提示されていたんですが

実際には、JavaScriptのライブラリやフレームワークを使ったときの

packageやclassの可視性なんかを、延々延々とJavaScriptのソースで説明するセッションで、

Javaコードは全く出てきませんでした。


ここJavaOneやぞ!


4日目終わり

この後の時間帯に、kotlinのセッションがあったり、

あの #てらだよしお さんのJavaEEのセッションがあったのですが、

どうしても眠かったため、ホテルに戻って休んでいました。


聞くところによると、kotlinのセッションは10人いなかったそうです。

kotlinの過疎感ハンパない!(><)


そして寺田さんのセッションは「質問はTwitterでお願い」と言っていたにも関わらず、

バンバン質問が出て、大変だったそうです(そして、きちんと回答したそうです!)

そりゃOracleのエンジニアがJavaEEについて話したら、質問出るって!


ちなみに4日目の夜には、トレジャーアイランドで

Maroon5というバンドのライブなどあったのですが、

上にも書いた通り、ホテルに戻ってお休みしていました。


Folder5が来るんだったら、無理してでも行ったと思うんですけどね。

#行かねーよ。


そんなわけで、JavaOneも後半戦に差し掛かってきました。