SIMDと画像処理
画像処理(ここでは、ビットマップ画像のみに意図的に制限し、ベクター画像の幅広いクラスを省略します)は、原則として、画像の各ポイントに適用される簡単な操作のセットです。 イメージポイント(ピクセル)を構成するカラーチャネルが通常小さな次元の整数として表されることを考慮すると、画像処理は1〜2バイトの整数で同じタイプの膨大な数の操作に削減されます。
汎用プロセッサ命令を使用してこのような操作を実行することは、4〜8バイトの数値を処理するために最適化されているため、完全に効果的ではありません。 実際、1バイトの数値で作業する場合、その有効性は4〜8倍低下します。 プロセッサの製造元は、このようなデータ(SIMD(単一命令、複数データ))を扱う際の生産性を向上させるためのかなり効果的なソリューションを長い間提案してきました。 x86プラットフォームを検討すると、MMD、3DNow!、SSE、SSE2、SSE3、SSEE3、SSE4.1、SSE4.2、AVXおよびAVX2という多数のSIMD拡張が存在します。 これらの拡張機能のほとんどは、実数のベクトル処理用です。 ベクトル整数命令に触れると、MMX、SSE2、およびAVX2コマンドのセットを区別できます。これらのコマンドには、それぞれ8、16、および32バイトのベクトルを操作する操作が含まれています。 MMX拡張は、1997年にPentium MMXプロセッサに初めて登場し、現在歴史的な関心を集めています。最新のx86プロセッサはすべて、2000年にリリースされたIntel Pentium 4プロセッサで初めて導入されたより高度なSSE2命令セットをサポートしているためです。 しかし、人生はまだ止まっておらず、2013年にAVX2命令をサポートするプロセッサの第4世代iCoreファミリがリリースされ、まもなく普及します。
したがって、画像処理を実装するC ++で記述された何らかのアルゴリズムがあります。このアプリケーションの速度は重要です(このようなアルゴリズムは、ビデオを処理するときによく見られます)。 アルゴリズムの速度を10倍以上加速する可能性のあるSIMDプロセッサ拡張の存在を考えると、何らかの方法でそれらを使用しようとするのは論理的です。 それで、それがいかに簡単かという疑問が生じますか? 以下では、開発者が自分のプロジェクトでSIMDプロセッサ拡張機能を使用するプロセスで直面しなければならないすべての長所と短所を詳細にリストし、それに答えようとします。
長所
- SIMD命令を使用すると、複数のソースデータセットを同時に処理できるため、アルゴリズムの全体的な時間が大幅に短縮されます。
- 拡張命令セットには、いくつかの汎用命令を置き換える特殊なコマンド(たとえば、数値のペアの平均または最大数の検索、飽和による加算または減算)が含まれています。 これらのコマンドを使用すると、速度がさらに大幅に向上します。
- 一般的な誤解に反して、SIMDプロセッサ拡張を使用するために、アセンブラを使用する必要はありません。 最新のすべてのC ++コンパイラは、コンパイラがCPUのコマンドに直接変換する、いわゆる組み込み関数をサポートしています。 したがって、SIMDを使用すると、使い慣れたIDEのC / C ++構文のフレームワーク内に完全にとどまることができます。
短所
- プロセッサ拡張を伴うコードの汎用性を失う。 実際、多数のプロセッサアーキテクチャがあり、それぞれに独自のベクトル命令セットが含まれる場合と含まれない場合があります。 したがって、プログラムの適用可能性を特定のアーキテクチャに制限するか、必要なアーキテクチャごとにアルゴリズムの実装をいくつか作成する必要があります。
- 入力データと出力データは、規則正しい方法でメモリ内に存在する必要があります(2番目または4番目ごとの要素を非常に効率的に抽出することは可能ですが、連続することが好ましい)。 入力データと出力データの密度が高いほど、実行する必要のある並べ替え操作が少なくなり、アルゴリズムの効率が上がります。 この要件により、データを保存する方法を再検討する必要があります。たとえば、カラーイメージを個別のカラープレーンのセットの形で保存し、各ポイントのカラーチャネルが交互に変わる単一のプレーンではありません(BGR-24のように)。
- 入力データと出力データはメモリ内で整列されることが望ましく(SSE2の場合は16バイト境界、AVX2の場合は32バイト境界)、整列されたデータの読み取りと保存がはるかに高速になります。 入力データと出力データの作成を制御する場合、特別なメモリアロケーターを使用してそれらのアライメントを実現します。 それ以外の場合、アルゴリズムの速度の低下を受け入れるか、アライメントされたデータまたはアライメントされていないデータの場合にアルゴリズムの2つのバージョンを記述する必要があります。
- 汎用レジスターで画像を処理するときに、計算プロセスで数値がオーバーフローする問題がほとんど発生しない場合(8ビットデータを処理するときにオーバーフローする可能性のない標準のintタイプを使用します)、ベクトル命令を使用して処理するときは、この問題を常に保持する必要があります制御下にあります。 たとえば、アルゴリズムで、計算プロセス中に8ビットデータのオーバーフローが発生する可能性がある場合、それらを16ビットに変換し、計算を実行し、逆変換を行う必要があります。 もちろん、これにはベクターをパックおよびアンパックするための特別なベクトル命令がありますが、これはすべてアルゴリズムを遅くし、16ビットデータのベクトル命令は8ビットのものに比べて一度に2倍少ないデータを処理するという事実があります。
- ベクトル命令を処理する場合、各要素のコマンドの条件付き実行の可能性はありません。 場合によっては、条件付きジャンプをエミュレートできます。そのためには、コードの両方のブランチを実行し、それらの作業の結果を結合する必要があります。 これは当然アルゴリズムのパフォーマンスに影響し、コードが条件付き遷移で飽和すると、SIMD最適化の意味が失われます。
- 画像を処理する場合、通常、境界にあるポイントは特別な方法で処理されます。 ベクトル命令の場合、点の境界ブロックでは、点の一部が一般的なアルゴリズムに従って処理され、一部が境界点のアルゴリズムに従って処理されるため、開発が非常に複雑になるため、問題は複雑です。
- 画像の幅は、ベクトルのサイズの倍数であるとは限りません。 したがって、任意の幅の画像を処理するには、部分的にしか塗りつぶせない行の最後のブロックを特別に処理する必要があります。
- スカラーレジスタで使用できるすべての数学演算に、ベクトル命令の独自の類似物があるわけではありません。 たとえば、整数除算はありません。 これらの場合、ベクトル化を拒否するか、利用可能な命令を使用してそれらをエミュレートする必要があります。これにより、効率が低下し、コードが複雑になります。
- 一般に、ベクトル命令を含むコードの可読性は、スカラーコードの可読性よりも著しく劣ります。
- プログラマーはスカラー値全体ではなくベクター全体の正確さを追跡する必要があるため、ベクター命令を含むコードのデバッグははるかに困難です。 また、最新のIDEは、この分野で大きな進歩があったにもかかわらず、スカラーよりもはるかに悪いベクトルデータを表示します。
- 経験から、ベクトル命令を使用して正しく機能するアルゴリズムを記述することは、それが使用されるすべての特徴的なケースをカバーする単体テストが存在しない限り、事実上不可能であることがわかります。
上記からわかるように、SIMDプロセッサ拡張機能を使用するとパフォーマンスが大幅に向上しますが、同時に開発プロセスが大幅に複雑になります。 他の価値があるかどうかは、特定のプロジェクトの条件に依存し、開発者の裁量にとどまります。
あとがき
この記事の著者は、彼の活動の性質(ビデオ分析用のアルゴリズムの開発)により、さまざまなベクトル命令(主にSSE2およびAVX2)を使用してC ++の画像処理アルゴリズムを最適化するプロセスにかなりの時間を費やしました。 特に、この作業の結果は、オープンソースコードを使用したC ++の画像処理アルゴリズムのライブラリであり、パブリックドメインで利用可能です。 読者がこのトピックを興味深いと思う場合、特定の例で最適化機能を説明するいくつかの記事を書く準備ができています。