寒くなってきましたね.秋です.ブログを更新する時間がなかなか確保できませんな今日この頃です.

はじめに

今日は機械学習におけるソフトの「再利用」と「再実装」についておもしろい記事を見つけたので引用しながら考察したいと思います.このトピックに関しては機械学習だけに限らず,すべてのソフトにおいていえると思います.ソフトの「再利用」と「再実装」
に関しては,私は学生時代から結構議論になっていましたし,そして現在ビジネスシーンでもこれについては議論になります.特にビジネスでは工数という概念があるためなるべくなら「再実装」はしたくないという前提がありますよね.

本日引用する記事は以下の記事です.

Re-implement or reuse?
Posted by Cheng Soon Ong on October 14, 2009

このブログはmloss.org(machine learning open source software)という機械学習に関する手法の実装を登録・検索できるサイト上の有
識者による機械学習に関するコラムに掲載されていた記事です.ここのサイトは実装の検索だけでもかなり有用なのですが,何気にブログもおもしろい!ということを渋谷の中心で叫びたいと常々思っていました.

Why reuse and Why re-impelment

さて,上記の記事で「再実装または再利用?」というタイトルで以下のように,それぞれのメリットを論じています.

Why reuse
    ■ Standard software engineering practice 
    ■ antipattern (reinvent the wheel) 
    ■ Better than "hacked up" own version
    ■ Save time by outsourcing maintenance

Why re-implement
    ■ Existing solution is not good enough
    ■ Educational purposes
    ■ Not aware of other solution
    ■ Existing interface changes too often

Re-implement or reuse? より引用 詳しくは参照先のエントリーを参照されたし.

和訳

  • なぜ再利用するか
    • 一般的なソフトウェア工学の慣習のため
    • アンチパターン(車輪の再発明)
    • 自分で作るよりも優れているため
    • メンテナンスの時間がとられない
  • なぜ再実装するのか
    • 既存のソリューションは十分ではない
    • 教育的な目的
    • すでに存在するソリューションの存在に気づいていない

考察

当たり前といってしまえば当たり前のことのなかもしれないですが,今後の自分のためにもこのトピックに関して自分なりの考えをまとめておきます.
ある問題を解決するときには,考えなしにすぐに再実装するのではなく,再実装するにせよ再利用するにせよ,必ず既存の実装について調査をしなくてはならないと思っています.例えば,既存の実装では,マシンスペックをどれぐらい要求するのか(特にメモリ使用量など),処理時間はどれぐらいかかるのか,APIは適切に実装されているかなどを調べなくてはならないと考えています.この調査をしっかりしていなければ,再実装した際に評価する指標も無いし,もしかしたら劣ったものを実装してしまう可能性もあるため
です.再利用できるかを検討する際は,「自分が求める要求」と「既存の実装に足りないこと」を照らし合わせ,再実装したほうが早いか,既存の実装を修正し再利用したほうが早いかを検討します.

ちょっと中途半端ですが今後もこの話題に関しては機会があったらまとめたいと思います.この手の話題は結構ソフトウェア工学系の分野では語り尽くされているのかもしれないので,その手の本を読んだときにまた再考したいと思います.

もし,読んでくださった皆様にも意見がございましたら,お気軽にコメントくださいませ.


thriftとかhadoopなど,何やらいろいろと手を出してしまい,ここのところブログの更新が滞ってしまっていますが,今日は前から書きたかったトピックについて自分へのメモの意味も含めて記しておきたいと思います.

はじめに

最近,といっても結構前からなのですが,海外のブログなどで「機械学習の勉強を始めるガイドライン」についてのエントリーがいくつか見られ,かつ,議論も少し盛り上がっています.僕は機械学習が好きなだけで,専門というにはほど遠いのですが,僕も一利
用者としてはこのトピックに関してはとても興味があります.

機械学習というと,色々な数学的な知識が必要であったり,統計学や人工知能の知識も必要になったりしまったりと,専門的に学ぶ機会が無かった人にとっては興味が湧いてもなかなか始めるには尻込みしてしまうことかと思います.今日紹介するエントリーは,
そんな方々にヒントになるような内容になっていると思います.基本的なことを体系的に説明しているのでとても質が高いです.

紹介するエントリー : Guide to Getting Started in Machin Learning

要約になりますが,上記のエントリーでは機械学習の勉強を始める流れを以下のように説明しています.

The Elements of Statistical Learning Data Mining, Inference, and Prediction.を読む

  • なんとこの本は[[PDF|http://www-stat.stanford.edu/~tibs/ElemStatLearn/]]が公開されていますので参照してください.
  • この本は有名な教科書なのですが,これのチャプター1のIntroductionを読んで概要をつかむと良いと言っています.
  • Introductionには統計学習がどのような分野で応用されているかなどがまとめられています.
  • 医療分野や,マーケティング,画像処理,e-mailのスパムフィルタリングなど

Rで試す

  • The Elements of Statistical Learning Data MiningのIntroductionを読んだら,をダウンロードしましょう.
  • GoogleやFacebookではRがどのように活用されているかを記したエントリーのリンクがありおもしろかったです.How Google and Facebook are using R

Googleの場合

The typical workflow that Bo thus described for using R was: 
(i) pulling data with some external tool,
(ii) loading it into R,
(iii) performing analysis and modeling within R,
(iv) implementing a resulting model in Python or C++ for a production environment.

上記を意訳で要約すると,GoogleのBoさんはRを以下のように使っているようです.

  • 何らかの抽出ツールでデータを取得する
  • Rに読み込ませる
  • Rを使って解析したりモデルを作ったりする
  • 結果のモデルをPythonやC++で実装する

といった感じで,つまりプロトタイプの段階でRを使って実験するようですね

データを使う

Rで何かしらので機械学習の手法を使うには,データが必要となります.学習やテスト(分類)に使うデータをよくデータセットというのですが,これを集めるのは容易ではありません.以前にここのサイトでも紹介したデータセット集(Classicicationにおけるデータセット)をこのエントリーでも紹介しています.

UCI Machine Learning Repository: Data Sets

ここのデータセットを使ってRで遊んでみると良いでしょう.エントリーでは,先ほど示したThe Elements of Statistical Learning Data Miningを読み進めてRで実装してみると良いと言っています.

個人的には直感的に学ぶにはWekaもいいと思っています.wekaについては後述します.

Bishop本を読む

他にも機械学習の教科書は山ほどあるのですが,以下の本がおすすめだと言っています.

この本はかなり評判がいいですよね.実は僕は図書館で必要な箇所を読んだことしかないのですが,よくまとまっていた記憶があります.これは和訳が販売されていて,これもかなり評判がいいですね.結構色々なところで読書会とかもされているようですね.僕
も買わなくちゃ.Bishop先生が書いた本ということで,一部ではBishop本と呼ばれているようです.

授業の動画ライブラリを見る

昨今は本当に便利な時代になって,スタンフォード大学の機械学習の授業を見ることができます.

Course Machine Learning

以下が授業で使っているハンドアウトになります.

CS229 Machine Learning Course Materials

実は,これは僕もジムで筋トレしながらこつこつと見ていたのですが,とてもおもしろいです.でも,音声が英語ということもあって,英語力も要求されるため,機械学習に関して全く知識が無いと聞き取りが大変かもしれないので,英語が得意ではない方は一通
り勉強し終わってから復習の意味で見ると良いかもしれません.

以上が上記のエントリーに記述されていた,機械学習の初学者のためのガイドラインとなります.

で,自分はどう思うか

で,自分もまだまだ初学者の一員ではあるのですが,自分なりに機械学習を勉強する事始めとして良いのではないかと思っていることをまとめておきます.もちろん,これは賛否両論あると思いますし,人によって効率の良い学習方法は違うと思いますので,これ
は一アイデアとして読んでいただけたらと思います.

まずは,興味を持った手法を徹底的に学び尽くす

私は機械学習の全体の知識をいきなりつけようとすると挫折してしまう可能性が高いと思っています.特に昨今のアルゴリズムはとても複雑なモデルを使うようになっており,理解に時間がかかるものが多くあります.そこで,私としては(当時の私は)自分がやりたいことが達成できそうなアルゴリズムを1つに絞って勉強するというスタンスをとりました.そして,そのアルゴリズムは初学者であれば単純なものが良いと思います.例えば,機械学習の分野(明確には機械学習の分野とは言えませんが)で単純なアルゴリズムで,威力があるものでは,Naive Bayesや,decision tree,KNNなどがあります.これらのアルゴリズムはデータマイニングや,パターン認識などですぐに使うことができます.人によっては式だけ眺めてうなっているよりも,作りながら悩んだ方が理解が早いことがあります.動かしながらだと直感的に理解できるんですね.もちろん,人それぞれ学習の仕方があると思うので万人には通用するとは思っていません.

そして,一つのアルゴリズムを徹底的に学ぶといっても,実は全然一つでは収まらなくなります.例えばNaive Bayesを使ってみよう!と思ってNaive Bayesの論文などを読むと,その論文の中には細かい引用がたくさんされています.そして,その引用先の論文で言っている手法を理解していないと,その論文で言っていることがわからなかったりします.結局一つの論文を読むのに,10以上の論文を読むことになったりします.だから,簡単なアルゴリズムでもちゃんと理解しようするにはとても苦労するんですね.だけど,学ぶことがたくさんあるからそういうことをしているうちにだんだん幅広い知識がついてくる,と経験上思いました.

オープンソースのライブラリから学ぶ

僕がこれらの勉強を始めた当時は,実はあまりオープンソースのライブラリの情報がそこまで出回っていませんでした.しかし,今は本当に多くの,かつ質のいいオープンソースの機械学習のライブラリがあります.教科書を見ていてよくわからない!という方は人の実装を見ながら仕組みを理解するといいと思います.数式がものすごく難しく説明されていても,ソースを見ると意外に実装は簡単だったりするものもあります,自分の力だけで実装するのもいいし,人の実装を参照しながら自分の実装をするのも良いと思います.

一例ではありますが,最近では以下のようなものがあります.(全部細かくは調査していません)

nltk

  • http://code.google.com/p/nltk/
  • Python
  • 自然言語処理のライブラリなのですが,機械学習のアルゴリズムも多く実装されています.
  • Pythonということもあって,処理は遅いですがコードは読みやすいのでプロトタイプとしてはおすすめです.
  • クラスタリング・分類関係だと以下のような手法が実装されています
    • decision tree
    • maximam entropy
    • naibe bayes
    • kmeans

weka

  • http://www.cs.waikato.ac.nz/ml/weka/
  • JAVA
  • このサイトでもちょっと紹介したwekaですが,これはJAVAで実装されたData miningツールで多くの機械学習の手法が実装されています.
  • 有名な手法はだいたい実装されています.スピードもそこそこ出るのでちょっと遊んだり実装を見るにはおすすめ.

Mahout

  • http://lucene.apache.org/mahout/
  • JAVA
  • Hadoop上で動く機械学習ライブラリです.これは初学のためにはお勧めできませんが,Hadoop上で動くということもあり,実用上使えると思って現在調査中です.

まとめ

今回は素人の素人による素人のための機械学習事始めということでエントリーを書かせていただきました.実は僕は勉強の仕方は「何が一番いい」っていうのは無いと思っています.でも,勉強するために手助けになるための情報は存在すると思っています.それを一度まとめておきたいなと思って,書かせていただきました.もし,こんな情報があるよ!とか,こうやると効率がいいよ!というアイデアがある方がいらっしゃいましたらご教授いただけたらと思います.


10/14 追記
shima_shimaさんより参考になるご意見をいただいたので追記しておきます。以下shima_shimaさんのコメントを整理して引用させていただきます。

  • Bishop本のレベル表

http://ibisforest.org/index.php?PRML/course

  • その他参考になる本
    • 強化学習なども解説されているが古い部分もある
    • カーネル
    • マルコフ連鎖
    • 入門で優しい内容
    • Bishopレベル表の入門レベルの内容
    • Bishopレベル表の入門レベルの内容
  • #% 2009-10-13T02:16:16+09:00|shima_shima|PRML本翻訳の仕掛け人です.とりあげていただいてどうもです. EoSL本は,Bishop本より高度だと私は思っています.加算型モデルを重視しています.ちょっと,内容的には我田引水があると思います. Bishop本は,ややカーネルが多く,全体をベイズの観点で見ているという特徴があります.Bishopさんの筆力により,読みやすい本だと思い翻訳を思い立ちました. いろいろなレベルの内容が混ざってますので,レベル表を作りました http://ibisforest.org/index.php?PRML/course あと,著名な本は Tom Mitchell の Machine Learning です.強化学習とかもちょっと載ってますが,やや古くなってきました. カーネル系にご関心があれば,赤穂「カーネル多変量解析」岩波 とか,MCMCとかなら伊庭 他「計算統計II — マルコフ連鎖
    モンテカルロ法とその周辺」岩波 とかかと. より入門的な内容としては,ブログの内容を拝見していると,おそらく簡単すぎるかと思いますが,荒木「フリーソフトで作る音声認識」森北の第1部 が一番やさしいと思います. その次だと,麻生 他「パターン認識と学習の統計学」岩波とか,元田 他「データマイニングの基礎」オーム社あたりかと思います.この辺が,Bishop本のコース表の入門レベルとお考え下さい. 線形代数と確率は多少いりますが,l^2空間とか,複素数とか,可則集合とかまで踏み込む必要はないので,高校数学 + Bishop本の1章+付録で事足ります. 機械学習にご関心をもっていただいて嬉しいです.以上,よろしければ,ご参考になさって下さい.
  • #% 2009-10-13T11:08:37+09:00|shunya|shima_shimaさん: 貴重なコメントありがとうございます!感激です. レベル表を拝見させていただきました.素晴らしいです! こういう指標があると学習するときは助かりますね.また,すべてではないですが各項目に解説ページが貼られているがとても便利だと思います.ご存知かとは思いますが,英語のwikipediaの機械学習のページが結構網羅的に書かれていて,普段はそこを参照したりしているのですが,shima_shimaさんのページもレパートリーに追加したいと思います. http://en.wikipedia.org/wiki/Machine_learning_algorithms レベル表を拝見していて思ったのですが,初学の場合はリストの「基礎」項目だけ読んでみて雑感をつかむのも良いかもしれませんね.項目を見ていて改めて良い本だと思いました.買っちゃいます. ご紹介をいただいた本では,麻生 他「パターン認識と学習の統計学」岩波は当時SVMの勉強をしていたときに読みました.当時はあまりこの手の本が無かったので重宝していた気がします. – 赤穂「カーネル多変量解析」岩波 – 伊庭 他「計算統計II — マルコフ連鎖モンテカルロ法とその周辺」岩波 – 荒木「フリーソフトで作る音声認識」森北の第1部 – 元田 他「データマイニングの基礎」オーム社 これらの本で,岩波のものは前から気になっていたのですが目を通せずにいました.Bisiop本の入門編といった感覚で読んでおきたいと思います.というのも,実は今回書いたエントリーの内容は入門としては難しいと感じておりました.しかし,入門の入門,つまりshima_shimaさんが紹介してくださったような本の情報が少ないように感じていて,そこらへんの情報を勉強がてら少しずつ整理していきたいと思います.
  • #% 2009-10-14T11:53:41+09:00|anonymous|PDFのリンクのURLにtypoがありました
  • #% 2009-10-14T11:57:44+09:00|m11m|荒木先生の本ですが、タイトルは軽いですけど中身は教科書です。学部の講義で使われていました。
  • #% 2009-10-14T14:13:28+09:00|shunya|anoymousさん: 修正しましたー。ご指摘ありがとうございます。
  • #% 2009-10-14T14:15:15+09:00|shunya|m11mさん: 情報提供ありがとうございます!amazonで目次を読んでみたのですが、基礎事項が整理されているよう良さそうですね。参考に読んでみますー。


ここまでの流れの整理をします。Thriftの調査の続きです。前回のエントリー(Thriftのスピードが改善しました)では、Thriftのperlクライアントが遅いのはNagleアルゴリズムが原因ではないかという仮説をたて、そしてNagleアルゴリズムをオフにした状態で速度が向上したことを書きました。

それに対し、tokuhiromさんにより投稿されたエントリー(ThriftはThrift::BufferedTransport をつかいわすれると 147 倍遅くなってつかいものにならない)では、send(2)のバッファリングをしていないことが原因で、Thriftが吐き出すデフォルトのperlクライアントのスケルトンでは使われていない
Thrift::BufferedTransport を使うことによってsend(2)がバッファリングされて、高速になることを示してくれました。加えてkazuhookuさんのご指摘によりread(2)もバッファリングしてないため遅いとのこと。僕の環境でもThrif::BuffredTransporを使うことによって高速化することを確認しました。

そして、「Thriftのスピードが改善しました」のエントリーに対して、はてぶコメントでkazuhookuさんに以下のコメントをいただきました。kazuhookuさん、ありがとうございます!

282QPSってむちゃくちゃ遅いと思う。てか、サーバサイドのロジックがここまで単純なら、多重化してベンチとるべき / これだとパケット流れまくるのでせめて MSG_MORE とか使うべき

http://b.hatena.ne.jp/entry/blog.broomie.net/index.cgi?id=38 より引用

MSG_MOREについて

ご指摘をいただくまで知らなかった&調べてみてわかったのですが、確かにTCP_NODELAYにするのであれば、MSG_MOREを使ったほうが良いみたいですね。冗長で恐縮なのですが、メモの意味も含めて整理すると、TCP_NODELAYというのはmanで以下のように解説されて
います。

% man tcp

ソケットオプション
  TCP ソケットのオプションは、オプションレベル引数に IPPROTO_TCP を指定した setsockopt(2) で設定でき、 getsockopt(2)で取得できる。さらに、ほとんどの IPPROTO_IP ソケットオプションも TCP ソケットに対して有効である。詳細は ip(7)を見よ。

TCP_NODELAY
  設定すると Nagle アルゴリズムを無効にする。すなわち、データ量が少ない場合でも各セグメントは可能な限り早く送信される。設定されていないと、送信する分だけ溜まるまでデータはバッファされ、小さなパケットを頻繁に送らずにすみ、ネットワークを有効に利用できる。このオプションは TCP_CORK により上書きされる。しかしながら、 TCP_CORKが設定されている場合であっても、このオプションを設定すると、送信待ちの出力を明示的に掃き出す (flush) ことになる。

以前のエントリーと繰り返しになってしまいますが、上記の引用のようにTCP_NODELAYを有効にすると 「データ量が少ない場合でも各セグメントは可能な限り早く送信される」 わけですね。そうすると、今回の実験のケースのような場合、細か
いパケットが通常よりも大量に送信されてしまう可能性があります。そこで、それを防ぐためにせめてMSG_MOREを使ったほうが良いという指摘をうけたのだと思います。MSG_MOREが何なのかというman sendで以下のように解説されています。

% man send

MSG_MORE (Linux 2.4.4 以降)
  呼び出し元にさらに送るデータがあることを示す。このフラグは TCP ソケットとともに使用され、TCP_CORKソケットオプションと同じ効果が得られる (tcp(7) を参照)。TCP_CORK との違いは、このフラグを使うと呼び出し単位でこの機能を有効にできる点であ
る。Linux  2.6 以降では、このフラグは UDP ソケットでもサポートされており、このフラグ付きで送信された全てのデータを一つのデータグラムにまとめて送信することを、カーネルに知らせる。まとめられたデータグラムは、このフラグを指定せずにこのシス
テムコールが実行された際に初めて送信される (udp(7) に記載されているソケットオプション UDP_CORK も参照)。

これだけではわからないので、TCP_CORKも引用しておきます。

% man tcp

TCP_CORK
  セットされると、partial フレームを送信しない。このオプションが解除されると、キューイングされたpartialフレームが送られる。これはsendfile(2)を呼ぶ前にヘッダを前置したり、スループットを最適化したい場合に便利である。現在の実装では、TCP_CORKで出力を抑えることができる時間の上限は200ミリ秒である。この上限に達すると、キューイングされたデータは自動的に送信される。Linux 2.5.71 以降においてのみ、このオプションをTCP_NODELAYと同時に用いることができる。移植性の必要なプログラムでは
このオプションを用いるべきではない。

この解説の通り、部分的な小さいフレームは送信せずにキューイングしたかたまりを送ってくれるようになります。そして、このフラグはTCP_NODELAYと同時に用いることができます。上記で引用したTCP_NODELAYの解説によるとTCP_CORKによってTCP_DELAYは上書きされてしまい、TCP_MSGが優先されるようですね。しかし、送信待ちの出力を明示的にflushするとなっているが、これがどう振る舞いに影響するのかはもっと詳しく調査してみないと定かではないのでここでは述べないことにします。

MSG_MOREをThrift::Socketで使ってみようと思って、perlライブラリに入ってるSocketを見てみたのですが、まだMSG_MOREは対応していないみたいでした。おそらくDanga::Socketなどを使えばできそうかもしれませんが、Thrift::Socketを大々的に書きかえる作業は今はやりたくないので今回はあ
きらめました。

で結局どうしたらいいのか

ここまでの考察で、確信はできないけれどもTCP_NODELAYやMSG_MOREを使うことは場合によっては危うい感じもします。であれば、少しでもリスクになりうるTCP_NODELAYフラグは使わないほうがよいと僕は結論を出しました。というのもtokuhiromさんによって示された、Thrift::BufferedTransportを使ってreadをバッファリングする方法で十分に高速化できることを確認できたためです。


多重化してベンチをとる

「282QPSってむちゃくちゃ遅いと思う。てか、サーバサイドのロジックがここまで単純なら、多重化してベンチとるべき」とのご指摘をうけ、「うう、確かにそうだなあ。。」と思い、server(c++) + client(perl + Thrift::BufferedTransport)の構成でクライア
ントを多重化してベンチをとってみることにしました。

server側をマルチスレッドにする

今回もプログラムは以前のエントリー(Thriftが便利すぎる)で紹介した「足し算・引き算」サーバを使います。この構成ではサーバ側はC++で
実装しています。そしてTSimpleServer.hというシングルスレッドのサーバを使っているため、このままの状態でクライアントを多重化してsendしてもシングルスレッドで処理されてしまいます。TSimpleServerで当方の環境で多重化したクライアントで使用したら
サーバがアボートしました。

そこで、今回はマルチスレッドのサーバを作成することにします。したがってTSimpleServerではなく、TThreadPoolServerを用います。そのほかにもサーバ用ライブラリはいくつかありますが時間の関係もあり全ては未調査なので、このエントリーでは他のライブ
ラリに関しては割愛させていただきます。以下に今回のベンチで使用したTThreadPoolServerを使ったサーバの実装を示します。

#include "TinyCalc.h"
#include 
#include 
#include 
#include 
#include 
#include 

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::server;
using namespace apache::thrift::transport;
using namespace apache::thrift::concurrency;


using boost::shared_ptr;

class TinyCalcHandler : virtual public TinyCalcIf {
public:
  TinyCalcHandler() {
    // Your initialization goes here                                                                                                                                                
  }

  double sum(const double param1, const double param2) {
    return param1 + param2;
  }

  double subtract(const double param1, const double param2) {
    return param1 - param2;
  }
};

int main(int argc, char **argv) {
  int port = 9090;
  int workerNum = 50;
  shared_ptr handler(new TinyCalcHandler());
  shared_ptr processor(new TinyCalcProcessor(handler));
  shared_ptr serverTransport(new TServerSocket(port));
  shared_ptr transportFactory(new TBufferedTransportFactory());
  shared_ptr protocolFactory(new TBinaryProtocolFactory());
  shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workerNum);
  shared_ptr threadFactory =
    shared_ptr(new PosixThreadFactory());
  threadManager->threadFactory(threadFactory);
  threadManager->start();
  TThreadPoolServer server(processor, serverTransport, transportFactory,
                           protocolFactory, threadManager);
  server.serve();

  return 0;
}

TSimpleServerとの大きな違いはスレッドのマネージャとしてThreadManagerを使うことです。上記の例のように使い方は簡単です。ThreadManager::newSimpleThreadManager(workerNum)この関数では、ThreadManagerを生成するときにworkerの数を指定します。今回は50にしましたが環境に合わせて引数を与えてください。

今回ベンチに使用したソースをUPしておきましたので、ご興味がある方は参照してみてください。コンパイルの仕方などは同封されているREADMEに記載しておきました。

TinyCalc.tar.gz

ベンチ方法

今回のベンチテストではサーバー1台、クライアント8台の構成で行いました。サーバ、クライアントすべて以下のスペックです。

cpu: AMD Opteron 2.4GHz * 4core
memory: 4GB

サーバについて

サーバのプログラムは上記のTinyCalc_server.cppを用い、ThreadManagerの引数に与えるworkerの数は50で行いました。

クライアントについて

クライアントはperlクライアントを用い、実装を以下に示します(TinyCalc.tar.gzに入っています)。localhostの部分は環境にあわせて修正してください。

1クライアントに5プロセスずつ同時に走らせ、クライアントサーバはすべてで8台あるので合計40プロセスが同時に1台のThriftサーバに問い合わせます。

#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/gen-perl/";

use lib './gen-perl';
use Thrift;
use Thrift::BinaryProtocol;
use Thrift::BufferedTransport;
use Thrift::Socket;
use TinyCalc;

use constant TEST_NUM => 100000;

my $transport = Thrift::Socket->new('localhost', 9090);
my $client = TinyCalcClient->new( Thrift::BinaryProtocol->new(Thrift::BufferedTransport->new($transport)) );
$transport->open();

for(my $i = 0; $i < TEST_NUM; $i++){
    my $arg1 = int(rand(10000));
    my $arg2 = int(rand(10000));
    my $sum = $client->sum($arg1, $arg2);
    my $subst = $client->subtract($arg1, $arg2);
    print "$sum\t$subst\n";
}
$transport->close();

結果

上記の構成で5回計測したところ平均28649QPSとなりました。処理は簡単であるためサーバ側のCPUはまだまだ余裕だったので,クライアントが増えればまだあげられると思います。でも、そろそろネットワークがボトルネックになるような気もしました。

考察

僕が想定しているThriftのユースケースでは、主に分類器やクラスタリングなどに使おうと思っています。分類器などの場合は、分類器自体の計算量が高いのでボトルネックは分類器となる可能性が高いと思います。今回Thriftで簡単な処理をさせて約3万QPSが出ることが確認できたので、分類器などを使う場合、Thriftは十分なスピードかな、と思いました。分類器でThriftを使う場合は転送させるデータ量も意識しないといけないので、圧縮して送ったりしないといけないかな、とか考えたりしています。

まだThriftのチェックできていないライブラリも多くあるので、今後もよさそうな情報があったらエントリーを書きたいと思います。情報提供していただいた皆様ありがとうございましたー。当方、ネットワークの知識はあまりないのでとても勉強になりました。
多謝多謝。