9/29 追記
本件に関して,一応の解決策を以下のエントリーにて記述しましたのでご参照ください.
http://blog.broomie.net/index.cgi?id=38
shunyaです.最近Crystal Keyのアルバムを聴いていてCrystal Keyが改めて良いと思いました.切なくて甘酸っぱいです.
Thriftのパフォーマンスについて
さて,前回のエントリー(Thriftが便利すぎる)では,僕が感じたThriftの便利さを熱弁する内容となっておりました.始めて使った感想としてはすごく便利!だったわけですが,実際に何かしらのサービスに導入するためにはThriftのパフォーマンスをチェックしなければなりません.
で,今回は簡単なThriftのベンチをとってみることにしました.僕的に気になるのが,前回のエントリーでも書いたようにRESTととの違いです.つまりRESTではCGIを使ってHTTPで通信するので,比較的高速に処理できます.それに対して,Thriftでは独自に定義されたRPCで通信します.とても便利なのはいいのですが,この独自のプロトコルがどれほどのオーバーベッドがあって,スピードに影響を及ぼすのかはあらかじめ既知にしておかなければなりません.そこで,今回はRESTとThriftで前回のエントリーで作成した,「足し算,引き算」のプログラムのベンチをとってみることにしました.以下の図に概要を示します.
*Thriftのテスト方法
図の左側がThriftの構成となります.サーバはC++で生成して,足し算,引き算のプログラムをそのC++のプログラムの中に書きました.クライアントはThriftで生成したperlライブラリを使いました.このプログラムの内容は前回のエントリーにも書いてあります
が,要所部分だけ引用しておきます.
今回使用したプログラムをアップしておきましたので,ご興味がある方は使ってみてください.
TinyCalc.tar.gz
コンパイルの仕方,テストプログラムについては同封したREADMEファイルに記述しました.
thriftファイル
#!/usr/bin/thrift service TinyCalc { double sum(1: double param1, 2: double param2) double subtract(1:double param1, 2:double param2) }
サーバープログラムに書いた関数
double sum(const double param1, const double param2) { return param1 + param2; } double subtract(const double param1, const double param2) { return param1 - param2; }
サーバプログラムをコンパイル
g++ -Wall -O2 -g TinyCalc_server.cpp TinyCalc.cpp -o TinyCal_server -lthrift -I/usr/local/include/thrift
クライアントは上記のようにperlを使いました.以下の条件でテストをします.
#!/usr/bin/env perl use strict; use warnings; use String::Random; use lib './gen-perl'; use Thrift; use Thrift::BinaryProtocol; use Thrift::Socket; use TinyCalc; use constant TEST_NUM => 1000; my $transport = Thrift::Socket->new('localhost', 9090); my $client = TinyCalcClient->new( Thrift::BinaryProtocol->new($transport) ); $transport->open(); my $rnd = String::Random->new(); for(my $i = 0; $i < TEST_NUM; $i++){ my $arg1 = $rnd->randregex('[1-9]{4}'); my $arg2 = $rnd->randregex('[1-9]{4}'); my $sum = $client->sum($arg1, $arg2); my $subst = $client->subtract($arg1, $arg2); print "$sum\t$subst\n"; } $transport->close();
RESTのテスト方法
RESTのテスト方法は先ほどの概要図の右側になります.RESTの場合は,サーバはfcgiをC++から呼んで実装しています.そのプログラムの中にThriftのサーバと同じようにsumとsubtractの関数を実装しています.
そしてクライアントはperlスクリプトを書いてLWPを使ってサーバにPOSTで問い合わせるようにしました.テスト内容は一緒で,1000回,ランダムに生成された4桁の数字で足し算,引き算の計算の要求をします.このプログラムは明日アップしますので,少々お待ちください.といっても説明した通りの内容です.
テスト結果
これまで述べてきた内容でスピードテストをしました.で,テストの結果なのですが実は自信がありません.結論から言ってしまうとThriftがすごく遅いんですね.さすがにここまでは遅くならないだろー!ってぐらい遅い結果となってしまいました.だから,この結果には以下の可能性が潜んでいます.
上記の可能性がある上で今回出た結果をあまり信用せずに眺めていただけたら,と思います.では,計測した結果を以下の図にしめします.計測方法は単純にperlクライアントをtimeコマンドを使って時間を計測し,Thrift,RESTそれぞれ5回ずつ計測して平均を
とりました.
なんと,RESTが9.89 secでThriftが81.41 secという結果になりました.実に約8倍もThriftが遅い計算となります.RESTのほうは約100QPSとなっていて妥当な値が出ています.それに対してThriftは約12QPSとちょっとありえない結果となてしまいました.
もしかしたら,perlクライアントに問題があるのかな?と思ってpythonクライアントも書いてperlと同じような条件でテストしてみました.TinyCalc.tar.gzにも入れておいたのですが,一応ここにpythonのテストプログラムを示しておきます.
#!/usr/bin/env python import thrift import thrift.transport.TSocket import thrift.protocol.TBinaryProtocol import TinyCalc.TinyCalc import random socket = thrift.transport.TSocket.TSocket('localhost',9090) protocol = thrift.protocol.TBinaryProtocol.TBinaryProtocol(socket) client = TinyCalc.TinyCalc.Client(protocol,protocol) socket.open() cnt = 0; while cnt < 1000: print client.sum(random.randint(10, 20),random.randint(10, 20)); print client.subtract(random.randint(10, 20),random.randint(10, 20)); cnt = cnt + 1 socket.close()
perlライブラリとはちょっとランダムな値の生成方法が違い,10から20の間の値をランダムに生成するといった実装になっています.そして,これの結果が5回計測して約80 secという結果になって,perlライブラリとほとんど同じになりました.
まとめ
やっぱりこの結果には自信がなくて公式のWhite Paperを読んだり,海外のサイトをチェックしてみたのですが,わからず.今回テストに使った環境を変えて別のサーバで実行してみたり,別のディストリビューションを使ってみたけど結果は同じになってしまいました.もしこの結果について何かわかる有識者の方がいらっしゃいましたらコメントかメールをいただけると幸いです.ちょっと気にな
るのでこの件に関しては引き続き調査したいと思います.しょうもない内容を最後まで読んでいただき恐縮でございます.