производительность — C ++ AMP: array_view и index ведут себя странно (неожиданные значения сохранены)

Я написал небольшую тестовую функцию, которая ведет себя не так, как я хочу.

По сути, он должен прочитать массив и записать обратно его содержимое (позже, когда это сработает, он должен сделать больше, но сейчас даже это не удается).

Отладка в коде GPU, я увидел, что первые несколько итераций (как-то выполняются параллельно … что, вероятно, имеет смысл для GPU, но удивляет меня при отладке) работают нормально … но потом, после 1-2 Debug-Continues (F5), некоторые ранее правильно установленные значения перезаписываются нулями. Я действительно не понимаю .. к тому времени, когда я снова на ЦПУ, многие значения равны 0, хотя они не должны быть 0 (в основном, они должны иметь исходные данные, что является простой последовательностью тестирования).

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

typedef unsigned char byte;

using namespace concurrency;

void AMPChangeBrightnessContrastWrapper2(byte* a, int len, float brightness, float contrast)
{
array_view<unsigned int> dst(len/4, (unsigned int*)a);
//dst.discard_data();
parallel_for_each(dst.extent, [=](index<1> idx) restrict(amp)
{
// split into bytes (in floats)
float temp1 = (dst[idx])  - (dst[idx] >> 8) * 256;
// this completely fails! float temp1 = dst[idx] & 0xFF;
float temp2 = (dst[idx] >> 8)  - (dst[idx] >> 16) * 256;
float temp3 = (dst[idx] >> 16) - (dst[idx] >> 24) * 256;
float temp4 = (dst[idx] >> 24);
// convert back to int-array
dst[idx] = (int)(temp1 + temp2 * 256 + temp3 * 65536 + temp4 * 16777216);

});
//dst.synchronize();
}

int _tmain(int argc, _TCHAR* argv[])
{
const int size = 30000;
byte* a = new byte[size];

// generate some unique test sequence.. first 99 numbers are just 0..98
for (int i = 0; i < size; ++i)
a[i] = (byte)((i + i / 99) % 256);

AMPChangeBrightnessContrastWrapper2(a, size, -10.0f, 1.1f);

for (int i = 0; i < 50; ++i)
printf("%i, ", a[i]);
char out[20];
scanf_s("%s", out);
return 0;
}

Итак, простые (запланированные) шаги:

  • инициализировать массив
  • передать массив в GPU (как массив unsigned int)
  • разбить каждый беззнаковый int на 4 байта и сохранить их в числах с плавающей точкой
  • (сделать некоторые вычисления, опущенные здесь для простоты)
  • конкатенировать байты, сохраненные в плавающих снова в исходное положение
  • (повторение)

В случае, если вам интересно .. это должны быть значения цвета ..

Результат:

  • некоторые значения, как и ожидалось, большинство имеют разные значения, хотя
  • похоже, что особенно байт 0 (каждого целого без знака) будет иметь неправильное значение
  • Сначала я попытался преобразовать неподписанные int-> byte-> float с помощью & 0xFF но это, похоже, полностью провалилось

Вывод (но это должно быть просто увеличение числа, начиная с 0):

0, 1, 2, 3, 0, 5, 6, 7, 0, 9, 10, 11, 16, 13, 14, 15, 0, 17, 18, 19, 32, 21, 22,
23, 32, 25, 26, 27, 32, 29, 30, 31, 0, 33, 34, 35, 64, 37, 38, 39, 64, 41, 42,
43, 64, 45, 46, 47, 64, 49,

Вопросы:

  • почему проблема с & 0xFF?
  • почему байт 0 каждого беззнакового целого получает странное назначенное значение?
  • Я полагаю, я не могу создать array_view байтов, я должен использовать int или float?
  • комментирование .synchronize в итоге ничего не изменило — как получилось?

1

Решение

• Я полагаю, я не могу создать массив_байт байтов, я должен использовать целые числа или числа с плавающей запятой?

Вы не можете создать массив или array_view байта. C ++ AMP поддерживает только ограниченное подмножество типов C ++. Вы можете использовать текстуру, а не вид массива. Для обработки изображений у этого есть несколько преимуществ, не в последнюю очередь упаковка и распаковка намного быстрее, потому что это реализовано аппаратным обеспечением графического процессора. Смотрите полный пример ниже.

• комментирование .synchronize в конце концов ничего не изменило — как получилось?

Вам не нужно dst.synchronize() поскольку dst array_view выходит из области видимости, что вызывает неявную синхронизацию данных обратно в память процессора. Между прочим, вы не должны звонить dst.discard_data() в начале функции, потому что если вы сделаете это, это будет означать, что данные из a не будет скопирован в графический процессор.

Вот реализация с использованием текстуры<>. Обратите внимание:

  • Использование текстуры unit_4 позволяет получить упаковку и распаковку
    ваши данные для «бесплатно».
  • Использование Clamp () лучше, чем если бы предложения, во-первых, он использует встроенную функцию, которая
    аппаратное обеспечение оптимизировано для. Вообще ветвление в ядрах плохо, потому что оно останавливает все
    нити в деформации, даже если они оценивали условие как ложное.
  • Вам нужны две текстуры, потому что, в отличие от массивов, они не поддерживают псевдонимы.
  • Я удалил некоторые временные переменные. Переменные используют регистровое пространство, которое очень ограничено
    ГПУ. Вы должны минимизировать его использование, чтобы убедиться, что все ваши потоки могут выполняться
    не ждет, пока освободится место в реестре.
  • Явное приведение с использованием static_cast<> означает меньше предупреждений компилятора и вообще считается
    быть хорошим (современным) стилем C ++.

И код …

void AMPChangeBrightnessContrastWrapper3(const byte* a, const int len,
const float brightness, const float contrast)
{
const int pixel_len = len / 4;
graphics::texture<graphics::uint_4, 1> inputTx(pixel_len, a, len, 8u);
graphics::texture<graphics::uint_4, 1> outputTx(pixel_len, 8u);
graphics::writeonly_texture_view<graphics::uint_4, 1> outputTxVw(outputTx);

parallel_for_each( outputTxVw.extent, [=, &inputTx, &outputTx](index<1> idx)
restrict(amp)
{
const graphics::uint_4 v = inputTx[idx];

float tmp = static_cast<float>(v.r);
tmp = (tmp - 128) * contrast + brightness + 128;
tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
const unsigned int temp1_ = static_cast<unsigned int>(tmp);

tmp = static_cast<float>(v.g);
tmp = (tmp - 128) * contrast + brightness + 128;
tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
const unsigned int temp2_ = static_cast<unsigned int>(tmp);

tmp = static_cast<float>(v.b);
tmp = (tmp - 128) * contrast + brightness + 128;
tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
const unsigned int temp3_ = static_cast<unsigned int>(tmp);

tmp = static_cast<float>(v.a);
tmp = (tmp - 128) * contrast + brightness + 128;
tmp = direct3d::clamp(tmp, 0.0f, 255.0f);
const unsigned int temp4_ = static_cast<unsigned int>(tmp);

outputTxVw.set(idx, graphics::uint_4(temp1_, temp2_, temp3_, temp4_));
});
copy(outputTx, (void*)a, len);
}

Вы можете найти гораздо больше примеров C ++ AMP в AMP Book

4

Другие решения

Хм .. так, чтобы ответить на мои собственные вопросы после еще нескольких проб и ошибок и ошибок снова:

  • & 0xFF работает нормально, как и >> <<
  • это была моя проблема не генерации правильных значений, особенно, я не учел (очень ограниченную) точность float, которая значительно ниже точности int (ну, mantisse)
  • до сих пор не знаю
  • до сих пор не знаю, но он работает одинаково (результат / скорость) с и без .synchronize

Если вы сталкиваетесь с чем-то похожим или нуждаетесь в нем, вот решение (как первоначально предполагалось), меняющее яркость и контрастность в массиве значений яркости:

void AMPChangeBrightnessContrastWrapper
(byte* a, int len, float brightness, float contrast)
{
array_view<unsigned int> dst(len/4, (unsigned int*)a);
parallel_for_each(dst.extent, [=](index<1> idx) restrict(amp)
{
float temp1 = dst[idx] & 0xFF;
temp1 = (temp1 - 128) * contrast + brightness + 128;
if (temp1 < 0)
temp1 = 0;
if (temp1 > 255)
temp1 = 255;

float temp2 = (dst[idx] >> 8) & 0xFF;
temp2 = (temp2 - 128) * contrast + brightness + 128;
if (temp2 < 0)
temp2 = 0;
if (temp2 > 255)
temp2 = 255;

float temp3 = (dst[idx] >> 16) & 0xFF;
temp3 = (temp3 - 128) * contrast + brightness + 128;
if (temp3 < 0)
temp3 = 0;
if (temp3 > 255)
temp3 = 255;

float temp4 = (dst[idx] >> 24);
temp4 = (temp4 - 128) * contrast + brightness + 128;
if (temp4 < 0)
temp4 = 0;
if (temp4 > 255)
temp4 = 255;

unsigned int temp1_ = (unsigned int)temp1;
unsigned int temp2_ = (unsigned int)temp2;
unsigned int temp3_ = (unsigned int)temp3;
unsigned int temp4_ = (unsigned int)temp4;
unsigned int res = temp1_ + (temp2_ << 8) + (temp3_ << 16) + (temp4_ << 24);
dst[idx] = res;
});
dst.synchronize();
}

Кроме того, несмотря на то, что я (кажется, я) делаю мало вычислений, он работал в 2-4 раза быстрее (выпуск / отладка), чем на процессоре с Intel HD 4000.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector