Увеличение переменной count в RenderScript

Я хочу подсчитать пиксели растрового изображения, используя следующий код RenderScript.

Рендерскрипт

Имя файла: counter.rs

#pragma version(1)
#pragma rs java_package_name(com.mypackage)
#pragma rs_fp_relaxed

uint count; // initialized in Java
void countPixels(uchar4* unused, uint x, uint y) {
  rsAtomicInc(&count);
}

Джава

Application context = ...; // The application context
RenderScript rs = RenderScript.create(applicationContext);

Bitmap bitmap = ...; // A random bitmap
Allocation allocation = Allocation.createFromBitmap(rs, bitmap);

ScriptC_Counter script = new ScriptC_Counter(rs);
script.set_count(0);
script.forEach_countPixels(allocation);

allocation.syncAll(Allocation.USAGE_SCRIPT);
long count = script.get_count();

Ошибка

Это сообщение об ошибке, которое я получаю:

ОШИБКА: Адрес не найден для подсчета

Вопросы

  • Почему мой код не работает?
  • Как я могу это исправить?

Ссылки


person winklerrr    schedule 22.12.2016    source источник


Ответы (2)


В качестве примечания: обычно не рекомендуется использовать атомарные операции в параллельных вычислениях, если в этом нет необходимости. RenderScript фактически предоставляет ядро сокращения для этого вид приложения. Может быть, вы можете попробовать.

Есть несколько проблем с кодом:

  1. Переменная count должна была быть объявлена ​​как volatile.
  2. countPixels должен был быть "void RS_KERNEL countPixels (uchar4 in)"
  3. script.get_count() не даст вам актуальное значение «count», вам нужно вернуть значение с помощью распределения.

Если вам нужно использовать rsAtomicInc, хорошим примером являются тесты RenderScript CTS:

AtomicTest. р

AtomicTest.java

person Miao Wang    schedule 23.12.2016
comment
В этом случае мне нужно ядро ​​сопоставления, потому что мне нужно сделать некоторые другие вещи, которые я должен посчитать. - person winklerrr; 24.01.2017

Вот мое рабочее решение.

Рендерскрипт

Имя файла: counter.rs

#pragma version(1)
#pragma rs java_package_name(com.mypackage)
#pragma rs_fp_relaxed

int32_t count = 0;
rs_allocation rsAllocationCount;

void countPixels(uchar4* unused, uint x, uint y) {
  rsAtomicInc(&count);
  rsSetElementAt_int(rsAllocationCount, count, 0);
}

Джава

Context context = ...;
RenderScript renderScript = RenderScript.create(context);

Bitmap bitmap = ...; // A random bitmap
Allocation allocationBitmap = Allocation.createFromBitmap(renderScript, bitmap);
Allocation allocationCount = Allocation.createTyped(renderScript, Type.createX(renderScript, Element.I32(renderScript), 1));

ScriptC_Counter script = new ScriptC_Counter(renderScript);
script.set_rsAllocationCount(allocationCount);
script.forEach_countPixels(allocationBitmap);

int[] count = new int[1];
allocationBitmap.syncAll(Allocation.USAGE_SCRIPT);
allocationCount.copyTo(count);

// The count can now be accessed via
count[0];
person winklerrr    schedule 24.01.2017
comment
Это неправильно. Вы не можете писать в одну и ту же ячейку памяти несколькими потоками без атомарной операции. Вы должны написать вызываемую однопоточную функцию с этим вызовом rsSetElementAt и вызывать после вызова ядра. - person sakridge; 24.01.2017
comment
Вот почему я использую rsAtomicInc() - person winklerrr; 24.01.2017
comment
rsAtomicInc в порядке. Проблема заключается в вызове rsSetElementAt. Это запись в rsAllocationCount из нескольких потоков. Вы не гарантируете, что получите правильный ответ. - person sakridge; 24.01.2017
comment
Неважно, из какого потока произойдет последний вызов rsSetElementAt_int(), потому что он использует переменную count, которая была атомарно увеличена и, следовательно, имеет правильное значение. - person winklerrr; 25.01.2017
comment
Нет. Atomic только гарантирует, что переменная count будет правильной после выполнения. Рассмотрим простой пример, где программа: atomic_inc load &count -> r0; сохранить r0 -> rsAllocationCount[0]. У вас есть 2 потока 0 и 1 и один процессор. Поток 0 запланирован и выполняет атомарную обработку, а затем загружает r0 со счетчиком, который теперь равен 1. Поток 0 прерывается, и теперь поток 1 запланирован и завершает программу. Он увеличивает счетчик до 2 и сохраняет 2 в памяти. Затем планируется поток 0, и он затем записывает r0 в память, которая по-прежнему равна 1. Теперь у вас неправильный ответ. - person sakridge; 25.01.2017
comment
Если бы не правильность, вы делаете гораздо больше записей, чем необходимо. Гораздо быстрее будет избежать всего этого и просто сделать один в конце с окончательным результатом. - person sakridge; 26.01.2017
comment
Это правильно, но это не меняет того факта, что мой ответ решает мою проблему. Если вы опубликуете ответ с еще более высокой производительностью, я приму ваш ответ как лучший. - person winklerrr; 26.01.2017