Thriftのスピードについて

ID: 37
creation date: 2009/09/27 22:57
modification date: 2009/09/27 22:57
owner: shunya
tags: Thrift

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で前回のエントリーで作成した,「足し算,引き算」のプログラムのベンチをとってみることにしました.以下の図に概要を示します.

image:1:1253879138-ThriftSpeedTest.png

Thriftのテスト方法

図の左側がThriftの構成となります.サーバはC++で生成して,足し算,引き算のプログラムをそのC++のプログラムの中に書きました.クライアントはThriftで生成したperlライブラリを使いました.このプログラムの内容は前回のエントリーにも書いてありますが,要所部分だけ引用しておきます.

今回使用したプログラムをアップしておきましたので,ご興味がある方は使ってみてください.

TinyCalc.tar.gz

コンパイルの仕方,テストプログラムについては同封したREADMEファイルに記述しました.

thriftファイル

  • sumが足し算のインターフェース,subtractが引き算のインターフェース
#!/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を使いました.以下の条件でテストをします.

  • テスト回数は1000回
  • 足し算(sub), 引き算(subtract)に与える引数は1から9の数字を用いた4桁の値をランダムに生成したものです
#!/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がすごく遅いんですね.さすがにここまでは遅くならないだろー!ってぐらい遅い結果となってしまいました.だから,この結果には以下の可能性が潜んでいます.

  • テストプログラムに重大なミスがある
  • 実は僕が知らないだけでThriftにはチューニング用のパラメータがあって,それを使っていない
  • 単に僕のプログラムがバグがある

上記の可能性がある上で今回出た結果をあまり信用せずに眺めていただけたら,と思います.では,計測した結果を以下の図にしめします.計測方法は単純にperlクライアントをtimeコマンドを使って時間を計測し,Thrift,RESTそれぞれ5回ずつ計測して平均をとりました.

image:2:1253876512-ThriftSpeedTestResult.png

なんと,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を読んだり,海外のサイトをチェックしてみたのですが,わからず.今回テストに使った環境を変えて別のサーバで実行してみたり,別のディストリビューションを使ってみたけど結果は同じになってしまいました.もしこの結果について何かわかる有識者の方がいらっしゃいましたらコメントかメールをいただけると幸いです.ちょっと気になるのでこの件に関しては引き続き調査したいと思います.しょうもない内容を最後まで読んでいただき恐縮でございます.

Share Facebookでshareする
6 comments
nokuno : Thriftのほうが遅いとは、意外な結果ですね。サーバー側のデフォルトのTSimpleServerは遅いらしいので、TThreadPoolServerやTNonblockingServer(要libevent)を使った方がよいかもしれません。ただ、クライアントが1つでも差が出るかというと微妙ですねえ。 (2009/09/28 06:22)
kmd : こんにちは。昔Perl版を試用したことがありますが、1要素1パケット位の勢いでネットワークの伝送効率がどうにも悪かった覚えがあります。tcpdumpで見てみてはいかがでしょうか。 (2009/09/28 12:31)
tokuhirom : 遅い理由について考察してみました http://d.hatena.ne.jp/tokuhirom/20090928/1254093779 (2009/09/28 14:24)
shunya : nokunoさん: アドバイスありがとうございます!TThreadPoolServer、TNonblockingServer存在は教えていただき初めて知りました。ちょっとヘッダーファイルを読んでからチャレンジしてみたいと思います!また別エントリーにてご報告しますー。 (2009/09/28 14:31)
shunya : kmdさん: アドバイスありがとうございます!僕はネットワークはそんなに詳しくないのですが、僕もそのように感じたんですよねー。keep-aliveできていない的な感じで。netstat見た感じはそうでもなかったのですが。。。tcpdumpは試していなかったので試させていただきたいと思います! (2009/09/28 14:33)
shunya : tokuhiromさん: 素晴らしいです!丁寧に再現していただきありがとうございます!安心しました。traceの結果まで載せていただき感無量でございます。この結果を参考にさせていただきちょっとperlクライアントライブラリをいじってみて、またベンチをとってみようかと考えています。また別エントリーで引用させていただきますー。 P.S 実は僕もです。。。=> 自分の手元に環境つくるのが面倒なので自分はやらないですがw (2009/09/28 14:39)
riddle for guest comment authorization:
Where is the capital city of Japan? ...