2

ベクトル化を目的とした次の関数を検討してください。

void AddSqr(float* restrict dst, float* restrict src, int cnt)
{
    for (int i=0; i<cnt; i++) dst[i] = src[i] * src[i];
};

もちろん、srcとdstがエイリアスされていない場合、これは機能します。しかし、src == dst の場合はどうでしょうか? もちろん、src == dst+1 などの極端なケースは許可されません。しかし、ポインターが同じであれば、問題はないはずです。それとも、何か不足していますか?

編集: restrict は Intel C++ コンパイラのキーワードで、MSVC には __restrict があります。

この質問に対する私のポイントは、あらゆる種類のベクトル化がどのようにうまくいかないかがわかりません. dst が変更された場合、src 値が書き込まれたという事実は出力が計算されたことを意味するため、src 値はもう必要ありません。唯一のケースは、コンパイラが dst 自体を一時バッファとして使用した場合であり、これは正しくないと思います。

4

3 に答える 3

2

restrictC では、コードはを介し​​て 1 つのオブジェクトに書き込みますdstが、 を介して同じオブジェクトを読み取るため、定義に違反して未定義の動作を引き起こしますsrc

dstと の間にオフセットがあるかどうかは問題ではありませんsrcfloat条件は、一方のポインターを介して書き込まれ、もう一方のポインターを介して読み取られるオブジェクトが存在することです。

于 2015-04-01T12:51:09.890 に答える
0

すべての回答に感謝します。つまり: - 標準 C++ の定義では、これは実際には正しくありません。- ただし、Intel から直接、これで問題ないという返信がありました。

私の最初の質問は、「ルールに従っている」かどうかではなく、うまくいかない可能性があるかどうかでした。src/dst 配列は 1:1 でマッピングされるため、配列が完全に異なるか、まったく同じであり、したがって、各項目はまったく無関係な項目に依存するか、それ自体に依存します。したがって、アイテムが書き換えられた場合、その最終値は計算されて保存され、サイクル中に再び必要になることはありません。

とにかく、いくつかの追加処理を行いました:

void AddSqr(float* restrict dst, float* restrict src, int cnt)
{
    if (dst == src)
        for (int i=0; i<cnt; i++) dst[i] = dst[i] * dst[i];
    else
        for (int i=0; i<cnt; i++) dst[i] = src[i] * src[i];
};

これにより、潜在的な問題が修正され、いくつかの追加の最適化の可能性が提供されます。ポインターが同じ場合、コンパイラーは 1 つのレジスターのみを使用して (またはオフセット レジスターを使用せずに)、配列をターゲットにすることができます。

于 2015-04-02T21:34:53.203 に答える
0

Restrict は、2 つのポインターが互いに干渉しない場合にのみ有効な最適化を許可するためのキーワードです。

非常に単純なケースでは、2 つのポインターが同一の場合に失敗する最適化が利用できる可能性は低いため、テストしても何も悪いことは起こりません。

しかし、より一般的なケースでは、restrictキーワードは、2 つのポインターが異なり、それらが指すデータ構造が異なることを主張していることを意味します。コンパイラは、このアサーションを自由に使用して、必要な最適化を許可します。特に、アサーションが正しくない場合にプログラムが壊滅的に失敗するような最適化を許可します。

C 標準では、アサーションが間違っている場合に何が起こるかを定義していないため、この失敗は「未定義の動作」と呼ばれます。これは最適化アサーションであるため、一般に「鼻の悪魔」と呼ばれる完全に予測不可能な動作は、C コンパイラが定義する妥当な動作です。

于 2015-04-01T13:40:49.810 に答える