1

データの入力ベクトルに基づいていくつかの計算を行う簡単なコードを書きたかったのです。値を 1 つだけ返す必要があります。これを達成する方法がわかりません。どのように動作するかを確認する簡単なテストを書いたところ、コンパイル エラーが発生しました。コードは次のとおりです。

Float Subset::parallel_tests() 
{ 
float sum = 0.0f; 

concurrency::parallel_for_each(concurrency::extent<1>(121), [=, &sum] (concurrency::index<1> idx) restrict(amp) 
{ 
    sum += 0.2f; 
}); 

return sum; 
} 

このコードをコンパイルしようとすると、次のエラーが発生します。

エラー C3590: 'sum': ラムダが amp 制限されている場合、参照によるキャプチャまたは 'this' キャプチャはサポートされません エラー C3581: 'cci::Subset::parallel_tests::': amp 制限されたコードでサポートされていない型です

4

3 に答える 3

1

コードがコンパイルされない理由は、sumがクラス内で宣言されていて、 でラップされていないためarray_viewです。基本的にthis->sum、AMP 制限付きコードからアクセスしようとしています。sumに渡す前に、次を使用してラップする必要がありparallel_for_eachますavSum

int sum = 0;
array_view<int, 1> avSum(1, &sum);

また、アトミック操作を使用して複数のスレッドにわたって の値をインクリメントする必要がありsum、GPU によって提供される並列処理が大幅に無効になります。これは正しいアプローチではありません。

割引

あなたが達成しようとしていると思うのは削減です。入力配列のすべての値を合計して、単一の結果を返そうとしています。これは、GPU プログラミングで十分に文書化された問題です。NVidia は、これに関するいくつかのホワイト ペーパーを作成しています。C++ AMP Bookもこれについて詳しく説明しています。

これが最も単純な実装です。タイリングを使用せず、比較的非効率的ですが、理解しやすいです。ループの各反復はstride、最終結果が要素 0 になるまで、配列の連続する要素を追加します。8 つの要素の配列の場合:

stride = 4: a[0] += a[4]; a[1] += a[5]; a[2] += a[6]; a[3] += a[7]
stride = 2: a[0] += a[2]; a[1] += a[1];

ゼロ要素に合計が含まれるようになりました。

class SimpleReduction
{
public:
    int Reduce(accelerator_view& view, const std::vector<int>& source, 
        double& computeTime) const
    {
        assert(source.size() <= UINT_MAX);
        int elementCount = static_cast<int>(source.size());

        // Copy data
        array<int, 1> a(elementCount, source.cbegin(), source.cend(), view);
        std::vector<int> result(1);
        int tailResult = (elementCount % 2) ? source[elementCount - 1] : 0;
        array_view<int, 1> tailResultView(1, &tailResult);

        for (int stride = (elementCount / 2); stride > 0; stride /= 2)
        {
            parallel_for_each(view, extent<1>(stride), [=, &a] (index<1> idx)
                restrict(amp)
            {
                a[idx] += a[idx + stride];

                // If there are an odd number of elements then the 
                // first thread adds the last element.
                if ((idx[0] == 0) && (stride & 0x1) && (stride != 1))
                    tailResultView[idx] += a[stride - 1];
            });
        }

        // Only copy out the first element in the array as this 
        // contains the final answer.
        copy(a.section(0, 1), result.begin());

        tailResultView.synchronize();
        return result[0] + tailResult;
    }
};

タイル内の各スレッドがその要素の結果を生成する責任を負い、すべてのタイルの結果が合計される場所にこれを並べて表示できます。

template <int TileSize>
class TiledReduction 
{
public:
    int Reduce(accelerator_view& view, const std::vector<int>& source, 
        double& computeTime) const
    {
        int elementCount = static_cast<int>(source.size());

        // Copy data
        array<int, 1> arr(elementCount, source.cbegin(), source.cend(), view);

        int result;
        computeTime = TimeFunc(view, [&]() 
        {
            while (elementCount >= TileSize)
            {
                extent<1> e(elementCount);
                array<int, 1> tmpArr(elementCount / TileSize);

                parallel_for_each(view, e.tile<TileSize>(), 
                    [=, &arr, &tmpArr] (tiled_index<TileSize> tidx) restrict(amp)
                {
                    //  For each tile do the reduction on the first thread of the tile.
                    //  This isn't expected to be very efficient as all the other
                    //  threads in the tile are idle.
                    if (tidx.local[0] == 0)
                    {
                        int tid = tidx.global[0];
                        int tempResult = arr[tid];
                        for (int i = 1; i < TileSize; ++i)
                            tempResult += arr[tid + i];

                        //  Take the result from each tile and create a new array. 
                        //  This will be used in the next iteration. Use temporary 
                        // array to avoid race condition.
                        tmpArr[tidx.tile[0]] = tempResult;
                    }
                });

                elementCount /= TileSize;
                std::swap(tmpArr, arr);
            }

            //  Copy the final results from each tile to the CPU and accumulate them 
            std::vector<int> partialResult(elementCount);
            copy(arr.section(0, elementCount), partialResult.begin());
            result = std::accumulate(partialResult.cbegin(), partialResult.cend(), 0);
        });
        return result;
    }
};

これは、適切なメモリ アクセス パターンがないため、依然として最も効率的なソリューションではありません。この書籍の Codeplex サイトで、これに関するさらなる改良を確認できます。

于 2014-02-17T21:59:47.953 に答える
0
//The method should compute a correlation value of two images (which had already been copied to GPU memory) 

float Subset::compute_correlation(const concurrency::array<float, 1>& source1, const concurrency::array<float, 1>& source2) 
{
    float result; 
    float parameter_1; 
    float parameter_2; 
    . 
    . 
    . 
    float parameter_n; 
    parrallel_for_each(...) 
    { 
         //here do some computations using source1 and source2 
         parameter_1 = source1[idx] o source2[idx]; 
         .
         . 
         . 
         //I am computing every parameter in different way 
         parameter_n = source1[idx] o source2[idx]; 
    } 
    //compute the result based on the parameters 
    result = parameter_1 o parameter_2 o ... o parameter_n; 

    return result; 
} 
于 2014-02-19T23:45:23.383 に答える
0

OK、削減の実装を開始しました。単純な縮小から始めましたが、問題が発生しました。std::vector を関数に渡したくありませんが、1 つまたは 2 つの同時実行::配列を渡します。

値が返されるように、ソースから情報を取得し、すべてを並列に合計する必要があります。どのように実装すればよいですか?

単純なバージョンのコードは次のようになります。

float Subset::reduction_simple_1(const concurrency::array<float, 1>& source)
{
    assert(source.size() <= UINT_MAX);
//unsigned element_count = static_cast<unsigned>(source.size());

unsigned element_count = 121; 

assert(element_count != 0); // Cannot reduce an empty sequence.
    if (element_count == 1)
    {
         return source[0];
    }

    // Using array, as we mostly need just temporary memory to store
    // the algorithm state between iterations and in the end we have to copy
    // back only the first element.
    //concurrency::array<float, 1> a(element_count, source.begin());

    // Takes care of odd input elements – we could completely avoid tail sum
    // if we would require source to have even number of elements.
    float tail_sum = (element_count % 2) ? source[element_count - 1] : 0;
    concurrency::array_view<float, 1> av_tail_sum(1, &tail_sum);

    // Each thread reduces two elements.
    for (unsigned s = element_count / 2; s > 0; s /= 2)
    {
        concurrency::parallel_for_each(concurrency::extent<1>(s), [=, &a] (concurrency::index<1> idx) restrict(amp)
    {
        //get information from source, do some computations and store it in accumulator 
        accumulator[idx] = accumulator[idx] + accumulator[idx + s];

        // Reduce the tail in cases where the number of elements is odd.
        if ((idx[0] == s - 1) && (s & 0x1) && (s != 1))
        {
            av_tail_sum[0] += accumulator[s - 1];
        }
    });
}

// Copy the results back to CPU.
std::vector<float> result(1);
copy(accumulator.section(0, 1), result.begin());
av_tail_sum.synchronize();

return result[0] + tail_sum;
} 

どういうわけか「アキュムレータ」を実装する必要がありますが、方法がわかりません。

于 2014-02-19T01:13:03.160 に答える