Go言語で画像縮小できるライブラリのベンチマーク

Go言語で画像縮小できるライブラリのベンチマークなどを取ろうと思って少し書いてみた。
https://github.com/oov/resize-test

この辺りのライブラリは画像の縮小ができるので、どれも func(src image.Image, w int, h int) (image.Image, error) で呼べるよう整理して、それを元にしたベンチマークのジェネレータとかサンプル画像出力プログラムを作った。

テスト環境は Intel Atom D510 で、物理コア数2でハイパースレッディング対応。遅い。
使用したGo言語環境は go1.3.1 の linux/amd64 版。
これはベンチマーク結果を Google ドキュメントでグラフ化したもの。画像を別窓で開く
amd64 用に最適化されている rez はかなり速くて、速度と品質のバランスを見て unsafe なコードが使える環境の場合は rez の Bicubic や Lanczos が良さそうに思える。

unsafe が使えない場合は同じレベルの速度で動くのは品質が NearestNeighbor 相当のものしかなく、それを避けると nfnt/resize の Bilinear、そして nfnt/resize の他のアルゴリズムや gift / imaging の Box という感じで続いていく。
nfnt/resize は全体的にパフォーマンスがいいが、出力品質と GOMAXPROCS=1 の時のパフォーマンスなどを見ると gift や imaging の Box が今回のサンプル画像においてはかなり健闘していながら速度もなかなか速いので、個人的に使いたいのはこれかなぁという感じがした。

テスト環境の物理コア数と一致する GOMAXPROCS=2 の時は大体のライブラリで速度が2倍近くなり、GOMAXPROCS=4 ではそこまで伸びないのもあるがそこそこ速くなり納得できる結果に。
一方で moustachio/​resize はおそらく元々 GAE 用のサンプルということもあって並列化されていないため GOMAXPROCS に関わらず速度は変わらず、graphics-go/graphics もまた変わらなかった。

これは余談だけど moustachio/​resize のルーチンはGo言語にまだこれといった画像縮小ライブラリが無かった時代に重宝されていたように思うが、使いやすい場所にライブラリが配置されていなかったこともあり fork ではなくコピーされたファイルがいくつかのリポジトリにあり紛らわしいのが探すとき地味にだるい。

そのうち go.image/draw が追加されそうなので、これが追加されてコミットが落ち着いた頃にでもベンチマークに追加しようと思う。

個人的なまとめ
  • rez 良い
  • gift や imaging の Box がその次ぐらいに好み
  • nfnt/resize も良い
  • 選べるぐらいに選択肢が増えて嬉しい
  • ImageMagick とか cgo 経由するようなのとかは面倒なので試してない
2014-08-16 追記

画像を別窓で開く
大変見づらくなってしまっているが、需要がありそうな ImageMagick(convert コマンド) もベンチマークに追加するために色々と変更した。

前回のプログラムでは
  1. 画像のデコード
  2. 画像の縮小
  3. 画像のエンコード
上記の3つのステップのうち、2の段階のみを純粋に計測していた。

ImageMagick が加わる場合これまでとは違い画像をデコード/エンコードする処理自体も使用されるコードが異なる上これらを切り離して計測するのは難しく需要も薄いと思われるので、公平な結果を簡単に出力できる
  1. 画像のデコード
  2. 画像の縮小
  3. 画像のエンコード
この3つのステップ全てをまとめたベンチマークを追加し、ベンチマークのメソッド名を
  • 縮小処理のみを計測しているものを BenchmarkScaler*
  • 一連の流れをまとめて計測したものを BenchmarkFlow*
という名前で統一した。

計測用のマシンに前回使用した Intel Atom D510 go1.3.1 linux/amd64 の環境を今回使うとベンチマーク完了まで20分強の時間を要してしまい辛くなってきたので、環境を変えて Intel Core i7 4790K go1.3.1 windows/amd64 で計測した。この CPU は物理コア数4でハイパースレッディング対応なので GOMAXPROCS=8 の結果も追加している。
また、今回使用した ImageMagick は chocolatey 経由でインストールしたもの。

早速 BenchmarkFlow* 辺りのスコアを見ていくと、前回速かったニアレストネイバー系や rez のパフォーマンスが BenchmarkScaler* に比べるとかなり落ち込んでいる。これはテストに使っている test.jpg が 4912x3264 で 4.68MB ぐらいある少し大きいファイルであることも原因の一端を担っていると思う(一端どころではない気もする)。

ともかく、この増えた分を差し引けば BenchmarkScaler* の方と関係性はほとんど変わらないので、後は ImageMagick との比較になる。今回 ImageMagick の縮小方法は -define ありとなしの2種類を定義していて、-define ありの方は JPEG 画像で縮小後のサイズが予め判明している場合に処理速度が速くなる。

フィルタ指定を特にしていない時 ImageMagick は Lanczos のようなので、それも踏まえて見てみると -define なしの方に関しては rez の Lanczos3 が結構いいところまで迫ってきている。
一方 -define ありの方はもうなんか他の追随を許さない勢いでとても速い。

まとめ2

今回選んだ画像がかなり大きいせいか、デコードに掛かる所要時間で勝負の大半がついてしまった感じがある。それでも -define なしの時と比べた限りではGo言語で書かれた JPEG デコーダや rez の縮小処理は決して遅くないので、今後同じようなアプローチで高速化されたGo言語版の JPEG デコーダなどが登場すると更に差が縮まりそうで楽しみになった。