1

思い通りに動作しない小さなテスト関数を作成しました。

基本的に、配列を読み取ってその内容を書き戻す必要があります (後でこれが機能するようになったら、さらに多くのことを行う必要がありますが、今のところこれでも失敗します)。

GPU コードをデバッグすると、最初の数回の反復 (どういうわけか並行して実行されます.. これはおそらく GPU にとっては理にかなっていますが、デバッグすると驚きます) が正常に機能していることがわかりました..しかし、その後、1-2 Debug-Continues (F5)、以前に正しく設定された値の一部が 0 で上書きされます。よくわかりません..再びCPUを使用するまでに、0であってはならないにもかかわらず、多くの値が0になっています(基本的に、単純なテストシーケンスである元のデータが必要です)。

#include "stdafx.h"
#include <amp.h>

typedef unsigned char byte;

using namespace concurrency;

void AMPChangeBrightnessContrastWrapper2(byte* a, int len, float brightness, float contrast)
    {
        array_view<unsigned int> dst(len/4, (unsigned int*)a);
        //dst.discard_data(); 
        parallel_for_each(dst.extent, [=](index<1> idx) restrict(amp) 
        {
            // split into bytes (in floats)
            float temp1 = (dst[idx])  - (dst[idx] >> 8) * 256;
            // this completely fails! float temp1 = dst[idx] & 0xFF;
            float temp2 = (dst[idx] >> 8)  - (dst[idx] >> 16) * 256;
            float temp3 = (dst[idx] >> 16) - (dst[idx] >> 24) * 256;
            float temp4 = (dst[idx] >> 24);
            // convert back to int-array
            dst[idx] = (int)(temp1 + temp2 * 256 + temp3 * 65536 + temp4 * 16777216);

        });
        //dst.synchronize();
    }

int _tmain(int argc, _TCHAR* argv[])
{
    const int size = 30000;
    byte* a = new byte[size];

      // generate some unique test sequence.. first 99 numbers are just 0..98
    for (int i = 0; i < size; ++i)
        a[i] = (byte)((i + i / 99) % 256);

    AMPChangeBrightnessContrastWrapper2(a, size, -10.0f, 1.1f);

    for (int i = 0; i < 50; ++i)
        printf("%i, ", a[i]);
    char out[20];
    scanf_s("%s", out);
    return 0;
}

とても簡単な(計画された)手順:

  • 配列の初期化
  • 配列を GPU に渡します (unsigned int 配列として)
  • 各 unsigned int を 4 バイトに分割し、float に格納します
  • (いくつかの計算を行いますが、簡単にするためにここでは省略しています)
  • フロートに格納されたバイトを元の位置に再び連結します
  • (繰り返す)

ご参考までに..これは色の値であるはずです..

結果は次のとおりです。

  • 一部の値は予想どおりですが、ほとんどの値は異なります
  • 特にバイト0(各unsigned intの)の値が悪いようです
  • 最初に unsigned int->byte->float を & 0xFF で変換しようとしましたが、完全に失敗したようです

出力は次のとおりです (ただし、0 から始まる数字が増えるだけである必要があります)。

0、1、2、3、0、5、6、7、0、9、10、11、16、13、14、15、0、17、18、19、32、21、22、23、32、 25, 26, 27, 32, 29, 30, 31, 0, 33, 34, 35, 64, 37, 38, 39, 64, 41, 42, 43, 64, 45, 46, 47, 64, 49,

質問:

  • & 0xFF の問題はなぜですか?
  • 各 unsigned int のバイト 0 に奇妙な値が割り当てられるのはなぜですか?
  • バイトのarray_viewを作成できないと思います.intまたはfloatを使用する必要がありますか?
  • 最後に .synchronize をコメントアウトしても何も変わらなかった - どうして?
4

2 に答える 2

4

•バイトのarray_viewを作成できないと思います.intまたはfloatを使用する必要がありますか?

バイトの配列または array_view を作成することはできません。C++ AMP は、C++ 型の限定されたサブセットのみをサポートします。配列ビューではなくテクスチャを使用できます。画像処理の場合、これにはいくつかの利点があります。特に、GPU のハードウェアによって実装されるため、パッキングとアンパッキングがはるかに高速になります。以下の完全な例を参照してください。

•最後に .synchronize をコメントアウトしても何も変わらなかった - どうして?

はスコープ外になり、データが暗黙的に同期されて CPU メモリに戻されるdst.synchronize()ため、は必要ありません。dst array_viewちなみに、関数の開始時に呼び出すべきではありませdst.discard_data()ん。これを行うと、からのデータaが GPU にコピーされないことを意味するからです。

texture<> を使用した実装を次に示します。注意点:

  • unit_4 のテクスチャを使用すると、データのパックとアンパックを「無料」で取得できます。
  • まず、ハードウェアが最適化された組み込み関数を使用します。一般に、カーネル内での分岐は、条件が false と評価されたとしても、ワープ内のすべてのスレッドを停止させるため、良くありません。
  • 配列とは異なり、テクスチャはエイリアシングをサポートしていないため、2 つのテクスチャが必要です。
  • いくつかの一時変数を削除しました。変数はレジスタ空間を使用しますが、これは GPU では非常に制限されています。すべてのスレッドがレジスタ空間が利用可能になるのを待たずに実行できるように、使用を最小限に抑える必要があります。
  • static_cast<> を使用した明示的なキャストは、コンパイラの警告が少なくなることを意味し、一般に、適切な (最新の) C++ スタイルと見なされます。

そしてコード...

void AMPChangeBrightnessContrastWrapper3(const byte* a, const int len, 
    const float brightness, const float contrast)
{
    const int pixel_len = len / 4;
    graphics::texture<graphics::uint_4, 1> inputTx(pixel_len, a, len, 8u);
    graphics::texture<graphics::uint_4, 1> outputTx(pixel_len, 8u);
    graphics::writeonly_texture_view<graphics::uint_4, 1> outputTxVw(outputTx);

    parallel_for_each( outputTxVw.extent, [=, &inputTx, &outputTx](index<1> idx) 
        restrict(amp) 
    { 
        const graphics::uint_4 v = inputTx[idx];

        float tmp = static_cast<float>(v.r);
        tmp = (tmp - 128) * contrast + brightness + 128;
        tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
        const unsigned int temp1_ = static_cast<unsigned int>(tmp);

        tmp = static_cast<float>(v.g);
        tmp = (tmp - 128) * contrast + brightness + 128;
        tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
        const unsigned int temp2_ = static_cast<unsigned int>(tmp);

        tmp = static_cast<float>(v.b);
        tmp = (tmp - 128) * contrast + brightness + 128;
        tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
        const unsigned int temp3_ = static_cast<unsigned int>(tmp);

        tmp = static_cast<float>(v.a);
        tmp = (tmp - 128) * contrast + brightness + 128;
        tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
        const unsigned int temp4_ = static_cast<unsigned int>(tmp);        

        outputTxVw.set(idx, graphics::uint_4(temp1_, temp2_, temp3_, temp4_));
    });
    copy(outputTx, (void*)a, len);
}

AMP Bookには、さらに多くの C++ AMP サンプルがあります。

于 2013-09-04T16:36:27.803 に答える
0

うーん..だから、試行錯誤とエラーを繰り返した後、私自身の質問に答えるために:

  • & 0xFF は正常に動作しており、>> と << も同様です。
  • それは正しい値を生成しないという私の問題でした。特に、intの精度(まあ、仮数)をはるかに下回るfloatの(非常に限られた)精度を考慮しませんでした
  • まだわからない
  • まだわかりませんが、.synchronize の有無にかかわらず同じ (結果/速度) に動作します

似たようなものに出くわしたり、それが必要な場合に備えて、明るさとコントラストを明るさの値の配列で変更する(当初の意図どおりの)解決策を次に示します。

void AMPChangeBrightnessContrastWrapper
    (byte* a, int len, float brightness, float contrast)
{
    array_view<unsigned int> dst(len/4, (unsigned int*)a);
    parallel_for_each(dst.extent, [=](index<1> idx) restrict(amp) 
    {
        float temp1 = dst[idx] & 0xFF;
        temp1 = (temp1 - 128) * contrast + brightness + 128;
        if (temp1 < 0)
            temp1 = 0;
        if (temp1 > 255)
            temp1 = 255;

        float temp2 = (dst[idx] >> 8) & 0xFF;
        temp2 = (temp2 - 128) * contrast + brightness + 128;
        if (temp2 < 0)
            temp2 = 0;
        if (temp2 > 255)
            temp2 = 255;

        float temp3 = (dst[idx] >> 16) & 0xFF;
        temp3 = (temp3 - 128) * contrast + brightness + 128;
        if (temp3 < 0)
            temp3 = 0;
        if (temp3 > 255)
            temp3 = 255;

        float temp4 = (dst[idx] >> 24);
        temp4 = (temp4 - 128) * contrast + brightness + 128;
        if (temp4 < 0)
            temp4 = 0;
        if (temp4 > 255)
            temp4 = 255;

        unsigned int temp1_ = (unsigned int)temp1;
        unsigned int temp2_ = (unsigned int)temp2;
        unsigned int temp3_ = (unsigned int)temp3;
        unsigned int temp4_ = (unsigned int)temp4;
        unsigned int res = temp1_ + (temp2_ << 8) + (temp3_ << 16) + (temp4_ << 24);
        dst[idx] = res;
    });
    dst.synchronize();
}

また、計算はほとんどしていませんが (リリース/デバッグ)、Intel HD 4000 を搭載した CPU よりも 2 ~ 4 倍高速でした。

于 2013-07-05T19:15:06.327 に答える