3

我已经开始尝试使用 C++ AMP。我创建了一个简单的测试应用程序只是为了看看它可以做什么,但是结果让我很惊讶。考虑以下代码:

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

using namespace concurrency;

int main( int argc, char* argv[] )
{
    uint32_t u32Threads = 16;
    uint32_t u32DataRank = u32Threads * 256;
    uint32_t u32DataSize = (u32DataRank * u32DataRank) / u32Threads;
    uint32_t* pu32Data = new (std::nothrow) uint32_t[ u32DataRank * u32DataRank ];

    for ( uint32_t i = 0; i < u32DataRank * u32DataRank; i++ )
    {
        pu32Data[i] = 1;
    }

    uint32_t* pu32Sum = new (std::nothrow) uint32_t[ u32Threads ];

    Timer tmr;

    tmr.Start();

    array< uint32_t, 1 > source( u32DataRank * u32DataRank, pu32Data ); 
    array_view< uint32_t, 1 > sum( u32Threads, pu32Sum );

    printf( "Array<> deep copy time: %.6f\n", tmr.Stop() );

    tmr.Start();

    parallel_for_each( 
        sum.extent,
        [=, &source](index<1> idx) restrict(amp)
        {
            uint32_t u32Sum = 0;
            uint32_t u32Start = idx[0] * u32DataSize;
            uint32_t u32End = (idx[0] * u32DataSize) + u32DataSize;
            for ( uint32_t i = u32Start; i < u32End; i++ )
            {
                u32Sum += source[i];
            }
            sum[idx] = u32Sum;
        }
    );

    double dDuration = tmr.Stop();
    printf( "gpu computation time: %.6f\n", dDuration );

    tmr.Start();

    sum.synchronize();

    dDuration = tmr.Stop();
    printf( "synchronize time: %.6f\n", dDuration );
    printf( "first and second row sum = %u, %u\n", pu32Sum[0], pu32Sum[1] );

    tmr.Start();

    for ( uint32_t idx = 0; idx < u32Threads; idx++ )
    {
        uint32_t u32Sum = 0;
        for ( uint32_t i = 0; i < u32DataSize; i++ )
        {
            u32Sum += pu32Data[(idx * u32DataSize) + i];
        }
        pu32Sum[idx] = u32Sum;
    }

    dDuration = tmr.Stop();
    printf( "cpu computation time: %.6f\n", dDuration );
    printf( "first and second row sum = %u, %u\n", pu32Sum[0], pu32Sum[1] );

    delete [] pu32Sum;
    delete [] pu32Data;

    return 0;
}

请注意,这Timer是一个使用 QueryPerformanceCounter 的简单计时类。无论如何,代码的输出如下:

Array<> deep copy time: 0.089784
gpu computation time: 0.000449
synchronize time: 8.671081
first and second row sum = 1048576, 1048576
cpu computation time: 0.006647
first and second row sum = 1048576, 1048576

为什么调用 synchronize() 需要这么长时间?有没有办法解决这个问题?除了计算性能的表现令人惊叹之外,同步()开销使我无法使用它。

我也可能做错了什么,如果是这样,请告诉我。提前致谢。

4

2 回答 2

5

函数 synchronize() 可能需要很长时间,因为它正在等待实际的内核完成其工作。

来自amp.h 的 parallel_for_each

请注意,parallel_for_each 的执行好像与调用代码同步,但实际上它是异步的。即一旦调用了parallel_for_each并且内核已经被传递到运行时,[parallel_for_each之后的代码]继续由CPU线程立即执行,而并行内核由GPU线程执行。

因此,衡量在 parallel_for_each 中花费的时间并不是特别有意义。

编辑:算法的编写方式,它不会从 GPU 加速中受益匪浅。source[i] 的读取是非合并的,因此它将比合并读取慢 16 倍。可以通过使用共享内存来合并读取,但这并不是很简单。我建议阅读 GPU 编程。

如果您只需要一个简单的示例来演示 C++ AMP 的实用性,请尝试矩阵乘法

当然,您将观察到的性能也很大程度上取决于您的 GPU 硬件型号。

于 2012-03-23T22:24:40.237 回答
3

除了 Igor 对您的特定算法的回应之外,请注意,您测量 C++ AMP 性能的方式通常存在多个不正确的方面(没有运行时初始化排除、没有丢弃初始 JIT、没有预热数据,以及已经指出的假设 p_f_e 是同步的),所以请在此处遵循我们的指南:

http://blogs.msdn.com/b/nativeconcurrency/archive/2011/12/28/how-to-measure-the-performance-of-c-amp-algorithms.aspx

于 2012-03-27T01:48:01.777 回答