ネットワークプログラムの性能向上は、動かして観察することも大事
あるブリッジ型のネットワークシステムを開発していて、性能をもう1段階アップさせて欲しいと言われ、他の仕事をこなしながら、悩み続けていました。
もともと性能要求は高いシステムで、ネットワークは10G接続です。このクラスになると、それこそチェックサムの計算などもボトルネックとして目立つようになるくらいです。
gprofやoprofileなどのプロファイラを使って、遅い処理や回数が多い処理を中心に高速化を行ってきましたが、さらに後1段階(わかりやすく言うと倍くらい)性能をアップさせたいということで、それができないと実用化は難しいので、プロジェクトは打ち切るか?ということまでになったので、そもそもやること盛りだくさんなのですが、本腰入れて調べました。
今回のシステムでは、大きく分けて2つのプログラムが連携して処理を行う為、まずは個々のプロセスの単独でのスループットを測定してみました。片方のプログラムは単独では使えないプログラムだったので、無理矢理ソースを変更して、単独で使えるバージョンを作り、それで測定しました。最初は単独で使えない方のプログラムがそもそも遅い処理が多いのが問題だろうと想像していたのですが、単独で動かしてみると意外と高速だったのです。
そうすると、親側のプログラムでデータを渡す部分が遅いのだろうということで、その部分の処理を部分的にコメントアウトしながら測定し、実はNAT(ネットワークアドレス変換)処理を無理矢理すっ飛ばすと速いということが分かりました。
NAT処理のソースを再確認すると、結構ログ出力関数を呼び出していることが分かりました。ログ出力は閾値で出すレベルを変えることができるのですが、そもそも1パケットごとにNAT処理を行うくらいの頻度では、関数を呼び出したり、文字列に成形したりすること自体でも影響が出てしまいます。まずは正常系のログ出力をコメントアウトしました。これだけでかなりスピードUPです。
さらに、NAT処理では検索にハッシュ検索を使っているのですが、ハッシュのキーとして、一度IPアドレスなどを文字列化して使っていました。そうすると、ハッシュに格納時はもちろん、検索時にも毎回文字列化をしないと検索できないので、それも頻度が高いので影響が大きいだろうと、キーをバイナリのまま使うように改良しました。
この2つの変更で、ほぼ2倍に高速化ができたという感じです。遅いと思っていたプログラムの方ではなく、親プログラムの方を直して速くなったという感じです。
プロファイラは性能向上にはとても便利な数値を出してくれるのですが、今回のケースでは、チェックサムなどさらに目立つ処理が上位に出てきていてNATには注目できなかったことや、そもそも2つのプログラムのどちらがボトルネックになっているのかも判断しにくかったのでした。もちろん、この段階に来るまでにすでにプロファイラの結果で高速化していたということもあります。
今どきのCPUは並列度が高く、今回のターゲットCPUでも12スレッドのCPUで、なかなかCPUの様子を観察していても、理解が難しいものです。CPUを明示して動かしてみたりもしましたが、速くなったり遅くなったりと、他の要因も関係するのでなかなか判断しにくく、一般用OSではもう無理かとも考えました。
結局は、基本に戻り、個別に実際に性能測定をしながら観察して、ボトルネックを切り分けられたということで、あらためて負荷テストの効果と、実際に動かしながら切り分けることの大切さを痛感したという感じです。
それと・・・尻に火がつかないとなかなか本腰が入らないというのも、毎度のことです。。まあ、人間の頭なんてそんなものでしょう。とはいえ、できると思って着手したものに対して、途中で「やっぱり無理」とは言えないものです。周りも巻き込んでいますし、意地でも解決しなければなりません。ということを考えると、余程好きな分野でないと、チャレンジングな開発は怖いと思いますね。