где ядра могут читать и писать из одного объекта ядра из нескольких потоков?
Под «ядрами» вы подразумеваете фрагменты кода, выполняемые на GPU, а под «одним объектом ядра» вы имеете в виду cl_kernel в коде хоста? Ядра на GPU никогда не видят структуру cl_kernel , которая существует на стороне хоста. Я предполагаю, что вы говорите об использовании аргументов буферного объекта ( cl_mem ) ядрами.
Вы можете думать о cl_kernel как:
struct {
size_t num_args;
void* args[];
} _cl_kernel;
typedef struct _cl_kernel * cl_kernel;
Если вы вызываете clSetKernelArg (), он просто устанавливает что-то в этой структуре. Если вы вызываете clEnqueueNDRangeKernel (), он берет моментальный снимок структуры cl_kernel (аргументов) и добавляет его в некоторую внутреннюю очередь устройства. Под «снимком» я не имею в виду, что он создает скрытый снимок фактического содержимого буфера cl_mem ; он просто копирует ссылку на аргументы cl_mem . Поскольку это ссылка, на самом деле не имеет значения, используете ли вы один объект cl_kernel из нескольких потоков или вызываете clCreateKernel несколько раз с тем же именем, а затем используете эти cl_kernel в каждая нить; это просто вопрос удобства, конечный результат тот же.
Если у вас есть одна упорядоченная очередь команд, ваши ядра будут выполняться детерминированно, в порядке очереди. Если у вас есть несколько очередей команд (в порядке или вне очереди, не имеет значения), не будет никакого неявного упорядочивания между очередями , поэтому, если вы поставите в очередь одно и то же ядро во все очереди, они будут выполняться в случайном порядке. Вы можете принудительно указать явный порядок с помощью событий. IOW, вы делаете:
cl_event event1, event2;
cl_kernel K;
...
clEnqueueNDRangeKernel(queue_1, K, ... , &event1);
clEnqueueNDRangeKernel(queue_2, K, ... , 1, &event1, &event2);
et c. Это заставит выполнение ядра ожидать на предыдущем, даже если они находятся в разных очередях. Но у вас будет только одно ядро, использующее буфер одновременно.
Если вы хотите использовать один и тот же буфер одновременно несколькими запущенными ядрами, тогда это зависит от модели использования этого буфера . Если вы выполняете только чтение, вы можете безопасно использовать буфер из любого количества ядер одновременно. Для использования записи, если вы знаете, что будете писать только в часть буфера, вы можете попробовать использовать суббуферы (clCreateSubBuffer). В противном случае вам, вероятно, не повезло (ну, может, вы могли бы попробовать atomi c ops, но это может сделать алгоритм непривычно медленным).