Почему вызов array_view :: synchronize () такой медленный? - PullRequest
3 голосов
/ 23 марта 2012

Я начал экспериментировать с 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 () занимает так много времени?Есть ли способ, как обойти это?Кроме того, производительность вычислений поразительна, однако издержки синхронизации () делают ее непригодной для использования.

Возможно также, что я делаю что-то ужасно неправильно, если так, скажите, пожалуйста.Заранее спасибо.

Ответы [ 2 ]

5 голосов
/ 24 марта 2012

Функция synchronize (), вероятно, занимает так много времени, потому что она ждет, пока фактическое ядро ​​завершит свою работу.

С parallel_for_each from amp.h :

Обратите внимание, что parallel_for_each выполняется так, как если бы он был синхронным с вызывающим кодом, но в действительности он асинхронный. То есть после того как был выполнен вызов parallel_for_each и ядро ​​было передано среде выполнения, [код после parallel_for_each] продолжает выполняться немедленно потоком ЦП, в то время как параллельно ядро ​​выполняется потоками ГП.

Таким образом, измерение времени, проведенного в parallel_for_each, не имеет особого смысла.

РЕДАКТИРОВАТЬ: способ написания алгоритма, он не выиграет много от ускорения графического процессора. Чтение источника [i] не слитно, и поэтому оно будет почти в 16 раз медленнее слияния. Можно объединить чтение, используя разделяемую память, но это не совсем тривиально. Я бы порекомендовал почитать о программировании на GPU.

Если вам нужен простой пример, демонстрирующий полезность C ++ AMP, попробуйте умножение матриц .

Конечно, производительность, которую вы наблюдаете, также сильно зависит от модели вашего графического оборудования.

3 голосов
/ 27 марта 2012

В дополнение к ответу Игоря на ваш конкретный алгоритм, обратите внимание, что есть несколько неверных аспектов того, как вы измеряете производительность 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...