2013-03-04 3 views
1

Я пытаюсь ускорить следующий бит кода CUDA, используя несколько потоков.Асинхронные потоки в CUDA не дают увеличения производительности

#define N (4096 * 4096) 
#define blockDimX 16 
#define blockDimY 16 

float domain1 [N]; 
float domain2 [N]; 

__global__ updateDomain1_kernel(const int dimX, const int dimY) { 
    // update mechanism here for domain1 
    // ... 
} 

__global__ updateDomain2_kernel(const int dimX, const int dimY) { 
    // update mechanism here for domain2, which is nearly the same 
    // ... 
} 

__global__ addDomainsTogether_kernel(float* domainOut, 
            const int dimX, 
            const int dimY) 
{ 
    // add domain1 and domain2 together and fill domainOut 
} 

void updateDomains(float* domainOut) { 
    dim3 blocks((dimX + blockDimX - 1)/blockDimX , (dimY + blockDimY- 1)/blockDimY); 
    dim3 threads(blockDimX, blockDimY); 

    updateDomain1_kernel<<<blocks, threads>>> (dimX, dimY); 
    updateDomain2_kernel<<<blocks, threads>>> (dimX, dimY); 
    addDomainsTogether_kernel<<<block, threads>>> (domainOut_gpu, dimX, dimY); 
    cudaMemcpy(domainOut, domainOut_gpu, N * sizeof(float), cudaMemcpyDeviceToHost); 
} 

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

void updateDomains(float* domainOut) { 
    dim3 blocks((dimX + blockDimX - 1)/blockDimX , (dimY + blockDimY- 1)/blockDimY); 
    dim3 threads(blockDimX, blockDimY); 

    cudaStream_t stream0, stream1; 
    cudaStreamCreate(&stream0); 
    cudaStreamCreate(&stream1); 

    updateDomain1_kernel<<<blocks, threads, 0, stream0>>> (dimX, dimY); 
    updateDomain2_kernel<<<blocks, threads, 0, stream1>>> (dimX, dimY); 
    cudaDeviceSynchronize(); 

    addDomainsTogether_kernel<<<block, threads>>> (domainOut_gpu, dimX, dimY); 
    cudaMemcpy(domainOut, domainOut_gpu, N * sizeof(float), cudaMemcpyDeviceToHost); 

    cudaStreamDestroy(stream0); 
    cudaStreamDestroy(stream1); 
} 

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

void updateDomains(float* domainOut) { 
    dim3 blocks((dimX + blockDimX - 1)/blockDimX , (dimY + blockDimY- 1)/blockDimY); 
    dim3 threads(blockDimX, blockDimY); 

    cudaStream_t stream0; 
    cudaStreamCreate(&stream0); 

    updateDomain1_kernel<<<blocks, threads, 0, stream0>>> (dimX, dimY); 
    updateDomain2_kernel<<<blocks, threads, 0, stream0>>> (dimX, dimY); 

    addDomainsTogether_kernel<<<block, threads0, stream0>>> (domainOut_gpu, dimX, dimY); 
    cudaMemcpy(domainOut, domainOut_gpu, N * sizeof(float), cudaMemcpyDeviceToHost); 

    cudaStreamDestroy(stream0); 
} 

Однако, опять-таки вряд ли какая-то разница в скорость работы. Во всяком случае, последний кажется самым быстрым. Что заставляет меня думать, что есть что-то о потоках CUDA, которые я не понимаю. Может кто-нибудь просветить меня о том, как ускорить этот код?

+0

Какова ваша вычислительная мощность? Существует множество правил уважения, чтобы потоки работали правильно. Предлагаю вам прочитать [this] (http://developer.download.nvidia.com/CUDA/training/StreamsAndConcurrencyWebinar.pdf). –

+0

Я прочитал этот документ, но в основном речь идет о разделении данных, что немного смутило меня. Я использую вычислительную возможность 2.0, кстати. – Yellow

ответ

1

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

+0

Но как узнать, достаточный ли параллелизм? То есть как я узнаю, что первый пример является достаточным? И означает ли это, что вторая реализация может повысить производительность на другом компьютере (с лучшим графическим процессором) или что-то не так в коде? – Yellow

+2

Это хороший ответ. Чтобы добавить некоторые особенности, сетка для этих ядер - это (4096/16) * (4096/16) = 65536 потоков. Эти многоблочные блоки в одном запуске ядра полностью заполнят машину, предотвращая выполнение каких-либо потоков из последующих запусков ядра, до тех пор, пока почти все блоки потоков из первого запуска ядра не будут исчерпаны. Этот вопрос является дубликатом нескольких других, таких как [этот] (http://stackoverflow.com/questions/14124673/cuda-kernels-not-executing-concurrently/14271418#14271418). –

+0

Ага, спасибо за разъяснение. – Yellow

Смежные вопросы