Я работаю над созданием игровой программы жизненного цикла на основе графического процессора. Если вы не знакомы с этим, вот Wikipedia Page. Я создал одну версию, которая работает, сохраняя массив значений, где 0 - мертвая ячейка, а 1 - живая. Затем ядро просто записывает в массив данных буфера изображений, чтобы нарисовать изображение на основе данных ячейки, а затем проверяет соседние ячейки каждой ячейки для обновления массива ячеек для последующего выполнения рендеринга.OpenCL Общая память среди задач
Однако, более быстрый метод вместо этого представляет значение ячейки как отрицательное число, если мертвое и положительное число, если оно живое. Число этой ячейки представляет собой количество соседей, которые она имеет плюс один (делая нуль невозможным, поскольку мы не можем отличить 0 от -0). Однако это означает, что при нерестах или убийстве ячейки мы должны соответствующим образом обновить значения восьми соседей. Таким образом, в отличие от рабочей процедуры, которая должна считываться только из соседних слотов памяти, эта процедура должна записываться в эти слоты. Это несовместимо, и полученный массив недействителен. Например, ячейки содержат числа, такие как 14, которые указывают 13 соседей, невозможное значение. Код верный, поскольку я написал ту же процедуру на процессоре, и он работает так, как ожидалось. После тестирования я считаю, что когда задачи пытаются записать в память одновременно, происходит задержка, которая приводит к ошибке записи. Например, возможно, существует задержка между чтением данных массива и настройкой, в течение которой данные изменяются, а неправильная процедура другой задачи. Я пробовал использовать семафоры и барьеры, но только что изучил OpenCL и параллельную обработку и не совсем понял их полностью. Ядро выглядит следующим образом.
int wrap(int val, int limit){
int response = val;
if(response<0){response+=limit;}
if(response>=limit){response-=limit;}
return response;
}
__kernel void optimizedModel(
__global uint *output,
int sizeX, int sizeY,
__global uint *colorMap,
__global uint *newCellMap,
__global uint *historyBuffer
)
{
// the x and y coordinates that currently being computed
unsigned int x = get_global_id(0);
unsigned int y = get_global_id(1);
int cellValue = historyBuffer[sizeX*y+x];
int neighborCount = abs(cellValue)-1;
output[y*sizeX+x] = colorMap[cellValue > 0 ? 1 : 0];
if(cellValue > 0){// if alive
if(neighborCount < 2 || neighborCount > 3){
// kill
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i!=0 || j!=0){
int wxc = wrap(x+i, sizeX);
int wyc = wrap(y+j, sizeY);
newCellMap[sizeX*wyc+wxc] -= newCellMap[sizeX*wyc+wxc] > 0 ? 1 : -1;
}
}
}
newCellMap[sizeX*y+x] *= -1;
// end kill
}
}else{
if(neighborCount==3){
// spawn
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i!=0 || j!=0){
int wxc = wrap(x+i, sizeX);
int wyc = wrap(y+j, sizeY);
newCellMap[sizeX*wyc+wxc] += newCellMap[sizeX*wyc+wxc] > 0 ? 1 : -1;
}
}
}
newCellMap[sizeX*y+x] *= -1;
// end spawn
}
}
}
- Массив выход это данные буфера изображения, используемые для визуализации вычислений в ядра.
- sizeX и sizeY константы - ширина и высота буфера изображения соответственно.
- массив colorMap содержит значения целочисленного значения rgb для черно-белого изображения соответственно, которые используются для правильного изменения значений буфера изображения для рендеринга цветов.
- newCellMap массив - это обновленная карта ячейки, которая рассчитывается после определения рендеринга.
- historyBuffer - это старое состояние ячеек в начале вызова ядра. Каждый раз, когда ядро выполняется, этот массив обновляется до массива newCellMap.
Дополнительно обертывание Функция делает пространство тороидальным. Как я могу исправить этот код так, чтобы он работал так, как ожидалось. И почему глобальное обновление памяти с каждым изменением задачи? Разве это не должно быть разделяемой памятью?
Ответ довольно прост. Считывание и запись в одно и то же место памяти из разных потоков в одном вызове ядра не определено. Единственный способ заставить их работать - это барьеры, и даже они работают только в одной рабочей группе. – sharpneli
Значит, это не произойдет в ячейках, граничащих с рабочей группой? Точно так же, как будет барьер в действии? Потому что это не все задачи. –
@HunterLarco Да, он будет терпеть неудачу в ячейках, граничащих с рабочей группой, но только если вы установите надлежащие барьеры. Как вы этого не сделаете, он не работает в любой ячейке. У вас не может быть барьера внутри if, поскольку барьер ** HAS ** встречается всеми рабочими предметами. – DarkZeros