Cuda atomics меняет флаг

У меня есть кусок последовательного кода, который делает что-то вроде этого

if( ! variable )
{
  do some initialization here 
  variable = true;
}

Я понимаю, что это отлично работает в сериале и будет выполняться только один раз. Какая операция атома будет правильной здесь, в CUDA?

1 ответ

Решение

Мне кажется, что вы хотите, чтобы "критический раздел" в вашем коде. Критическая секция позволяет одному потоку выполнять последовательность команд, в то же время предотвращая выполнение этих инструкций любым другим потоком или блоком потоков.

Например, критическая секция может использоваться для управления доступом к области памяти, чтобы разрешить бесконфликтный доступ к этой области одним потоком.

Атомика сама по себе может использоваться только для очень ограниченной, в основном единственной операции, над одной переменной. Но атомика может быть использована для создания критической секции.

Вы должны использовать следующий код в вашем ядре, чтобы контролировать поток доступа к критическому разделу:

__syncthreads();
if (threadIdx.x == 0)
  acquire_semaphore(&sem);
__syncthreads();
  //begin critical section
  // ... your critical section code goes here
  //end critical section
__syncthreads();
if (threadIdx.x == 0)
  release_semaphore(&sem);
__syncthreads();

Перед ядром определите эти вспомогательные функции и переменную устройства:

__device__ volatile int sem = 0;

__device__ void acquire_semaphore(volatile int *lock){
  while (atomicCAS((int *)lock, 0, 1) != 0);
  }

__device__ void release_semaphore(volatile int *lock){
  *lock = 0;
  __threadfence();
  }

Я протестировал и успешно использовал приведенный выше код. Обратите внимание, что он по существу осуществляет арбитраж между блоками потоков, используя поток 0 в каждом блоке потоков в качестве запросчика. Вы должны далее условие (например, if (threadIdx.x < ...)) код критической секции, если вы хотите, чтобы только один поток в блоке-победителе выполнял код критической секции.

Наличие нескольких потоков в варп-арбитре для семафора представляет дополнительные сложности, поэтому я не рекомендую такой подход. Вместо этого, пусть каждый потоковый блок осуществляет арбитраж, как я показал здесь, а затем управляйте своим поведением в пределах победившего потокового блока, используя обычные методы связи / синхронизации потокового блока (например, __syncthreads(), общая память и т. д.)

Обратите внимание, что эта методология будет дорогостоящей для производительности. Вы должны использовать критические секции только тогда, когда вы не можете понять, как иначе распараллелить ваш алгоритм.

Наконец, слово предупреждения. Как и в любой параллельной параллельной архитектуре, неправильное использование критических секций может привести к тупику. В частности, допущение относительно порядка выполнения потоковых блоков и / или деформаций внутри потокового блока является ошибочным подходом.

Другие вопросы по тегам