naoki86star

インターネットの片隅でなにかしら書いてみる

sflowtool のコードをいじってみる

 sflowtoolにはtcpdumpで保存したpcapから読み込むオプションあります。個人的にはこれが非常に便利と思っていてよく使います。pcapファイルは多くの場合サイズが大きくなるので残そうとするなら圧縮しておくこと多いと思います。なのでこんなコマンドラインを多く打つ・書くことになります。

bzip2 -dc hoge.pcap.bz2 | sflowtool -r - -l

 それほど強い動機はないのですけども、bzip2 コマンドを省略して、sflowtoolの中でbz2をdecompressしてsflowtoolだけでpcap.bz2を読めないかやってみたです。bz2はlibbzip2ライブラリ(aptのパッケージならlibbz2-dev)で圧縮展開できるみたいです。この場合はファイルからの展開だけできれば目的が達成できます。

 まず普通(?)にfreadの部分とかの置き換えでやってみたらbzip2コマンドパイプで呼び出すよりすこーし時間かかるようになってしまいました。はい、マルチCPUのPCで作業したのですが、bzip2コマンドパイプ呼び出しはいい感じで展開とsflowtoolの処理がCPU分散していたようです。

 コードの断片が無駄になってしまうので、展開部分をマルチスレッド化して試してみます。結果としては、bzip2コマンドパイプのときと同等な速度で動いてくれるようになりました。*1

 結局、多分ファイルリードの時間が支配的なのでbzip2コマンドパイプを圧倒的に超える速さは難しいんだろうと思いつつ、いろいろ見てみます。
まずpcap.bz2からの読み込みとsflowtool本体の処理を平行にするので、pcap.bz2を展開したバッファが必要です。バッファの塊を複数(2つ)の(一応)リングバッファ構成でpcap生データを書き込む領域とsflowtoolの読み込む領域を重ならないようにしました。

struct {
    uint8_t data[BUFFER_LENGTH];
    size_t length;
    int status;
} buffer[NUMBER];
enum {
 EMPTY,
 LOADED,
 CLOSED,
}

 バッファの塊ごとにEMPTY/LOADEDのように状態を付けて、EMPTYバッファにpcap生データを書き込む・読み込んだらEMPTYに戻す方針です。

 pthread_cond変数を使うかどうか考えてみましたが、2スレッドだけなので、状態変数(今回のはbuffer.status)の判定だけでいけるかと考えてみて、
とりあえずビジーループしてみました。最初の失敗は、元Makefileにはコンパイルで-O2ついていたせいか状態変数を書き換えても別スレッドで新しい値を参照してくれない、ことが起きてました。これはデッドロックとは呼ばないとは思いますが、似たようなものでした。
 いくらかの方法を試してみましたがビジーループのまま多分buffer.statusにvolatileを付けるのが一番妥当ぽいと考えました。

  • ビジーループ内でsleep(0);でスレッドスケジュールを逃がす
  • ビジーループ内でpthread_yeild();でスレッドスケジュールを逃がす
  • ビジーループ内でasm("nop");を挟む

sleep(0);は自分の環境では安定して動作してくれました。pthread_yeild()と等価でないかと思うのですが、pthread_yeild()は単に一旦スケジュールが最低に落ちてでも2スレッドしかいないのですぐ戻ってくるのかと。sleep(0)だと再スケジュール時にシグナルで通知されているのかもしれない。asm("nop");を挟んでよくなるのはとりも直さずミクロなタイミング依存であることを示唆していて、sleep(0)を含めて多分なにがしかよろしくない方法でないかと考えます。原本を強制参照させるvolatileの意義はこういうことろにあるのかと改めて思いました。
volatile なぜか存在は20年以上前から知ってましたけども、使って効いたというのは初めてです。組み込みでコンパイラの仕様というかクセみたいなのがあるときには気になってくるんだろうなと思いました。

今回の結果はこれ。追加したものの構成は手抜きで*.hで拡張分を追加している。ローカル変数とかstaticで隠ぺいしようとして隠れていない。

*1:ファイルが大きければ若干秒単位の差を得ることができました。。。