転置演算子最適化のためのいくつかの一般的なシナリオ

すべての転置シナリオで同時に最適化できるカーネルを作成することは困難ですが、いくつかの一般的な転置シナリオを要約し、目的を絞った方法で最適化することができます。ここでは、形状のサイズを考慮せずに、transpose による軸の変換のほんの数例を示します。したがって、これらのシナリオでは、転置された軸の形状サイズのターゲットを絞った最適化も考慮する必要があります。

シーン 1: バッチ 2D、パーマ: 021

2D、3D、またはより高次元のテンソルの場合、最も内側の 2 次元を交換します。これらはすべてバッチ 2D に統合できます。2D 行列の転置はバッチ = 1 に相当し、3 次元を超えるテンソルは最も内側の 2 次元以外のすべての次元を 1 つの次元に結合できます。

大きな 021 行列を小さな行列に転置する方法:

たとえば、入力は転置の場合 [4096, 4096] という大きな行列ですが、各スレッドは入力の同じ行の隣接する位置を読み取りますが、書き込み時には同じ列の隣接する位置を書き込み、書き込みの場合は同じものを書き込みます。列のスパン ライン長が長すぎると、ライトバック キャッシュ ヒットが発生しにくくなります。パフォーマンスを最適化するために、スパンのライン長を減らすことを検討してください。

大規模な移調をいくつかの小さな移調に分割することを検討してください。

たとえば、M と N の両方をより小さい次元に分割できることを考慮して、MN を NM に転置します。M は M1M0 に分割され、N は N1N0 に分割されます。

次に、問題は MN を NM に置き換えると、M1M0N1N0 を N1N0M1M0 に置き換えます。

一度に 2 軸のみ交換できると仮定すると、次の 3 つの手順で実現できます。

M1M0N1N0->N1M1M0N0->N1M1N0M0->N1N0M1M0

最初のステップと 3 番目の部分はそれぞれテンソルを転置します (以下の 0213 転置シーンなど)。これは一般に非常に効率的に達成できます。

2 番目のステップでは、単一要素の転置の次元が MN から M0N0 に削減され、キャッシュの利用がより容易になります。たとえば、[4096,4096] は転置のために [64,64,64,64] に分割できます。

この方法の視覚的な表示は次のとおりです。これは、大きな行列を小さな行列に分割し、それぞれの小さな行列を個別に転置し、その後、小さな行列全体を転置することと同じです。

この回路図は M1M0N1N0->M1N1M0N0->M1N1N0M0->N1M1N0M0 を示しており、これは最終目標まであと 1 歩であることに注意してください。

シーン2:0213

その特徴は、内側の 2 つの隣接する次元が相互作用し、1 つ以上の最も内側の次元を含まないことです。以上のように、交換されない隣接する次元をまとめて結合し、最外次元が足りない場合は1を補うことができます。

このシーン perm=[2, 0, 3, 1, 4] では複数の軸が同時に転置されているように見えますが、形状要素 1 の特殊性により押しつぶせるので変換できます[784, 3, 4, 12] から [3, 4, 784, 12] を転置する場合は、0213 メソッドを使用して解くことができます。

転置形状が 1 のアルゴリズムを削除します

perm = [2, 0, 3, 1, 4]
in_shape = [1, 784, 1, 4, 12]

rm_axes = []
for idx, elem in enumerate(in_shape):
    if elem == 1:
        rm_axes.append(idx)

print("rm_axes:", rm_axes)

def remove_axis(in_shape, perm, rm_axis):
    del in_shape[rm_axis]
    perm_rm_idx = -1
    for idx, elem in enumerate(perm):
        if elem == rm_axis:
            perm_rm_idx = idx
        if elem > rm_axis:
            perm[idx] = perm[idx]-1

    del perm[perm_rm_idx]

for rm_axis in reversed(rm_axes):
    remove_axis(in_shape, perm, rm_axis)

print("perm:", perm)
print("in_shape:", in_shape)

シナリオ 3: 隣接する 2 つの軸を交換しますが、いずれかの軸に対応する形状は 1 です

このシーンではトランスポーズは必要なく、形状変更のみが必要です。

上記のアルゴリズムを使用して転置形状を 1 として削除した後、この転置のパーマは [0,1,2,3,...] になります。この転置が何も操作を実行する必要がないことを判断するのは非常に簡単です。そして直接削除するだけです。

シナリオ 4: 複数の軸を交換しますが、一部のパーマは隣接しています

ここでは perm=[1, 2, 0] なので、3 つの軸が交換されているように見えますが、実際には 2 つの 1x64 が一緒に交換され、1 つの次元にマージできます。この問題は、上記のシナリオ 1 になります。したがって、解決策としては、一緒に変換される隣接する軸をマージして、問題を単純化することが考えられます。

シーン 5: その他

もちろん、入力形状の最初の次元が 1 ではない場合など、上記の方法を使用しても解決できないシナリオはまだ少数あります。

おすすめ

転載: blog.csdn.net/u013701860/article/details/126738143