Entry

プログラミングメモ - bswap 命令についてちと検証した

2010年03月09日

先日,バイトオーダーの入れ替えには CPU のインストラクションを直接呼ぶのが普通,とか書いたんですけれど,少し気になる話を読ませてもらったのでメモ。悪口じゃないです。念のため。

意外にもアセンブラよりもビット演算で実装したほうが速い。最初のポインタ使うのはかなり遅い。

バイトスワップの話 - くまったうの日記

当のインストラクションである bswap は,1サイクル(クロック)で済んでしまう命令なので,命令自体のコストは論理演算よりもはるかに少ないはず。どうしてそうなるのか,ちょっと理由が思いつかなかったので,テストコードをお借りして手元でも試してみました。アセンブリが GNU 形式で書かれていたので,処理系は GNU g++ (MinGW)。その他のスペックは以下を参照してください。

  • OS : Windows xp sp3 32 bit
  • CPU: AMD Athron(tm) 64 Processor 3200+ (2.0 GHz)
  • RAM: 3.06 GB (実装 4.0 GB)

3回試した結果を貼り付けます。元記事さんの方のコンパイルオプションには最適化オプション(-O1)が付いていたけれども,こいつは外さないとどこで速くなったか分かりづらくなっちゃうので,とりあえず外したまま計測。

>g++ -v
(snip)
gcc version 3.4.5 (mingw-vista special r3)

>g++ bswap.cpp -o bswap -Wall -W

>bswap
bswap(pointer)      = 6.562000
bswap(bit operator) = 4.672000
bswap(assembler)    = 2.187000

>bswap
bswap(pointer)      = 6.562000
bswap(bit operator) = 4.672000
bswap(assembler)    = 2.187000

>bswap
bswap(pointer)      = 6.609000
bswap(bit operator) = 4.703000
bswap(assembler)    = 2.219000

この環境だと,論理操作を行ったものより bswap を使ったものの方が約2.1倍高速。うーん。再現しない。

続いて,引用記事さんの通り,最適化オプション(-O1)を付けたものを計測。

>g++ bswap.cpp -o bswap -Wall -W -O1

>bswap
bswap(pointer)      = 3.625000
bswap(bit operator) = 1.828000
bswap(assembler)    = 1.515000

>bswap
bswap(pointer)      = 3.625000
bswap(bit operator) = 1.843000
bswap(assembler)    = 1.516000

>bswap
bswap(pointer)      = 3.625000
bswap(bit operator) = 1.828000
bswap(assembler)    = 1.515000

やはり,bswap を使ったものの方が速い。ただし,速度比は約1.2倍とかなり落ちます。さらに,調子に乗って O2 で実行した結果は以下の通りです。

>g++ bswap.cpp -o bswap -Wall -W -O2

>bswap
bswap(pointer)      = 3.109000
bswap(bit operator) = 1.578000
bswap(assembler)    = 0.359000

>bswap
bswap(pointer)      = 3.093000
bswap(bit operator) = 1.578000
bswap(assembler)    = 0.360000

>bswap
bswap(pointer)      = 3.093000
bswap(bit operator) = 1.578000
bswap(assembler)    = 0.360000

速度比が4.5倍に広がりました。うーん……。

念のため,AMD 系の CPU が特に速いのかもしれないので,他のマシンでも試してみました。これは O2 オプションで作ったものだけ。スペックはこんなやつ。

  • OS : Windows xp sp3 32 bit
  • CPU: Intel Xeon E5520 (2.26 GHz) x 2 (8 Cores) + Hyper-Threading
  • RAM: 3.06 GB (実装 4.0 GB)
>bswap
bswap(pointer)      = 1.093000
bswap(bit operator) = 0.985000
bswap(assembler)    = 0.218000

>bswap
bswap(pointer)      = 1.062000
bswap(bit operator) = 1.000000
bswap(assembler)    = 0.219000

>bswap
bswap(pointer)      = 1.078000
bswap(bit operator) = 1.000000
bswap(assembler)    = 0.218000

スレッドは関係ないので,コアの数はどうでもいいんですけれど,やはり結果は速度比において AMD 版で動かした場合とほとんど同じ。むふぅ。むしろ,Intel 系の CPU では,ポインタ操作も論理操作も同じくらいのパフォーマンスなのが気になるところです。キャッシュによく当たっているからだろうか。

ともあれ,総じて見ると,手元の環境に関する限り bswap は十分速いといえそうです。テストコードについてちゃんと調べるとなると,インライン展開の有無やループの展開(カウンタの最適化)等々といった,最適化のポイントも踏まえる必要があるので,生成されるコード(アセンブリ)も見る必要があります。ただ,今回はパス。あたしんとこの環境では,bswap の方が遅くなるケースを再現できませんでした。ま,パフォーマンスもさることながら,分かりやすさ(可読性)の点でも,bswap 使っといた方がいいんでねいかな,と。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN