Производительность OpenCL AMD и NVIDIA

Я реализовал простое ядро, которое является своего рода сверткой. Я измерил его на NVIDIA GT 240. При записи на CUDA потребовалось 70 мс, а при записи на OpenCL — 100 мс. Ладно, подумал я, компилятор NVIDIA лучше оптимизирован под CUDA (или я что-то не так делаю). Мне нужно запустить его на графических процессорах AMD, поэтому я перешел на AMD APP SDK. Точно такой же код ядра.

Я провел два теста, и их результаты меня обескуражили: 200 мс на HD 6670 и 70 мс на HD 5850 (столько же, сколько у GT 240 + CUDA). И меня очень интересуют причины такого странного поведения.

Все проекты были построены на VS2010 с использованием настроек примеров проектов NVIDIA и AMD соответственно.

Пожалуйста, не считайте мой пост рекламой NVIDIA. Я прекрасно понимаю, что HD 5850 мощнее GT 240. Единственное, что хотелось бы знать, так это почему такая разница и как решить проблему.

Обновлять. Ниже приведен код ядра, который ищет 6 шаблонных изображений одинакового размера в базовом. Каждый пиксель базового изображения рассматривается как возможный источник одного из шаблонов и обрабатывается отдельным потоком. Ядро сравнивает значения R, G, B каждого пикселя базового изображения и эталонного, и если хотя бы одно отличие превышает параметр diff, соответствующий пиксель считается несовпадающим. Если количество несовпадающих пикселей меньше maxNonmatchQt, соответствующий шаблон срабатывает.

__constant int tOffset = 8196; // one template size in memory (in bytes)
__kernel void matchImage6( __global unsigned char* image, // pointer to the base image
            int imgWidth, // base image width
            int imgHeight, // base image height
            int imgPitch, // base image pitch (in bytes)
            int imgBpp, // base image bytes (!) per pixel
            __constant unsigned char* templates, // pointer to the array of templates
            int tWidth, // templates width (the same for all)
            int tHeight, // templates height (the same for all)
            int tPitch, // templates pitch (in bytes, the same for all)
            int tBpp, // templates bytes (!) per pixel (the same for all)
            int diff, // max allowed difference of intensity
            int maxNonmatchQt, // max number of nonmatched pixels
            __global int* result, // results
                            ) {
int x0 = (int)get_global_id(0);
int y0 = (int)get_global_id(1);
if( x0 + tWidth > imgWidth || y0 + tHeight > imgHeight)
    return;
int nonmatchQt[] = {0, 0, 0, 0, 0, 0};
for( int y = 0; y < tHeight; y++) {
    int ind = y * tPitch;
    int baseImgInd = (y0 + y) * imgPitch + x0 * imgBpp;
    for( int x = 0; x < tWidth; x++) {
        unsigned char c0 = image[baseImgInd];
        unsigned char c1 = image[baseImgInd + 1];
        unsigned char c2 = image[baseImgInd + 2];
        for( int i = 0; i < 6; i++)
            if( abs( c0 - templates[i * tOffset + ind]) > diff || 
                            abs( c1 - templates[i * tOffset + ind + 1]) > diff || 
                            abs( c2 - templates[i * tOffset + ind + 2]) > diff)
                nonmatchQt[i]++;
        ind += tBpp;
        baseImgInd += imgBpp;
    }
    if( nonmatchQt[0] > maxNonmatchQt && nonmatchQt[1] > maxNonmatchQt && nonmatchQt[2] > maxNonmatchQt && nonmatchQt[3] > maxNonmatchQt && nonmatchQt[4] > maxNonmatchQt && nonmatchQt[5] > maxNonmatchQt)
        return;
}
for( int i = 0; i < 6; i++)
    if( nonmatchQt[i] < maxNonmatchQt) {
        unsigned int pos = atom_inc( &result[0]) * 3;
        result[pos + 1] = i;
        result[pos + 2] = x0;
        result[pos + 3] = y0;
    }
}

Конфигурация запуска ядра: Глобальный рабочий размер = (1900, 1200) Локальный рабочий размер = (32, 8) для AMD и (32, 16) для NVIDIA.

Время выполнения: HD 5850 — 69 мс, HD 6670 — 200 мс, GT 240 — 100 мс.

Любые замечания о моем коде также высоко ценятся.


person AdelNick    schedule 23.01.2012    source источник
comment
Здесь недостаточно информации, чтобы ответить на этот вопрос! Каждая из карт NVidia и AMD — архитектурно хитрые звери, и производительность, которую вы видите, во многом зависит от кода; понять разницу в производительности между ними еще сложнее. Можешь выложить свое ядро ​​и драйвер?   -  person Jonathan Dursi    schedule 23.01.2012
comment
Какой алгоритм вы используете в своем ядре? Шаблоны доступа к памяти? размер волнового фронта/деформации? Нужно больше информации, чтобы дать совет.   -  person mfa    schedule 23.01.2012
comment
Сколько потоков вы запускаете? И вы векторизируете массив?   -  person captain    schedule 23.01.2012
comment
спасибо всем за ваши ответы. Я опубликую свой код ядра завтра.   -  person AdelNick    schedule 23.01.2012
comment
Какое соотношение между maxNonmatchQt и размером шаблона (tW*tH) следует ожидать? Эта информация может помочь с оптимизацией. Вы используете 3 байта на пиксель или 3 * n байтов на пиксель? Я предполагаю, что каждый из каналов RGB использует одинаковое количество байтов. Это правильно? Кроме того, не могли бы вы объяснить imgPitch и tPitch, пожалуйста? Я не могу понять это полностью только из кода. Благодарность   -  person mfa    schedule 05.02.2012
comment
Разве ваш локальный размер работы не должен быть фактором вашего глобального размера работы?   -  person MVTC    schedule 05.02.2012
comment
Подожди, как ты это измеряешь? Делаете один прогон ядра? В этом случае вы полностью ограничены задержкой. Выпускаете ли вы поток вызовов ядра и измеряете время выполнения от начала до конца?   -  person doug65536    schedule 17.01.2013
comment
Я что-то упустил или локальная память вообще не используется? В таком случае, зачем заботиться о размере локальной работы?   -  person doug65536    schedule 17.01.2013


Ответы (2)


Разница во времени выполнения вызвана компиляторами. Ваш код можно легко векторизовать. Рассматривайте изображения и шаблоны как массивы векторного типа char4 (четвертая координата каждого вектора char4 всегда равна 0). Вместо 3 память читает:

unsigned char c0 = image[baseImgInd];
unsigned char c1 = image[baseImgInd + 1];
unsigned char c2 = image[baseImgInd + 2];

используйте только один:

unsigned char4 c = image[baseImgInd];

Вместо громоздких, если:

    if( abs( c0 - templates[i * tOffset + ind]) > diff || 
               abs( c1 - templates[i * tOffset + ind + 1]) > diff || 
               abs( c2 - templates[i * tOffset + ind + 2]) > diff)
         nonmatchQt[i]++;

использовать быстро:

    unsigned char4 t = templates[i * tOffset + ind];
    nonmatchQt[i] += any(abs_diff(c,t)>diff);

Таким образом, вы увеличиваете производительность своего кода до 3 раз (если компилятор сам не векторизует код). Я предполагаю, что компилятор AMD OpenCL не делает такой векторизации и других оптимизаций. По моему опыту, OpenCL на GPU NVIDIA обычно можно сделать быстрее, чем CUDA, потому что он более низкоуровневый.

person gudasergey    schedule 27.10.2013
comment
Графические процессоры HD5850 и HD6670 имеют архитектуру, которая поддерживает векторизованный код, но компилятор от AMD не обязательно достаточно умен, чтобы выполнить такое преобразование. Архитектура, используемая в графических процессорах Nvidia, не поддерживает векторизацию. - person chippies; 23.08.2015
comment
Ты прав. Графические процессоры NVIDIA не требуют векторизации, в отличие от графических процессоров AMD. - person gudasergey; 12.09.2015

Точного идеального ответа на этот вопрос быть не может. Производительность OpenCL зависит от многих параметров. Количество доступов к глобальной памяти, эффективность кода и т. д. Более того, очень сложно сравнивать два устройства, поскольку они могут иметь разные локальные, глобальные, постоянные памяти. Количество ядер, частота, пропускная способность памяти, что более важно, аппаратная архитектура и т. д.

Каждое железо дает свой прирост производительности, например native_ от NVIDIA. Поэтому вам нужно больше узнать об оборудовании, на котором вы работаете, которое действительно может работать. Но лично я бы порекомендовал не использовать такие аппаратные оптимизации, которые могут повлиять на гибкость вашего кода.

Вы также можете найти некоторые опубликованные документы, которые показывают, что производительность CUDA намного лучше, чем производительность OpenCL на том же оборудовании NVIDIA.

Поэтому всегда лучше писать код, обеспечивающий хорошую гибкость, а не оптимизацию для конкретных устройств.

person Megharaj    schedule 05.02.2013