Как я могу обеспечить порядок между записью и чтением в глобальную память?

У меня есть ядро ​​CUDA следующей формы:

Void launch_kernel(..Arguments...) 
{  
    int i = threadIdx.x
    //Load required data 
    int temp1 = A[i];
    int temp2 = A[i+1];
    int temp3= A[i+2]; 
    // compute step 
    int output1 =  temp1 + temp2 + temp3;
    int output2 = temp1 + temp3;  
    // Store the result
    B[i] = output1; 
    C[i] = output2;
}  

Как обсуждалось в руководстве CUDA, модель согласованности для глобальной памяти графического процессора не является последовательной. В результате может показаться, что операции с памятью выполняются в порядке, отличном от порядка исходной программы. Чтобы принудительно упорядочить память, CUDA предлагает функции __threadfence (). Однако, согласно руководству, такая функция обеспечивает относительный порядок операций чтения и относительный порядок операций записи. Цитата из руководства:

Все записи в общую и глобальную память, сделанные вызывающим потоком перед вызовом __threadfence_block (), наблюдаются всеми потоками в блоке вызывающего потока, как происходящие до всех записей в общую память и глобальную память, сделанных вызывающим потоком после вызова. to __threadfence_block ();

Таким образом, очевидно, что __threadfence () недостаточно для обеспечения упорядочения операций чтения и записи.

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


person Abhishek    schedule 10.07.2017    source источник
comment
Самый короткий ответ: вы не можете. Если у вас есть проблемы, которые обязательно должны иметь гарантированный порядок транзакций с памятью, тогда у вас есть проблема, которая, вероятно, полностью не подходит для CUDA.   -  person talonmies    schedule 10.07.2017
comment
вы, конечно, можете принудительно упорядочить активность всех потоков в блоке. Ваш вопрос неясен в отношении объема приказа, который вы хотите применить. Вы пытаетесь принудительно упорядочить только действия из определенного потока?   -  person Robert Crovella    schedule 10.07.2017
comment
Я хочу принудительно упорядочить действия с памятью в потоке. В частности, как видно из примера ядра, я просто хочу убедиться, что каждый поток завершает свой раздел чтения перед выполнением раздела вычислений и хранения. Я, конечно, не стремлюсь принудительно упорядочивать операции с памятью в нескольких потоках.   -  person Abhishek    schedule 11.07.2017
comment
Ваш код в порядке. Барьеры памяти в первую очередь связаны с обеспечением видимости деятельности для других наблюдателей. Если бы поведение одного потока было таким, как вы предполагаете, CUDA была бы ужасно нарушена. Вы можете ожидать, что в одном потоке действия, выполненные перед оператором, завершатся до того, как этот оператор будет выполнен, если такое утверждение действительно повлияет на правильность кода. В вашем коде все загрузки temp1, temp2 и temp3 будут правильно выполняться до того, как они будут использоваться для вычисления промежуточных величин output1 и output2. Аналогично для B[i], C[i]   -  person Robert Crovella    schedule 11.07.2017


Ответы (1)


Как сказал @RobertCrovella в своем комментарии, ваш код и так будет работать нормально.

temp1, temp2 и temp3 являются локальными (которые будут использовать либо регистры, либо локальную память {глобальная память для каждого потока}). Они не разделяются между потоками, поэтому нет никаких проблем с параллелизмом. Они будут работать как обычный C / C ++.

A, B и C являются глобальными. Это будет предметом проблем синхронизации. A используется только для чтения, поэтому порядок доступа не имеет значения. B и C записываются, но каждый поток записывает только в свой индекс, поэтому порядок их записи не имеет значения. Вам не нужно беспокоиться о том, чтобы гарантировать завершение чтения глобальной памяти. В потоке ваш код будет выполняться в порядке написания с соответствующими остановками для доступа к глобальной памяти. Вы не захотите этого из соображений производительности, но вы можете делать такие вещи, как B [i] = 0; B [i] = 5; temp1 = B [я]; и иметь temp1 гарантированно равным 5.

В этом примере вы не используете разделяемую память, однако она является локальной для блоков потока, и вы можете синхронизировать внутри блока потока с помощью __syncthreads ();

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

person Jonathan Olson    schedule 12.07.2017
comment
Отличный ответ. Согласно моей информации, даже если код выполняется в соответствии с исходным порядком программы, из-за буферов загрузки / хранения, чтение / запись, наблюдаемое в памяти, может отличаться от исходного порядка программы. В моем случае я согласен с тем, что все намного проще из-за отсутствия связи между потоками. Наконец, важна моя забота о завершении чтения глобальной памяти перед выполнением следующих разделов, поскольку мой статический анализ (для программы CUDA) предполагает, что все операции с памятью выполняются в текстовом порядке. Поэтому я хочу убедиться, что анализ остается правильным во время выполнения (на слабых моделях памяти). - person Abhishek; 19.07.2017