Hirotaka Sasaki
hiro1****@mti*****
2007年 5月 28日 (月) 03:13:22 JST
中居様 Cc: UltraMonkey-L7 開発者の皆様 はじめまして、佐々木と申します。 epoll部分のみしか分からないのですが、自分の認識と少し違うな、 と思う箇所がありますので、念のためコメントさせて頂きます。 少しでも参考になれば幸いです。 On Fri, 25 May 2007 10:43:19 +0900 中居憲久 <nakai****@yes*****> wrote: > > NTTComwareでUltraMonkey-L7の開発をしている中居と申します。 > 以後、よろしくお願いいたします。 > > さて、今現在のUltraMonkey-L7の次の部分を改良しようと思っております。 > 順に説明させていただきます。 > > 1.l7syncd > > 現在 l7syncd同士が通信をし、l7vsdとl7syncdがsharedmemoryを利用して同期 > するデータをやり取りしている。 > > [問題点] > ・protomod_c*系で1処理ごとにsem_init()やshm_init()を実行して共 > 有メモリにデータを書き込むため、パフォーマンス的に不利となる。 > ・UDPでデータをやり取りしているが、UDPはパケット順序が保障され > ないにもかかわらず、受け取った順序で共有メモリに書き込んでいるため、古 > いデータでsyncされる危険性がある ・パケットサイズを考慮しておらず、パ > ケット分割が発生した場合にデータが破損する可能性がある(MTUサイズによ > る)。 > > [解決案] > ・共有メモリを使用しない方式に変更する必要があると思われる。 > ・どのタイミングで同期化するのか検討が必要 > > 2.sorryサーバへの振り分け > > [問題点] > ・protomod_c*モジュール中のmatch_cldata()で共有メモリの情報を > 参照するために速度的な問題がある ・また、match_cldata()で判定後にiomの > destを直接書き換えている。 ・共有メモリ中の判定状況を戻す手段が無い > [解決案] > ・各clientにリアルサーバの接続を振り分けるsched部分がsorryサー > バの振り分け機能を受け持つのが妥当と考える。 > > > 3.振り分け機能 > [追加機能] > ・リアルサーバにはweightの値があるため、それを利用して振り分け > るschedモジュールを作成する。 > > > 4.select -> epoll化 > [考察] > epollに関してはEdgeTriggerとLevelTriggerが存在する。それぞれの > メリットデメリットは LTでは現在の構造的に無理なくマッチングされるが、 > FDの配列を走査する必要があり、betterではあるが bestではない。 > ETでは、イベントにマッチしたFD配列をkernelが提供してくれるた > め、FDの配列を走査する必要が無い。 エッジトリガ・イベントトリガともに、epoll_wait()のevents配 列へ、先頭から順に発生したイベントのみを設定してくれるため、差 異はなく、イベントトリガであっても全FDの走査は不要だと思います。 > また、回線の切断などの回線異常も > epoll_wait()のイベントとして捕捉可能なため異常処理が上位で捕捉可能にな > り、コード的に見通しが良くなり、メモリリークの可能性が減る。ただし、 イベントトリガでも捕捉可能ですので、差異はないと思います。 (この記述はselectとの比較でしょうか?) > バッファより大きなデータがTCP/IPスタックに来た場合には一度に全部読むの > ではなく、複数回にわたって読み込む場合には再度イベント登録する必要があ > り、その部分のコストがかかる。 > 色々と調査・検証されていると思いますので、自信がなくなってきま したが、現状の自分の認識では、エッジトリガとレベルトリガの処理 は下記のような違いがあると思っています。 (1) ソケットに何らかのイベントが発生すると、該当のソケッ トを、イベント有リスト(カーネル内のeventpoll構造体 のrdllistメンバのことです。良い日本語名が思いつかな かったため、便宜上、イベント有リストと呼びます)に登録 する。 (2) ユーザがepoll_wait()を発行すると、イベント有リス トに登録されたソケットのイベントを調査し、監視対象の イベントが発生していれば、epoll_wait()のパラメータ events配列に設定する。 (3) イベントが発生していたソケットを、レベルトリガの場合 は、イベント有リストに再登録するが、エッジトリガの場 合は、再登録しない。 (4) ユーザがepoll_wait()を発行すると、レベルトリガの 場合は、イベント有リストに登録されているので、再度イ ベントの有無が調査されるが、エッジトリガの場合は、調 査されない。(イベントが通知されない) ここで、(4)の前に、全てのデータを読み込んでしまった場合、レ ベルトリガでは、イベントが何もないにもかかわらず、イベント有 無が再調査され、この時はじめてイベント有リストから外れるため、 1回分、イベント有無調査の処理が余計にかかることになります。 なので、エッジトリガを採用し、recv()した時に受信バッファサ イズと読み込みサイズが同じ時だけepoll_ctl(EPOLL_CTL_MOD) を発行(この時にイベント有無が調査されます)すればうまく行くの かな、と思ったのですが、別の問題が発生します。 対抗マシンから、データ送信と同時にコネクションがクローズされ、 その後でepoll_wait()を発行した場合、データ受信とコネクショ ンのクローズイベントが1つのイベントとして消費されるため、1回 目のrecv()でデータを読み込んだ後に、コネクションがクローズ されているか判定するため、もう1回 recv()を発行する必要があ ると考えます。 前述のイベント有無調査と再recv()処理で、どちらが優位か分か らないのですが、エッジトリガを採用するならば、この辺も考慮さ れて設計された方が良いと思います。 > [実験] > 上記考察より、以下の実験を行ってみました。 > > 1)epollとselectの速度比較 > > サーバ側はepollとselectの2種類を作成する。 > サーバ側のプログラムは接続しているコネクションからデー > タを読み出すのみ(そのまま捨てる)。クライアント側はepollとselectとも共 > 通で指定分のthreadをcreateし、各threadはサーバに接続を行い、 > pthread_wait()で待ちの状態に入る。親threadは子threadが全てpthread_wait > ()に入った段階で、 ptthread_cond_broadcast()を投げて一斉にサーバに対し > て64kのデータを送信して終了する。各threadがpthread_wait()から抜けた状 > 態の時間とsend()から戻ってきた時間を合算してベンチとする。 Connection > を別時間とすることで純粋にsend()する時間を計測する。socketは > nonblockingに設定。なお、計測はRDTSC命令を使い分解度をクロックとしてあ > る。 > > > 結果 > > epollを使用したサーバの場合 > [root @ um01 client]# ./testClient 1000 > Time : 22782330 > [root @ um01 client]# ./testClient 1000 > Time : 22991643 > [root @ um01 client]# ./testClient 1000 > Time : 21260133 > > 平均クロック(平均時間):22344702 (0.006982719sec) > > selectを使用したサーバの場合 > [root @ um01 client]# ./testClient 1000 > Time : 28468971 > [root @ um01 client]# ./testClient 1000 > Time : 30799269 > [root @ um01 client]# ./testClient 1000 > Time : 30726819 > 平均クロック(平均時間):29998353 (0.009374485sec) > > おおよそ30%の性能改善が見られる。 > > 2)epollをETで使用した場合にepoll_ctl()でイベントを再登録すると > きのコスト > > これは上記サーバプログラムでepoll_ctl()の前後で時間計 > 測をした。 > 結果:平均クロック数:14419.53(=4.5nsec) > > > [結果考察] > selectからepollへの変更は1.3倍の待ち受け部分の性能向上が見込ま > れる。またepoll_ctlの処理コストも少ないことから、現状connで回線異常を epollをサポートすることは大賛成なのですが、「epollへの変更」 ということは、selectは無くしてしまうのでしょうか? 今後もLinuxしかサポートしないというならば、それで良いと思いま すが、他OSへの移植なども考えにあるならば、selectも残しておい ても良い気がします。 > またepoll_ctlの処理コストも少ないことから、現状connで回線異常を > 検出し、protomod*系で判断したのち、 imouxでコネクション等を削除してい > る部分がすべてiomuxで完結するメリットは大きいと考える。また、現在 send()時にエラーを検出する場合や、前述の理由により複数回 recv()を発行した場合に、recv()時にエラーを検出する場合もあ るため、iomuxだけで完結させるのは、難しいと思います。 > iomux_listでfdを管理しているが、epoll自体がfdの集合管理を行うため、 > iomux_listの走査が不要になり、この部分でのコードの単純化、及びメモリ > リークの可能性が減る。 > クライアントと実サーバのコネクションを1セットで処理している ため(例えば、クライアントのコネクションがクローズされた場合 は、同時に実サーバのコネクションもクローズするため)、epoll _waitで返却されたイベントに対応するソケットが、現時点で有効 なものかどうか(すでにクローズ済でないかどうか等)、管理する必 要はあると思います。 > > 以上、現状4つについて考察と試験結果を書かせていただきました。 > 上記案について、意見等ありましたらご指摘いただけると幸いです。 > どうぞよろしくお願いいたします。 > > _______________________________________________ > Ultramonkey-l7-develop mailing list > Ultra****@lists***** > http://lists.sourceforge.jp/mailman/listinfo/ultramonkey-l7-develop > -- Hirotaka Sasaki <hiro1****@mti*****>