Как определить производительность графического процессора CUDA?

Я пишу программу cuda для сопоставления каждого входного изображения с разрешением ~ 180X180 с примерно 10 000 шаблонных изображений с разрешением ~ 128 * 128. Цель состоит в том, чтобы добиться производительности в реальном времени, т. е. сопоставления шаблонов 25–30 входных изображений (каждое со всеми 10 000 шаблонами) за 1 секунду.

в настоящее время я использую следующий подход

  1. Предварительно загружены все шаблоны в глобальную память графического процессора, чтобы сохранить операции ввода-вывода во время выполнения.
  2. Создано одно ядро ​​для сопоставления одного исходного изображения со всеми изображениями шаблона и возврата массива для всех положительных совпадений.
  3. Выполнение всех операций во временной области (без использования БПФ). Причина в том, что я попробовал реализацию Radix-4 fft, но для этого требуется много промежуточных глобальных операций чтения и записи, что в конечном итоге занимает больше времени.

до сих пор для 1 входного изображения на 10 000 шаблонов требуется около 2 секунд.

Мои вопросы:

  1. Есть ли способ определить, достижима ли эта задача в реальном времени или нет? Я имею в виду с помощью максимальных FLOPS и ограничений пропускной способности ввода/вывода и т.д.
  2. Как вычислить, полностью ли используется графический процессор?
  3. Возможные пути повышения производительности?

Характеристики машины: [i7-4770, 8 ГБ, GTX-680]

Объяснение текущего кода ядра:

  1. все шаблонные изображения [размер около 128X128 в RGB] загружаются в память графического процессора. Идея состоит в том, чтобы сохранить ввод-вывод во время работы.
  2. Каждое входное изображение загружается в память текстуры, потому что текстура является хорошим вариантом для 2D-адресации.
  3. Каждый блок имеет 1024 потока.
  4. Каждый поток вычисляет значение для каждого выходного пикселя, размер вывода [31X31 = 961 пиксель].
  5. Количество запущенных блоков равно количеству сопоставленных изображений шаблона.

Код ядра:

__global__ void cudaMatchTemplate(TemplateArray *templates, uchar *Match)
{
    int global = blockIdx.x*blockDim.x + threadIdx.x;

    __shared__ int idx[TEMPLATE_MATCH_DIM];
    __shared__ float out_shared[TEMPLATE_MATCH_DIM];

    //halving the template size....
    int rows = (templates[blockIdx.x].nHeight)/2;
    int cols = (templates[blockIdx.x].nWidth)/2;

    int fullCol = templates[blockIdx.x].nWidth;

    int x = templates[blockIdx.x].nMatchLeft;
    int y = templates[blockIdx.x].nMatchTop;

    int offset_y =  (threadIdx.x/TEMPLATE_MATCH_SIZE);
    int offset_x =  (threadIdx.x - offset_y*TEMPLATE_MATCH_SIZE);

    // *************** Performing match in time domain *****************************//
    int sum = 0;
    float temp;
    int idxXFactor = 3*(2*(offset_x) + x);
    int idxYFactor = 2*(offset_y) + y ;
    
    for (int i = 0; i < rows; i++)
    {
        int I=3*i*fullCol;
        int sourceIdxY = idxYFactor + 2*i;
        for (int j = 0; j < cols; j++)
        {
            int J=3*j;
            int sourceIdxX = idxXFactor + 2*J;          
            int templateIdx = 2*I+2*J;
            //**** R *****//
            temp = float(tex2D(SourceImgColorTex,sourceIdxX,sourceIdxY)) - float(templates[blockIdx.x].pRAWPixels_gpu[templateIdx]);
            sum = sum + temp*temp;
            //**** G *****//
            temp = float(tex2D(SourceImgColorTex,sourceIdxX+1,sourceIdxY)) - float(templates[blockIdx.x].pRAWPixels_gpu[templateIdx +1]);
            sum = sum + temp*temp;
            //**** B *****//
            temp = float(tex2D(SourceImgColorTex,sourceIdxX+2,sourceIdxY)) - float(templates[blockIdx.x].pRAWPixels_gpu[templateIdx +2]);
            sum = sum + temp*temp;
        }
    }

    __syncthreads();
    
//placing all values in shared memory for comparison.
    if(threadIdx.x < TEMPLATE_MATCH_DIM)
    {
        idx[threadIdx.x] = threadIdx.x;
        out_shared[threadIdx.x] = sum;
    }
    __syncthreads();


// //computing the Min location.....//

#pragma unroll
    for(int s=512; s>0; s>>=1) 
    {
        if ((threadIdx.x < s) &&((threadIdx.x + s)<TEMPLATE_MATCH_DIM))
        {
            idx[threadIdx.x] = out_shared[threadIdx.x] < out_shared[threadIdx.x + s] ? idx[threadIdx.x] : idx[threadIdx.x + s];
            out_shared[threadIdx.x]  = out_shared[threadIdx.x] < out_shared[threadIdx.x + s] ? out_shared[threadIdx.x] : out_shared[threadIdx.x + s];           
        }
        
    }

    __syncthreads();

    if(threadIdx.x <1)
    {
        int half_Margin = MARGIN_FOR_TEMPLATE_MATCH/2;
        int matchY = idx[0]/TEMPLATE_MATCH_SIZE ;
        int matchX = idx[0] - matchY * TEMPLATE_MATCH_SIZE;

        int diff = absolute(half_Margin - matchX) + absolute(half_Margin - matchY);
        if(diff < THRESHOLD)
        {
            Match[blockIdx.x] = 1;
        }
        else
            Match[blockIdx.x] = 0;

    }
}

person Genutek    schedule 11.01.2014    source источник
comment
Вы действительно спрашиваете, как ускорить некоторый код, который вы не показали и едва описали, в 50-60 раз?   -  person talonmies    schedule 11.01.2014
comment
Я обновил свой запрос по вашему запросу. Пожалуйста, дайте мне знать, если вам нужны дополнительные разъяснения. Я надеюсь, что это будет полезно в ответах на вопросы.   -  person Genutek    schedule 11.01.2014


Ответы (1)


Я постараюсь ответить на большинство ваших вопросов.

Есть ли способ определить, достижима ли эта задача в реальном времени или нет? Я имею в виду с помощью максимальных FLOPS и ограничений пропускной способности ввода-вывода и т. д.

Я понятия не имею, как определить, достижимо ли ядро ​​в режиме реального времени, вы можете максимизировать свое ядро ​​​​CUDA, используя Калькулятор занятости CUDA. Вы можете использовать текстуру, поверхностную память, постоянную память, закрепленную память хоста и многое другое. Это зависит от реализации вашего алгоритма.

Как вычислить, полностью ли используется графический процессор?

Вы можете использовать калькулятор занятости CUDA и визуальный профайлер CUDA. Я настоятельно рекомендую использовать визуальный профилировщик, который поможет вам понять CUDA.

Возможные пути повышения производительности?

Есть несколько интересных способов сделать это. Во-первых, вы можете максимизировать вызов ядра, используя описанный выше метод. Если этого недостаточно, попробуйте реализовать конвейер с использованием потоковых объектов, чтобы одновременно копировать данные и вычислительные задания.

Если это не сработает, попробуйте работать с задержкой, управляйте несколькими потоками, одновременно обращающимися к GPU, так как CC 3.5 CUDA запустила HyperQ, это может помочь вам выполнить несколько вызовов параллельно.

Если это не сработает, рассмотрите возможность использования нескольких устройств с графическим процессором.

person TripleS    schedule 11.01.2014
comment
Я внес некоторые изменения в код и сократил время с 2 секунд до примерно 0,4 секунды, но до 0,04 секунды еще далеко. основываясь на ваших ответах, я провел несколько тестов и получил некоторое представление о том, где можно сделать дальнейшие улучшения. Есть ли у вас какие-либо идеи о том, сколько улучшений я получу при использовании объединенной памяти? - person Genutek; 14.01.2014
comment
Объединение памяти может значительно повысить производительность ядра CUDA, попробуйте использовать память текстуры\поверхности для кэшированной памяти только для чтения. Улучшение зависит от алгоритма, но оно может улучшиться на 10 % во время выполнения. - person TripleS; 20.01.2014