本文作者:LL0229

初识CUDA编程:理解线程索引与Warp分组

LL0229 2024-09-08 822 抢沙发
初识CUDA编程:理解线程索引与Warp分组摘要: ...
  1. 线程索引与全局计算:CUDA中的基础定位机制

在CUDA编程中,每个线程都有一个唯一的全局索引,它由线程在块中的位置(threadIdx)以及块在网格中的位置(blockIdx)共同决定。这个全局索引使得每个线程能够在并行计算中独立地访问和处理特定的数据位置,从而实现数据的分布式计算和处理。2. 块(Block)与网格(Grid):分层并行计算的架构

CUDA将计算任务组织为多个块(blocks),每个块又包含若干线程。所有块组成一个网格(grid),并行执行在GPU上。这种分层结构让程序能够灵活地分配和管理大量并行线程,充分利用GPU的计算能力。块的索引(blockIdx)和线程的索引(threadIdx)共同用于在程序中定位和管理计算任务。

3. Warp:GPU中的基础执行单元

在CUDA架构中,warp是执行任务的最小单元,通常由32个连续的线程组成。所有的warp内线程同时执行相同的指令,通过优化warp内的指令调度,CUDA能够大幅提高并行计算的效率。理解warp的工作机制对于编写高效的CUDA程序至关重要,因为warp内的线程分支和内存访问模式将直接影响性能。

4. GPU内存管理:从CPU到GPU的数据传输

在CUDA编程中,GPU内存管理是性能优化的关键因素。数据需要从CPU传输到GPU进行并行计算,然后再将结果传回。使用cudaMalloccudaMemcpy等函数,可以在GPU上动态分配内存和实现主机(CPU)与设备(GPU)之间的数据交换,确保程序在大规模数据并行处理中保持高效。

5. 同步与异步:CUDA中的并行执行模型

CUDA程序中的内核函数(kernel)在设备上执行时,主机(CPU)和设备(GPU)之间是异步的,意味着CPU可以在不等待GPU完成任务的情况下继续执行其他操作。这种异步执行模式提高了程序的整体效率,但也需要程序员注意同步问题,如使用cudaMemcpy确保数据在正确的时间被传输和使用。#include <stdio.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>

int getch(void) {
    struct termios oldattr, newattr;
    int ch;
    tcgetattr(STDIN_FILENO, &oldattr); // 获取终端属性
    newattr = oldattr;
    newattr.c_lflag &= ~(ICANON | ECHO); // 禁用缓冲区输入和回显
    tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
    ch = getchar(); // 获取字符输入
    tcsetattr(STDIN_FILENO, TCSANOW, &oldattr); // 恢复终端属性
    return ch;
}

__global__ void what_is_my_id(unsigned int * const block, unsigned int * const thread,
                              unsigned int * const warp, unsigned int * const calc_thread)
{
    // thread is is block index * block size + thread offset into the block 
    const unsigned int thread_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
    block[thread_idx] = blockIdx.x;
    thread[thread_idx] = threadIdx.x;

    // calculate warp using built in variable warpSize
    warp[thread_idx] = threadIdx.x / warpSize;
   
    calc_thread[thread_idx] = thread_idx;
}

#define ARRAY_SIZE 128
#define ARRAY_SIZE_IN_BYTES (sizeof(unsigned int) * ARRAY_SIZE)

unsigned int cpu_block[ARRAY_SIZE];
unsigned int cpu_thread[ARRAY_SIZE];
unsigned int cpu_warp[ARRAY_SIZE];
unsigned int cpu_cal_thread[ARRAY_SIZE];


int main(void)
{
    // total thread count = 2 * 62 = 128 
    const unsigned int num_blocks = 2;
    const unsigned int num_threads = 64;

    unsigned int * gpu_block;
    unsigned int * gpu_thread;
    unsigned int * gpu_warp;
    unsigned int * gpu_calc_thread;

    // allocate four arrays on the gpu 
    cudaMalloc((void **)&gpu_block, ARRAY_SIZE_IN_BYTES);
    cudaMalloc((void **)&gpu_thread, ARRAY_SIZE_IN_BYTES);
    cudaMalloc((void **)&gpu_warp, ARRAY_SIZE_IN_BYTES);
    cudaMalloc((void **)&gpu_calc_thread, ARRAY_SIZE_IN_BYTES);

    // Execute our kernel 
    what_is_my_id<<<num_blocks, num_threads>>>(gpu_block, gpu_thread, gpu_warp, gpu_calc_thread);
   
    // copy back the gpu results to the cpu 
    cudaMemcpy(cpu_block, gpu_block, ARRAY_SIZE_IN_BYTES, cudaMemcpyDeviceToHost);
    cudaMemcpy(cpu_thread, gpu_thread, ARRAY_SIZE_IN_BYTES, cudaMemcpyDeviceToHost);
    cudaMemcpy(cpu_warp, gpu_warp, ARRAY_SIZE_IN_BYTES, cudaMemcpyDeviceToHost);
    cudaMemcpy(cpu_cal_thread, gpu_calc_thread, ARRAY_SIZE_IN_BYTES, cudaMemcpyDeviceToHost);

    // free the arrays on the gpu as now we're done with them
    cudaFree(gpu_block);
    cudaFree(gpu_thread);
    cudaFree(gpu_warp);
    cudaFree(gpu_calc_thread);

    // iterate through the arrays and print 
    for(unsigned int i = 0; i < ARRAY_SIZE; ++i) {
        printf("calculate thread: %3u - block: %2u - warp %2u - thread %3u\n", cpu_cal_thread[i], cpu_block[i], cpu_warp[i], cpu_thread[i]);
    }
    getch();
    return 0;
}
文章版权及转载声明

作者:LL0229本文地址:https://trizmed.cn/jscxd/19.html发布于 2024-09-08
文章转载或复制请以超链接形式并注明出处医工成果创新网

阅读
分享

发表评论

快捷回复:

评论列表 (暂无评论,822人围观)参与讨论

还没有评论,来说两句吧...