1
  • デバイス: テスラ C2050
  • OS : Windows 7 エンタープライズ
  • IDE: VS 2012

こんにちは、みんな。ボリュームの計算に AMP C++ を使用しています。

(0,0,0) に 1 つのポイントを持つ何百万もの四面体があります。したがって、四面体の体積を簡単な方法で取得できます。

sum += triangle.x1 * triangle.y2 * triangle.z3 + \
       triangle.y1 * triangle.z2 * triangle.x3 + \
       triangle.x2 * triangle.y3 * triangle.z1 - \
       triangle.x3 * triangle.y2 * triangle.z1 - \
       triangle.x2 * triangle.y1 * triangle.z3 - \
       triangle.y3 * triangle.z2 * triangle.x1;

そこで、AMP C++ を使用して計算を高速化したいと考えています。

これがコードです。

typedef struct
{
    double x1;
    double y1;
    double z1;
    double x2;
    double y2;
    double z2;
    double x3;
    double y3;
    double z3;
} Triangle;

そして、主な機能は次のとおりです。

accelerator my_accelerator(accelerator::default_accelerator);
accelerator_view acc_view = my_accelerator.get_default_view();

const int BLOCK_SIZE = 64;
int outputSize = int(numTriangles / BLOCK_SIZE);

int dimA = int(numTriangles / BLOCK_SIZE) * BLOCK_SIZE;
std::cout<<dimA<<std::endl;

//copy triangles from host to device
array<Triangle,1> triangle(numTriangles);
copy(vTriangle.begin(),vTriangle.end(), triangle);

//Volume
std::vector<double> volumeCPP;
for (int i=0; i < outputSize; i++)
{
    volumeCPP.push_back(double(0));
}
array_view<double,1> volume(outputSize,volumeCPP);
volume.discard_data();

clock_t start,finish;
start = clock();
parallel_for_each(
    volume.extent.tile<1>(),
    [=, &triangle](tiled_index<1> t_idx) restrict(amp)
    {
        double sum = 0.0f;
        tile_static Triangle tile_triangle[4];
        tile_triangle[t_idx.local[0]] = triangle[t_idx.global];
        if (t_idx.local[0] == 0)
        {
            for (int idx=0; idx < BLOCK_SIZE; idx++){
                sum += tile_triangle[idx].x1 * tile_triangle[idx].y2 * tile_triangle[idx].z3 + tile_triangle[idx].y1 * tile_triangle[idx].z2 * tile_triangle[idx].x3 + tile_triangle[idx].x2 * tile_triangle[idx].y3 * tile_triangle[idx].z1 - tile_triangle[idx].x3 * tile_triangle[idx].y2 * tile_triangle[idx].z1 - tile_triangle[idx].x2 * tile_triangle[idx].y1 * tile_triangle[idx].z3 - tile_triangle[idx].y3 * tile_triangle[idx].z2 * tile_triangle[idx].x1;
                //t_idx.barrier.wait();
            }
            //t_idx.barrier.wait();
        }
        volume[t_idx.global] = sum;
    }
);

acc_view.wait();
finish = clock();
copy(volume, volumeCPP.begin());

だから、すべての仕事がダウンしています。しかし、興味深いことはです。CPU(シングルコア)コードよりもコストがかかります。

CPU (シングルコア) 上の C++ は、1024 * 1024 * 2 三角形の計算を完了するのに 0.085 秒かかります。しかし、AMP C++ コードのコストは 0.530 秒です。C++ コードよりもはるかに多くの.

インターネットで検索した後、ヒントがあります。最初にデバイスをウォームアップすると、計算で「リアルタイム」コストを取得できます。

そのため、最初に 128 個の三角形を計算してデバイスをウォームアップし (約 0.2 秒かかります)、次に 1024 * 1024 * 2 個の三角形を計算してボリュームを取得します。はるかに高速になりました (約 0.091 秒かかります) が、それでも CPU (シングルコア) コードよりは遅くなります。

理由と、計算を高速化するのを手伝ってくれる人がいれば知りたいです。

どうもありがとう。

4

2 に答える 2

2

まず、以下は、いくつかのコメントを含む、わずかに優れた実装であると私が考えるものです。あなたのコードは、回避できるいくつかのことを行っています。

ただし、ここで実際に行っているのは削減です。これは、非常に綿密に研究され、最適化されたアルゴリズムです。AMP Algorithms Codeplex サイトにC++ AMP 実装があり、STL スタイルのアルゴリズムとして実装されています。C++ AMP がニーズを満たしていないと結論付ける前に、この reduce 実装を使用してみます。実行するのは簡単で、パフォーマンスが大幅に向上する可能性があるからです。私はあなたがどのように乗りこなすか興味があります。

AMP Book Codeplex サイトには、C++ AMP カーネルのタイミングを計るためのヘルパー クラスが含まれています。付属の本では、リダクションの実装についても説明しています。それには章全体があります。

void Foo()
{
    const int numTriangles = 128;
    std::vector<Triangle> vTriangle;

    accelerator my_accelerator(accelerator::default_accelerator);
    accelerator_view acc_view = my_accelerator.get_default_view();

    const int BLOCK_SIZE = 64;
    int outputSize = int(numTriangles / BLOCK_SIZE);

    const int dimA = numTriangles;
    std::cout<<dimA<<std::endl;

    //copy triangles from host to device
    // Use and array_view to automatically sync your data. 
    // You can use acc_view.flush() to make sure that copy is complete 
    // when you are running your timing code. Make this const so that AMP does
    // not copy your input data back to the CPU.

    array_view<const Triangle, 1> triangle(vTriangle.size(), vTriangle.data());

    //Volume
    // Don't push_back this causes (re)allocation as the vector grows. 
    // Set size and fill at the same time.

    std::vector<double> volumeCPP(outputSize, 0.0);

    array_view<double, 1> volume(outputSize, volumeCPP);
    volume.discard_data();

    // I would use the timing code on CodePlex. 
    // It will be more accurate than this.
    clock_t start, finish;
    start = clock();
    parallel_for_each(
        // Not sure a tile size of 1 will be handled that 
        // well by the runtime in terms of perf. I see why you
        // are doing it to get tile_static. You might be better off having larger tiles.

        volume.extent.tile<1>(),
        [=](tiled_index<1> t_idx) restrict(amp)
        {
            double sum = 0.0f;
            for (int idx = 0; idx < BLOCK_SIZE; idx++)
            {
                // Loading the single triangle into tiled memory is a good idea because
                // elements are read more than once.
                tile_static Triangle tile_triangle;
                tile_triangle = triangle[t_idx.global * BLOCK_SIZE + idx];

                sum += tile_triangle.x1 * tile_triangle.y2 * tile_triangle.z3 + 
                    tile_triangle.y1 * tile_triangle.z2 * tile_triangle.x3 + 
                    tile_triangle.x2 * tile_triangle.y3 * tile_triangle.z1 - 
                    tile_triangle.x3 * tile_triangle.y2 * tile_triangle.z1 - 
                    tile_triangle.x2 * tile_triangle.y1 * tile_triangle.z3 - 
                    tile_triangle.y3 * tile_triangle.z2 * tile_triangle.x1;
            }
            volume[t_idx.global] = sum;
        }
    );
    // Force data copy back to CPU.
    volume.synchronize();
    double sum = std::accumulate(begin(volumeCPP), end(volumeCPP), 0.0);
}

これは、AMP アルゴリズム ライブラリを使用して、map/reduce パターンを使用して問題の解決策を実装する別の例です。

std::vector<Triangle> triangles_cpu(1000);

array_view<const Triangle, 1> triangles_gpu(triangles_cpu.size(), triangles_cpu.data());
concurrency::array<double, 1> volumes_gpu(triangles_cpu.size());
array_view<double, 1> volumes_gpuvw(volumes_gpu);
amp_stl_algorithms::transform(begin(triangles_gpu), end(triangles_gpu), begin(volumes_gpuvw), 
    [=](const triangle& t) restrict(amp)
{
    return t.x1 * (t.y2 * t.z3 - t.y3 * t.z2)
        + t.y1 * (t.z2 * t.x3 - t.x2 * t.z3)
        + t.z1 * (t.x2 * t.y3 - t.x3 * t.y2);
});
double sum = amp_stl_algorithms::reduce(begin(volumes_gpuvw), end(volumes_gpuvw), 0.0);
于 2013-09-04T21:54:03.513 に答える
0

因数分解することで、少し高速化できるはずです。

四面体の体積の式に注意してください。

+ x1 * y2 * z3
+ y1 * z2 * x3
+ x2 * y3 * z1
- x3 * y2 * z1
- x2 * y1 * z3
- y3 * z2 * x1

次と同等です。

+ x1 * (y2 * z3 - y3 * z2)
+ y1 * (z2 * x3 - x2 * z3)
+ z1 * (x2 * y3 - x3 * y2)

元の数式には 12 の乗算があり、同等の数式には 9 つの乗算があります (25% 減)。全体の改善がどの程度になるかはわかりませんが、20% の改善が得られても驚かないでしょう。

于 2013-09-03T08:18:32.803 に答える