2014年8月31日日曜日

ベクトル命令での画像処理

最近、画像処理をintrinsicで書いてる。
その話をちょっと書いてみようかと。
対象はAVXやAVX2、AVX512ね。

画像のフィルター処理だと、例えば3×3のマス目にblurマスクを掛けるとか。
ここでは単純に前後3マスの合計値としましょうか。

Cだと、
D[X] = S[x] + S[x+1] + S[x+2];
なんて書くわけですが、AVXでこれをこのまま書くと、
__m256 v1 = _mm256_loadu_ps(&S[x]);
__m256 v2 = _mm256_loadu_ps(&S[x+1]);
__m256 v3 = _mm256_loadu_ps(&S[x+2]);
__m256 v = _mm256_add_ps(_mm256_add_ps(v1, v2), v3);
_mm256_storeu_ps(&D[x], v);
みたいになっちゃって、ちょっとロードが勿体なくない?とかなるわけです。
これを、
__m256 s00, s08;
s08 = _mm256_loadu_ps(&S[xb]);
for (x = xb; x < xe; ++x) {
    s00 = s08;
    s08 = _mm256_loadu_ps(&S[x+8]);
    __m256 v1 = s00;
    __m256 v2 = s00とs08を合わせて4バイトシフト
    __m256 v3 = s00とs08を合わせて8バイトシフト
    __m256 v = _mm256_add_ps(_mm256_add_ps(v1, v2), v3);
    _mm256_storeu_ps(&D[x], v);
}
なんてできれば良い感じになるじゃないですか。
これがフィルター処理の基本形ですかね。

3×3など複数行をまたぐ処理の場合は、これをs0_00, s1_00, s2_00みたいに、
複数行をベクトルレジスタに読んでおいて、似たような処理を行います。

こうやってintrinsicで書いても早くならない関数もありますが、
大体は早くなりますし、最近intrinsicで書くのに慣れてきたので、
最初からintrinsicで書いてもいいかなとか思い始めてたりw

2014年8月8日金曜日

TAU、PAPI紹介

最近、お仕事でよくTAUを使ってる。

思い返すと10年以上前、イリノイにいたころSCに行って、大学の研究室がCD-ROMでPAPI配ってるの貰って有難う使ってみるとかいってたのが、そういうパフォーマンス計測ツールとのファースト・コンタクト。

PAPIはそれなりに世間では利用されるようになったのだけど、個人的には自前でプローブ埋めるの面倒だし……と使わずにいました。でも、去年あたりから、適当なチューニングだと、試行錯誤ばっか増えて、全然効率良くならないし、計測ツールも使ってみるかと使い始めた所。

以下、簡単にTAUとPAPIの説明。
  • PAPIは計測ツールそのもの
  • TAUは計測ツールを使うためのsuiteや環境
例えば、PAPIを使うには、ユーザが最初に計測するHWカウンターを登録してやり、それから計測したい関数の最初と最後にSTART/STOPを埋め込む必要がある。また得た値を自前で集計する必要もある。

一方、TAUを使うには、計測したい範囲にSTART/STOPを埋め込む。後は、実行時に環境変数で何を測るか指定するだけ。結果は勝手にファイルに書きだされる。

自由度は多少低いのだけど、使いやすさだけを考えると、TAUの圧勝という感じ。まあ、TAUは、PAPIや他のライブラリなんやツールなんかを、統合して使うための環境みたいな物なので、当然と言えば当然なんですけどね。

TAUを使うと、埋め込んだSTART/STOP間について、環境変数を弄るだけで、コンパイルしなおしもなしに、以下の値が計測できます。これは便利w
  • 実行時間(積算)
  • 実行時のHWカウンター(積算)
  • START/STOPの実行時刻(個別)
そういうわけで、HWカウンターなど見ずにチューニングしてる方には、TAU&PAPI、お薦めです。