В этом ядре есть проблема с гонкой памяти (поэтому правильность чтения после записи):
if (distance < minimum[0]) {
minimum[0] = distance;
minimum[1] = x;
}
При выполнении каждый поток в блоке будет пытаться одновременно прочитать и записать значение минимума. Нет никаких гарантий того, что произойдет, если несколько потоков в деформации пытаются выполнить запись в одну и ту же общую память, и нет никаких гарантий, какие значения будут прочитаны другими деформациями в одном и том же блоке при загрузке из области памяти, в которую выполняется запись. Доступ к памяти не является атомарным, и нет никакой блокировки или сериализации, которые гарантировали бы, что код выполнил тип операции сокращения, которую вы пытаетесь выполнить.
Более мягкая версия той же проблемы относится к обратной записи в глобальную память в конце ядра:
__syncthreads();
globalOutputData[y * 2] = minimum[0];
globalOutputData[y] = minimum[1];
Барьер перед записью гарантирует, что запись до минимума будет завершена до того, как «окончательное» (хотя и непоследовательное) значение будет сохранено в минимуме, но затем каждый поток в блоке выполнит запись.
Если ваше намерение состоит в том, чтобы каждый поток вычислял расстояние, а затем для минимального значения расстояния по блоку для записи в глобальную память, вам придется либо использовать атомарные операции с памятью (для разделяемой памяти это поддерживается только на устройствах Compute 1.2 / 1.3 и 2.x), либо записывать явное сокращение общей памяти. После этого только один поток должен выполнить обратную запись в глобальную память.
Наконец, у вас также есть потенциальная проблема с корректностью синхронизации, которая может привести к зависанию ядра. __syncthreads()
(который отображается на инструкцию панели PTX) требует, чтобы каждый поток в блоке прибыл и выполнил инструкцию до продолжения ядра. Имея такой поток управления:
if (x < critters) {
....
__syncthreads();
....
}
приведет к зависанию ядра, если некоторые потоки в блоке могут разветвляться вокруг барьера и выходить, в то время как другие ожидают на барьере. Никогда не должно быть никакого расхождения ветвлений вокруг вызова __syncthreads (), чтобы гарантировать правильность выполнения ядра в CUDA.
Итак, в заключение вернемся к чертежной доске по крайней мере по трем вопросам в текущем коде.