CUDA 4.0 RC — много потоков хоста на один GPU — поведение cudaStreamQuery и cudaStreamSynchronize

Я написал код, который использует множество потоков хоста (OpenMP) на один GPU. Каждый поток имеет свой собственный поток CUDA для упорядочения запросов. Это очень похоже на приведенный ниже код:

#pragma omp parallel for num_threads(STREAM_NUMBER)
for (int sid = 0; sid < STREAM_NUMBER; sid++) {
    cudaStream_t stream;
    cudaStreamCreate(&stream);

    while (hasJob()) {

        //... code to prepare job - dData, hData, dataSize etc

        cudaError_t streamStatus = cudaStreamQuery(stream);
        if (streamStatus == cudaSuccess) {
             cudaMemcpyAsync(dData, hData, dataSize, cudaMemcpyHostToDevice, stream);
             doTheJob<<<gridDim, blockDim, smSize, stream>>>(dData, dataSize);
        else {
             CUDA_CHECK(streamStatus);
        }
        cudaStreamSynchronize(stream);
    }
    cudaStreamDestroy(stream);
}

И все было хорошо, пока я не получил много мелких работ. В таком случае время от времени cudaStreamQuery возвращает cudaErrorNotReady, что для меня неожиданно, т.к. я использую cudaStreamSynchronize. До сих пор я думал, что cudaStreamQuery всегда будет возвращать cudaSuccess, если он вызывается после cudaStreamSynchronize. К сожалению, оказалось, что cudaStreamSynchronize может завершиться, даже если cudaStreamQuery все еще возвращает cudaErrorNotReady.

Я изменил код на следующий, и все работает правильно.

#pragma omp parallel for num_threads(STREAM_NUMBER)
for (int sid = 0; sid < STREAM_NUMBER; sid++) {
    cudaStream_t stream;
    cudaStreamCreate(&stream);

    while (hasJob()) {

        //... code to prepare job - dData, hData, dataSize etc

        cudaError_t streamStatus;
        while ((streamStatus = cudaStreamQuery(stream)) == cudaErrorNotReady) {
             cudaStreamSynchronize();
        }
        if (streamStatus == cudaSuccess) {
             cudaMemcpyAsync(dData, hData, dataSize, cudaMemcpyHostToDevice, stream);
             doTheJob<<<gridDim, blockDim, smSize, stream>>>(dData, dataSize);
        else {
             CUDA_CHECK(streamStatus);
        }
        cudaStreamSynchronize(stream);
    }
    cudaStreamDestroy(stream);
}

Итак, мой вопрос .... это ошибка или функция?

РЕДАКТИРОВАТЬ: это похоже на JAVA

synchronize {
    while(waitCondition) {
         wait();
    }
}

person kokosing    schedule 08.03.2011    source источник
comment
Учитывая, что это вопрос об ошибке, а не о функции, возможно, вам лучше задать вопрос на форумах NVIDIA, где кто-то из их команды разработчиков может прояснить это.   -  person Ade Miller    schedule 08.03.2011
comment
@Ade: я сделал это - forums.nvidia.com/index.php?showtopic=194982   -  person kokosing    schedule 08.03.2011
comment
Вы уверены, что правильно инициализировали cuda в каждом потоке? Вы передаете правильный поток cudaStreamSynchronize (в вашем коде нет параметра)? С другой стороны, поток не должен быть готов планировать дополнительные передачи памяти и выполнение ядра. Вот почему это называется потоком...   -  person Jonas Bötel    schedule 08.03.2011
comment
Вы были правы, я добавил stream в качестве параметра в cudaStreamSynchronize. Как я уже писал ранее, я использую 4.0 RC, и, насколько мне известно, для инициализации каждого хост-потока не требуется особой осторожности. Я знаю, что могу планировать задания для потоковой передачи столько, сколько захочу, даже если поток уже выполняется, но я хочу знать, когда конкретный поток завершил свою работу.   -  person kokosing    schedule 09.03.2011


Ответы (2)


Что находится под

//... code to prepare job - dData, hData, dataSize etc

Есть ли у вас там какие-то функции типа cudaMemcpyAsync, или передача памяти только в коде, который вы показали? Эти асинхронные функции могут выйти раньше, даже если код еще не достиг места назначения. Когда это произойдет, cudaStreamQuery вернет cudaSuccess только в случае успешной передачи памяти.

Кроме того, использует ли hasJob() какие-либо функции host-CUDA?

Если я не ошибаюсь, в одном потоке невозможно выполнить и передачу ядра, и передачу памяти. Следовательно, вызов cudaStreamQuery необходим только тогда, когда ядро ​​зависит от данных, передаваемых другим потоком.

person CygnusX1    schedule 09.03.2011
comment
Как вы сказали, это асинхронные функции, поэтому мне интересно, что упомянутое поведение погоды является нормальной ситуацией, которая может произойти со временем. функция, и они могут выйти раньше, чем ожидалось. Были перечислены все вызовы функций cuda, функция hasJob() или код для подготовки задания только в обычном С++. Здесь все потоки независимы, я просто хочу, чтобы какой из них закончил свою работу, и я хочу назначить ему новую работу - динамично и эффективно. - person kokosing; 09.03.2011

Раньше не замечал: cudaStreamSynchronize() должен принимать параметр (stream). Я не уверен, какой поток вы синхронизируете, когда параметр опущен, возможно, по умолчанию он равен потоку 0.

person CygnusX1    schedule 09.03.2011
comment
Вы правы, я случайно пропустил это. Я исправил вопрос. - person kokosing; 09.03.2011
comment
Это решило вашу проблему или это была просто ошибка в коде вопроса, а не в вашем реальном исходном коде? Причина, по которой я спрашиваю, заключается в том, что я считаю, что вы не должны сталкиваться с тем, что вы описываете, и я думаю о возможных ошибках в коде, прежде чем обвинять NVIDIA (хотя у них действительно много ошибок). - person CygnusX1; 09.03.2011
comment
Неа. Только вот это было под вопросом. В моем коде было правильно cudaStreamSynchronize с stream в нем. - person kokosing; 09.03.2011