Итак, вот в чем проблема. Оператор ++ фактически представляет собой три операции в одном: считывает текущее значение, увеличивает его, записывает новое значение. В Aparapi у вас есть потенциально потоки 1024 GPU одновременно. Это означает, что они будут читать значение, вероятно, все равно, когда значение равно 0, а затем увеличит его до 1, тогда все 1024 потока будут писать 1. Таким образом, он действует как ожидалось.
То, что вы пытаетесь сделать, называется функцией Map-reduce. Вы просто пропускаете много шагов. Вы должны помнить, что Aparapi - это система, в которой нет безопасности нитей, поэтому вам нужно написать свои алгоритмы, чтобы это учесть. Именно здесь приходит Map-reduce, и вот как это сделать. Я только что написал это и добавил его в репозиторий Aparapi в своем новом доме, подробнее ниже.
int size = 1024;
final int count = 3;
final int[] V = new int[size];
//lets fill in V randomly...
for (int i = 0; i < size; i++) {
//random number either 0, 1, or 2
V[i] = (int) (Math.random() * 3);
}
//this will hold our values between the phases.
int[][] totals = new int[count][size];
///////////////
// MAP PHASE //
///////////////
final int[][] kernelTotals = totals;
Kernel mapKernel = new Kernel() {
@Override
public void run() {
int gid = getGlobalId();
int value = V[gid];
for(int index = 0; index < count; index++) {
if (value == index)
kernelTotals[index][gid] = 1;
}
}
};
mapKernel.execute(Range.create(size));
mapKernel.dispose();
totals = kernelTotals;
//////////////////
// REDUCE PHASE //
//////////////////
while (size > 1) {
int nextSize = size/2;
final int[][] currentTotals = totals;
final int[][] nextTotals = new int[count][nextSize];
Kernel reduceKernel = new Kernel() {
@Override
public void run() {
int gid = getGlobalId();
for(int index = 0; index < count; index++) {
nextTotals[index][gid] = currentTotals[index][gid * 2] + currentTotals[index][gid * 2 + 1];
}
}
};
reduceKernel.execute(Range.create(nextSize));
reduceKernel.dispose();
totals = nextTotals;
size = nextSize;
}
assert size == 1;
/////////////////////////////
// Done, just print it out //
/////////////////////////////
int[] results = new int[3];
results[0] = totals[0][0];
results[1] = totals[1][0];
results[2] = totals[2][0];
System.out.println(Arrays.toString(results));
Имейте ввиду, что, хотя это может показаться неэффективным, на самом деле он работает очень хорошо на гораздо большем количестве. Этот алгоритм работает просто отлично с
size = 1048576.
С нового размером следующего результатом, рассчитанным на моей системе примерно за секунду.
[349602, 349698, 349276]
Последнее замечание, вы можете рассмотреть вопрос о переходе к более активному проекту на aparapi.com. Он включает в себя несколько исправлений ошибок и множество дополнительных функций и улучшений производительности над старой библиотекой, с которой вы связаны выше. Он также находится в центральной части с примерно дюжиной релизов. поэтому его легче использовать. Я просто написал код в этом ответе, но решил использовать его в новом разделе примера репозитория Aparapi, вы можете найти это на the following link in the new Aparapi repository.
Письмо гонки состояние. Вам нужны атомные функции приращения. –