Upload
masaru-oki
View
609
Download
3
Embed Size (px)
Citation preview
Lagopusの性能の話Apr 25, 2015
Masaru OKI @masaru0714
● OpenFlow対応ソフトウェアスイッチです
● (もちろん)オープンソースです
○ http://lagopus.github.io● よく言われている特徴
○ x86 Linuxで動きます
○ DPDKを使っていて、内部の工夫もあり速いです
○ マルチコア・マルチスレッドで動作します
○ OpenFlowの仕様のカバー範囲が大きいです
Lagopusとは(おさらい)
● MPLS-VLAN変換をやりつつ● 10万フローエントリを持った状態で● 10GbEでワイヤーレート (14.88Mfps)
○ ショートパケット (64bytes)
Lagopusの性能目標(当時)
ether header MPLSshim header upper layer header and payload
ether header VLAN header upper layer header and payload
● パケットの書き換えを伴う転送を実行する● 2ポート
想定している通信
MPLS network VLAN networkLagopus
ざっくり一言でいえば、ユーザスペースでパケット読み書きするライブラリ● コピーを(なるべく)しない● コンテキストスイッチさせない● メモリフォールトを起こさない● 割り込み駆動させない● キャッシュ活用、SSE命令活用● マルチコア活用、NUMAアーキテクチャを意識
予備調査サンプルプログラム example/l2fwd にてショートパケット転送→10GbEワイヤーレート出ました
I/O性能: DPDK
● ソフトウェアで処理する以上、基本的に○ なるだけメモリアクセスしない○ メモリコピーもなるだけ避ける○ コードも小さく実行ステップ数を少なく○ 余計な処理を削る
● などすれば、それらをしない場合と比較して、速くなる。● もちろん、本当に必要な処理を省いて速くしても意味はない。
OpenFlow処理性能
● OpenFlowでは特定の通信1本を「フロー」と呼んでいる○ たとえばPC-AがServer-Bに80/tcpの通信をするパケット○ たとえば任意ホストがServer-Cに53/udpの通信をするパケット○ 折り返し(たとえば上記パケットの応答)も別フローという勘定
● OpenFlowスイッチは、パケットヘッダを見てフローを選別し処理する○ 処理: パケットヘッダの書き換え、指定ポートへのパケット送信
● ひとつのフローを選別(match)する設定をフローエントリと呼ぶ○ これらが登録された塊をフローテーブルと呼ぶ
● OpenFlow処理対象のパケットは、一つのフローエントリにのみmatch
フローエントリ・フローテーブル
● 10万フローエントリ=10万種類のmatch○ 今回の想定では1方向(折り返し方向のパケットは考えない)
10万フローエントリ
entry # 優先順位 match action counter
1 1 マッチ条件1 処理1
2 1 マッチ条件2 処理2
:
100,000 1 マッチ条件100,000 処理100,000
matchの部分だけ抜粋したもの※実際のOpenFlowプロトコルは下記をバイナリエンコードしている
in_port=1,eth_dst=00:00:00:00:00:00,eth_type=0x8847,mpls_label=0,in_port=1,eth_dst=00:00:00:00:00:00,eth_type=0x8847,mpls_label=1,in_port=1,eth_dst=00:00:00:00:00:00,eth_type=0x8847,mpls_label=2,::in_port=1,eth_dst=00:00:00:00:00:00,eth_type=0x8847,mpls_label=99999,
使用したフローエントリ
MPLSのeth_type
label指定を10万種類
● 1秒間に1488万パケットを転送● 1パケットあたりの所要時間: 1/14.88M=67.2nsec● 2GHz CPUでは134クロック● 3GHz CPUでは201クロック
14.88Mfps
● Intel DPDKという速いパケットI/Oライブラリがある● Lookup(match)とactionがどのくらいの速さか測ってみて考える● まず愚直にエントリ1にmatch?→エントリ2に…というものを作る
○ 末尾のエントリでは遅くなるという予想は簡単にできる● 1コアで、受信-OpenFlow処理-送信を連続実行(1スレッド)
Lagopusプロトタイプ1st
受信 match action 送信コア1
10万エントリをリニアサーチする
● 測ってみないとどのくらい理想と現実に差があるかわからない● 測ってみた (Xeon 2.2GHz)● 先頭のエントリにmatchするトラフィックのみ: 14.88Mfps● 末尾のエントリにmatchするトラフィックのみ: 22Kbps (42fps)
笑えるくらいひどい数字を見た。
● 末尾エントリに当たる場合、ほとんど流れないに等しい。● 末尾エントリに当たるパケットで速くする必要がある。
Lagopusプロトタイプ1st測定
● Lookup アルゴリズム○ さすがに愚直に10万エントリをリニアサーチするのはないだろう○ どうにかリニアサーチせずに済ませられないかのアイデア出し
● 対象エントリをどれだけ絞り込めるか?○ リニアサーチでいえば、エントリ数が半分ならかかる時間も半分○ Lookupにかかるメモリアクセス量が少ないほど速くなる
● データ構造○ サーチに適したデータ構造をとることで速度向上できるか?
● 複数コアの利用○ 複数コアで並列処理させればその分速くなる○ 処理をパイプライン化させればコアごとの処理が局所化できる
速度向上案
● https://www.openmp.org/● 繰り返し処理が記述されたプログラムを並列化する● C言語では#pragmaディレクティブを埋め込むだけ● 例
#pragma omp parallel forfor (i = 0; i < max_entry; i++) {
並列化したい処理;}
● どのコアを使うなどの細かい制御は要求されず自動的に処理される● 探索対象フローエントリを等分してそれぞれのコアで処理● 処理済みのエントリを持ち寄って、もっともpriorityの高いエントリを採用
OpenMPを使ってみた
● 試作して動かしてみた● かえって遅くなった● なぜか
○ 10コアでばらしても1コアあたり1万エントリの探索○ コアごとの選出エントリが出揃わないと比較できない○ 比較して最終的に選ぶときには1コアでの処理○ 1パケットごとにスレッドをバラして止めてを繰り返しすぎる○ DPDKのコア・スレッド管理との相性も良くない
● OpenMPによる高速化の試みは失敗。● 書いた試作コードは非公開のまま闇に消えていただいた
OpenMPを使ってみた: 結果
● ルーティングテーブルではPatricia Treeが使われる● OpenFlowでは単純にPatricia Treeを適用することができない● OpenFlowのmatchは複雑
○ パケットヘッダの特定のフィールド40種類 (OpenFlow 1.3)■ eth_src, eth_dst, eth_type, vlan_id, ip_src, ip_dst, ip_proto,...
○ 任意のフィールド複数をAND指定できる○ 省いたフィールドはワイルドカード○ 値の比較の際にビットマスクを指定できる○ エントリにpriorityがあり、順序に基づきmatch結果が決まる
● OpenFlow仕様を逸脱する実装は行わない● option指定となっている機能も実装を省かない前提
Lookupの難しさ
ip_srcをキーにして高速探索するコードを作っても結果が誤りとなる例
一筋縄ではいかない例
フロー# priority ip_src ip_dst
1 32 192.168.0.1 -
2 31 - 10.0.0.1
3 30 192.168.0.2 -
4 29 192.168.0.3 -
priority順に探索する
フロー2が正解
ip_srcで探索すると
フロー3となるが、誤り
eth IPv4 src:192.168.0.2 dst: 10.0.0.1 Payload
● いくつかのフィールドにてexact matchと無指定のテーブルを用意する● exact matchは、配列やhash table, patricia treeなどで高速探索● 探索した先は、別のフィールド用の2つのテーブル● 無指定のテーブルは、別のフィールド用の2つのテーブル● exact matchを続けた結果のエントリと無指定のエントリのpriorityを比較● 優先度の高い方を選択 * テーブル段数でエントリを特定する● flow_mod(フローエントリ登録)にも利用し、高速化できた
リニアサーチよりは速いが、これがベストかは疑問。他の探索アルゴリズムやテーブル構造についても調査・検討中。
現在のLookup table実装
● 他の要素(VLAN IDやMPLS labelなど)を省いて簡略化した例● eth_typeは配列、ip_src, ip_dstはマスク別のpatricia tree● priorityの高いほうの結果(フローエントリ)を選択する
Lookup table例
Top
0x0000 0x0800 0xffff無指定
無指定 ip_src
無指定 ip_dst無指定 ip_dst
...eth_type
ip_src
ip_dst
● 1コアでやるから遅い● 複数コアで並列に処理すればその分速くなる
○ ただしX個にすればX倍、とはいかない● 共有リソースアクセスに対するロックが問題となる→バルク処理● 命令キャッシュのヒット率を高めるためコアごとに処理内容を分ける
複数コアの利用
受信OpenFlow処理
送信OpenFlow処理OpenFlow処理OpenFlow処理
受信 送信
Lock n個処理 Unlkn個受信 n個送信
read lock同士は干渉しない
locklessのring buffer
● テーブル探索で速くなったが8コア、12コアでも14.88Mfpsに届かず● 8Mfps程度● Linuxに用意されているperfコマンドを用いて遅い個所を調査● Lagopusを動かしておき、別ターミナルでsudo perf topを実行● 実行時間の長いfunctionが上位に表示される
○ 処理が軽くても空回り(busy loop)しているfunctionは上位に表示● 調査結果、まだまだLookupが遅いことが判明
まだ遅い。
1. 最初のパケットは通常のマッチ処理を行う2. マッチ完了→パケットヘッダの情報とフローエントリが関連付けられる3. 関連付けをキャッシュテーブルに登録する4. 次のパケットはパケットヘッダの情報でキャッシュを引く5. 見つかればフローエントリは確定しているのでマッチ処理完了6. 見つからなければ通常マッチ処理してキャッシュ登録
これにより12コアを使ってMPLS-VLAN転送14.88Mfpsを達成。(2014年3月)
Lookup軽量化: flow cache
● OpenFlow処理スレッドでは、1パケットをmatch-action完了まで実行する● 毎回やる必要のない処理があれば、省くことで速くなる● たとえば、特定処理の時のみ使われる変数の初期化
○ OpenFlowのactionには、2種類がある■ 即時実行するapply-action■ 書き溜めておき、最後に実行するwrite-actions
○ write-actions用変数の初期化は毎回必要とはならないことが多い○ これを必要な時のみ実行するよう変更
もっと速く
● OpenFlow処理スレッドでは、1パケットをmatch-action完了まで実行する● 細かく見ると、パケットヘッダの情報収集、match、actionにわけられる● パケットヘッダの情報収集をn個まとめて処理するよう変更● 命令キャッシュの利用効率が向上● Intel Performance Counter Monitor (Intel PCM)で調査できる
○ CPUが持つカウンタを参照するのでXeonが必要● 2015年4月現在、前出と同ハードにて、4コアを使って13Mfpsの性能
○ 開発版○ 近日リリース予定とのことです
もっともっと速く
アイデア段階のものがいくつかある。● matchとactionをスレッド(コア)分離させる● matchをバルク処理させる● action処理を見直す● RSSである程度処理を分散させ、一部の探索を省く● ポートごとに独立した処理とし、ポート数が増えても速度を維持させる● Intel NICのflow director機能を使い、ハードウェアflow cacheを実現● flow cacheに使っているhash tableを高性能なものに置き換える● バルク処理パケット数の調整(レイテンシ・揺らぎが増える)● フローテーブルの内容によりダイナミックに探索テーブル生成
● and, more! 提案、実装、大歓迎!
さらなる性能向上に向けて
Lagopus
http://lagopus.github.io
ONF (OpenFlowの総本山)
http://www.opennetworking.org/
Lagopus User Community
http://www.lagopus.community/cms/
DPDK
http://dpdk.org/
Reference