バンディング低減MT 高速化版+7

高速化ネタを思いついたので2年ぶり(たぶん)に更新。


だいぶ前、バンディング低減フィルタをSIMDを使って高速化した。だいぶんと速くなったのでもういいかなと思っていたのだけど、さらに速くするネタを思いついたので更新。

だいたい1.3~1.8倍ぐらい速い。



画像処理系のフィルタは1要素のサイズが小さいのもあって、整数SIMD化するとだいぶ速くなる。そうすると、だんだん相対的に演算よりメモリアクセスのほうが重い、という感じになってきて、速度があげにくくなってしまう。下手をすると演算を増やしてでもメモリアクセスを減らしたほうがいいなどという、なにをやっているのか意味の分からない状態になってしまう。(実際バンディング低減の高速化はそんな感じ)

こういう状態の中でさらに高速化しようとすると、もはや演算の中身はどうでもよくて、処理中のメモリアクセスのパターンを把握して、なんとかそれを減らせないか、もしくはキャッシュヒットさせるかということが大切になる。

バンディング低減フィルタのsample=1の時、あるピクセルの処理に必要なデータは下の図のような感じで、自分のピクセル(オレンジ)と、rangeの範囲内でランダムに決まる2ピクセル(ピンク)となっている。

bandingMT_simd_block_01.png

フレームのピクセルデータのメモリ上の並びは、横方向にまずは並べていく方式なので、縦方向にずれているピクセルはメモリ上では非常に遠い。このため、参照範囲が縦方向にかなり広い可能性がある(ここでは上下にそれぞれrange分だけ離れている可能性がある)というのはかなり厄介になる。

これまでのバンディング低減フィルタの処理は、フレームを下の図のように分割したうえで各スレッドに割り当て、各スレッドはフレームデータのメモリ上の並びに沿って、まずフレームを横方向に処理していって、1行終了したらもとに戻って次の行の処理を始めるというものだった。

bandingMT_simd_block_02.png

この処理の順序では、横方向に広い範囲のデータを一気に処理するため、キャッシュにも横方向に広い範囲のデータを取り込んでしまう。AviutlのYC48はわりかし重めのフォーマットなので、fullHDだと1行が約12KBにもなる。HyperThreadだとすると1スレッドが使えるのは半分だとして、L1キャッシュ(32KB)には1行分程度しか乗らないのでまあ期待しないとしても、L2(256KB)にも10行程度しか乗らない。一方、rangeは30などに設定することが多いので、上下の全60ライン分の範囲が参照される可能性があり、これに対してL2のキャッシュ量256KB/2=10行分というのはけっこう足りないことが分かる。このため、range内のピクセルをランダムに参照するとき、L2キャッシュミスしてしまう確率がかなり高いんじゃないかなあ、ということになる。

そこで、処理順を変更することを考える。

フレームを処理する際に、一気に横方向に処理してしまうのではなく、フレームをいくつかのブロックに分解し、ブロックごとに処理していくことを考える。こうすると、キャッシュに取り込む横方向のデータ量を抑えられ、縦(行)方向のデータもある程度キャッシュに載せておけるようになるかもしれない。ブロック処理中にはブロック周辺のデータがキャッシュにあるので、L2あたりのキャッシュヒット率が上がるかなあ、と期待できる。L1はちょっと少なすぎるので、ここではまあ、L3ヒットになってしまっていたものがL2ヒットになればいいかなあ、ぐらいのイメージ。

横を8分割、縦を5分割する例。
bandingMT_simd_block_03.png

このとき、実際にどのくらいのブロックサイズが良いかはよくわからないので、実際にブロックサイズを変えて、fullHDのフレームの平均処理時間を計ってみた。CPUによって効果が変わる可能性があるので、いくつかのCPUで測定してみた。

比較した環境はこんな感じ。

CPUi7 5960Xi7 6700Ki7 4770Ki7 4610YN3150
コア数8C/16T4C/8T4C/8T2C/4T4C/4T
CPU世代HaswellSkylakeHaswellHaswellAirmont
Coreクロック4.2GHz4.3GHz3.8GHz1.2-2.9GHz2.08GHz
Uncoreクロック3.6GHz4.1GHz3.8GHz?-
L1キャッシュ32KBx832KBx432KBx432KBx224KBx4
L2キャッシュ256KBx8256KBx4256KBx4256KBx21MBx2
L3キャッシュ20MB8MB8MB4MB-
メモリDDR4-2400DDR4-2933DDR3-2400DDR3-1600DDR3-1600
OSWin10 x64Win10 x64Win10 x64Win10 x64Win10 x64


この環境で、縦横それぞれのブロックの数を変えながら、fullHD (1920x1080)のフレームを処理したときの平均処理時間(単位:ms)を図って、エクセルで表にしてみた。

まず i7 5960X。赤枠のところが従来のバージョンで、横方向にはブロック分割せず、縦方向にはスレッド数分分割するというもの。横方向の分割数を増やしていくと、あるところまでは高速化していくことが分かる。縦方向の分割数はスレッド数以上には増やさないほうがいいみたい。

i7 5960X 1080p 1フレームの平均処理速度 (単位:ms)
bandingMT_simd_block_04.png


次に6700K。赤枠のところが従来のバージョン。やはり、横方向の分割数を増やしていくと、あるところまでは高速化する。従来(赤枠)と比べると、高速化率はかなり大きい。

i7 6700K 1080p 1フレームの平均処理速度 (単位:ms)
bandingMT_simd_block_05.png

次に4770K。やはり、横方向の分割数を増やしていくと、あるところまでは高速化する。これも従来(赤枠)と比べると高速化率はかなり大きい。

i7 4770K 1080p 1フレームの平均処理速度 (単位:ms)
bandingMT_simd_block_06.png

デュアルコアもちょっと試そうということで4710Y。ところが、やっぱりタブレットなので、どうしても熱の制約にひっかるらしく、結構測定が不安定な様子。それでもなんとなく傾向は見える。

i7 4710Y 1080p 1フレームの平均処理速度 (単位:ms)
bandingMT_simd_block_07.png

最後にCeleron N3150。横方向の分割数を増やしていくと、高速化していくのだが、これまでとはだいぶグラフの傾向が異なっている。というかものすごく遅い…。

Celeron N3150 1080p 1フレームの平均処理速度 (単位:ms)
bandingMT_simd_block_08.png

というわけで、あまり分割数を増やしすぎてもよくないが、横方向の分割数を16ぐらいまで上げるとだいたいどのCPUでもいい感じかな、ということが分かる。縦方向の分割数は並列スレッド数分だけあるといいようだ。

最終的には、解像度がfullHD以外でもうまくいくように、横方向のブロック分割は128ピクセルの大きさ(fullHDだと15分割)、縦方向のブロック分割数はスレッド数分、という風にした。

最後に今回のfullHDでの処理時間(単位:ms)と高速化率をまとめるとこんな感じ。

i7 5960Xi7 6700Ki7 4770Ki7 4610YN3150
高速化版+61.913.434.1530.0361.63
高速化版+71.511.902.5324.6024.48
高速化率x 1.27x 1.80x 1.64x 1.22x 2.52


6700Kだと1.8倍とかになっていて、それなりに高速化できている。ただ、fullHDより小さい解像度では、ここまで大きな効果は出ない。


というわけで、メモリアクセスのパターンを考慮して、すこし処理順を変えてあげると、キャッシュミスが減ったりで結構速度が変わったりすることがわかった。たぶんL3ヒットがL2ヒットになっただけだと思うんだけど、それでもそれなりに効果はあるようだ。

と、ここまで、高速化した過程を書いてきたのだけど、読み返すと、なんか延々とキャッシュがどうのこうのという話をするという、異常にマニアックな記事になってしまった。申し訳ない…。



バンディング低減 ダウンロード>>
ダウンロード (ミラー) >>
OneDriveの調子がいまいちの時はミラー(GDrive)からどうぞ。同じものです。

ソースはこちら


スポンサーサイト

コメントの投稿

非公開コメント

毎度御礼申し上げます

AviUtl用の各種フィルタを開発・配布されている個人サイトさんは数多く存在していますが、いま現在でも精力的にフィルタのアップデートを続けていらっしゃる方は rigaya さん以外あまり居られなように思います。

わたしなどが今更申し上げるまでもありませんが、「 AviUtl から AVC/HEVC な mp4 をエンコードするために欠かせない .auo プラグイン各種 」に至っては、世界中でこちらからしか手に入らないため、その点においても感謝に堪えません。

例えば、某う◯◯んさんが公開されている AviUtil 用フィルタは、手軽に実用的なフィルタを追加できるため個人的に重宝しているのですが、数年前から更新がパっタリ止まっていらっしゃるため「 最新のコンパイラにソースコードを対応させることができたら、実行速度が上がるかもしれないなあ… 」とボンヤリ頭をよぎるものの、最新 ver. のコンパイラにソースコードを適応させるだけのコーディング知識等が皆無な自分を恨めしく自嘲するばかりです。

Visual Sutdio 用のソースコードなら、開発バージョンがかなり前のものでも自動的に処置してくれるのに… (>_< ;)

Re: 毎度御礼申し上げます

ああ、そういっていただけるとありがたいです。

最近はソース自体の高画質化が進んだためか、ノイズ除去などのフィルタ類の必要性はだいぶ薄れているように思います。

以前はわたしもいろいろな方の作ってくださったフィルタを使ってみたりしていましたが、最近はそういったことをせず、だいたい決まったフィルタ(afs,リサイズ,エッジレベル,バンディング低減)のみをかけてx264/x265でエンコードする、という感じになってしまっています。

プロフィール

Author:rigaya
アニメとか見たり、エンコードしたり。
連絡先(@を半角にしてください!)
rigaya34589@live.jp
github

最新記事
最新コメント
カテゴリ
月別アーカイブ
カウンター
検索フォーム
いろいろ
公開中のAviutlプラグインとかのダウンロード

○Aviutlプラグイン
x264guiEx 2.xx (ミラー)
- x264を使用したH264出力
- x264guiExの導入>
- x264.exeはこちら>

x265guiEx (ミラー)
- x265を使用したH.265/HEVC出力
- x265.exeはこちら>

QSVEnc + QSVEncC (ミラー)
- QuickSyncVideoによるH264出力
- QSVEncCはコマンドライン版
- QSVEncC 導入/使用方法>
- QSVEncCオプション一覧>

NVEnc + NVEncC (ミラー)
- NVIDIAのNVEncによるH264出力
- NVEncCオプション一覧>

VCEEnc + VCEEncC (ミラー)
- AMDのVCEによるH.264出力

ffmpegOut (ミラー)
- ffmpeg/avconvを使用した出力

自動フィールドシフト (ミラー)
- SSE2~AVX2による高速化版
- オリジナル: aji様

エッジレベル調整MT (ミラー)
- エッジレベル調整の並列化/高速化
- SSE2~AVX対応
- オリジナル: まじぽか太郎様

バンディング低減MT (ミラー)
- SSE2~AVX2による高速化版
- オリジナル: まじぽか太郎様

PMD_MT (ミラー)
- SSE2~FMA3による高速化版
- オリジナル: スレ48≫989氏

透過性ロゴ (ミラー)
- SSE2~FMA3によるSIMD版
- オリジナル: MakKi氏

AviutlColor (ミラー)
- BT.2020nc向け色変換プラグイン
- BT.709/BT.601向けも同梱

○その他
x264afs (ミラー)
- x264のafs対応版

aui_indexer (ミラー使い方>)
- lsmashinput.aui/m2v.auiの
 インデックス事前・一括生成

auc_export (ミラー使い方>)
- Aviutl Controlの
 エクスポートプラグイン版
 エクスポートをコマンドから

aup_reseter (ミラー)
- aupプロジェクトファイルの
 終了フラグを一括リセット

CheckBitrate (ミラー, 使い方, ソース)
- ビットレート分布の分析(HEVC対応)

チャプター変換 (ミラー使い方>)
- nero/appleチャプター形式変換

エッジレベル調整 (avisynth)
- Avisynth用エッジレベル調整

メモリ・キャッシュ速度測定
- スレッド数を変えて測定

○ビルドしたものとか
L-SMASH (ミラー)
x264 (ミラー)
x265 (ミラー)

○その他
サンプル動画
その他

○読みもの (ミラー)
Aviutl/x264guiExの色変換
動画関連ダウンロードリンク集
簡易インストーラの概要

○更新停止・公開終了
改造版x264gui
x264guiEx 0.xx
RSSリンクの表示
リンク
QRコード
QR