проблемы с чтением из __глобальной памяти после atom_inc в OpenCL

OpenCL не имеет глобального барьера, который остановит все потоки, поэтому я пытаюсь создать обходной путь с помощью следующего кода:

void barrier(__global uint* scratch) {
  uint nThreads = get_global_size(0);
  atom_inc(scratch);
  /* this loop never terminates */
  while(scratch[0] < nThreads) {
    continue;
  }
}

Идея состоит в том, что каждый поток зацикливается до тех пор, пока все они не увеличивают этот фрагмент памяти.

Однако значение, прочитанное с нуля[0], никогда не меняется для потоков после того, как оно было прочитано, и оно зацикливается навсегда. Я знаю, что он увеличивается, потому что это правильное значение, когда я считываю его обратно на хост.

Кэшируется ли глобальная память локально? Что тут происходит?


person Steve Blackwell    schedule 29.09.2010    source источник
comment
Какая платформа/драйвера/и т.д. вы тестируете это на?   -  person runexe    schedule 30.09.2010
comment
почему вы хотите это сделать? Синхронизация между рабочими группами не может работать, поскольку они могут не работать одновременно. И даже если это будет работать: по крайней мере, для gpu (opencl только для процессора не имеет смысла...), это будет невероятно медленно, так как он должен сериализовать все эти обращения к глобальной памяти. Таким образом, даже если вы используете только 10000 потоков (которые будут выполняться полностью одновременно на графических процессорах среднего и высокого уровня) и атомарные операции работают на чипе (так что только Fermi), такой барьер все равно будет стоить 1000000 циклов (каждый поток имеет задержку в несколько циклов как для чтения, так и для записи). , по одному потоку за раз)   -  person Grizzly    schedule 01.10.2010
comment
Стандартным способом использования глобальной синхронизации в opencl является запуск нового ядра, которое должно быть намного быстрее при разумном количестве потоков (сейчас я не уверен насчет nvidia, но amd указывает 225 мкс в качестве задержки ядра, в то время как глобальный барьер, подобный этому, будет стоить несколько мс.   -  person Grizzly    schedule 01.10.2010
comment
То, что рабочие группы не обязательно работают одновременно, является ключевым фактом, которого я не знал, и именно поэтому я разместил это в ответе ниже. Я меньше беспокоился о производительности, так как мне приходилось делать это только один раз за выполнение.   -  person Steve Blackwell    schedule 01.10.2010
comment
Если производительность не имеет значения, то еще меньше причин не просто разделить функциональность на несколько ядер и использовать запуск нового ядра в качестве точки синхронизации (конечно, поскольку большие ядра означают больше регистров/потоков, а меньшие ядра могут даже повысить производительность ). Кроме того, я все еще думаю, что в долгосрочной перспективе есть соображения производительности, потому что зачем еще использовать opencl. Но я просто хотел пояснить, почему это плохая идея, даже если она сработает, ведь другие могут попробовать с меньшим количеством рабочих групп ;).   -  person Grizzly    schedule 03.10.2010


Ответы (1)


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

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

Если реализация (у меня Radeon 5750, использующая Stream SDK 2.2) одновременно выполняет все рабочие группы, то, вероятно, это не будет проблемой. Но это не относится к моей установке.

person Steve Blackwell    schedule 30.09.2010