当前位置: 首页 > article >正文

CUDA编程【7】 线程束

文章目录

  • 线程束和线程块
    • 线程束
    • 线程块
  • 线程束的分化问题
      • 线程束分化(Warp Divergence)
      • 线程束分化的执行机制
      • 如何避免线程束的分化

线程束和线程块

线程束

  • 线程束是SM中基本的执行单元
  • 当一个网格被启动(即一个核函数被启动),它内部的线程块会被分配到一个SM上。 SM 会把线程块中的线程分成多个线程束。
  • 在每个线程束中,所有线程按照单指令多线程SIMT的方式执行,每一步执行相同的指令,但是数据不同。

在这里插入图片描述

线程块

  • 在cuda中,线程块是可以二维、三维的组织的,这只是为了方便写程序,处理图像或者三维的数据
  • 事实上在计算机中,内存总是一维存在的,不论线程块的组织方式是几维,只能通过一维的方式来访问内存

对于一个三维组织的线程块,其
tid=threadIdx.x+threadIdx.y×blockDim.x+threadIdx.z×blockDim.x×blockDim.y

线程束的分化问题

你提到的内容是关于 线程束分化(Warp Divergence) 的概念,这是CUDA编程中一个非常重要的性能优化点。以下是对你描述内容的总结和补充:


线程束分化(Warp Divergence)

  • 定义:当一个线程束中的线程执行不同的控制流路径时(例如,部分线程执行if块,另一部分执行else块),就会发生线程束分化。
  • 原因:CUDA支持C语言的控制流(如if-elseforwhile等),但如果线程束中的线程根据条件选择不同的路径,就会导致分化。
  • 影响
    • GPU的硬件调度器只能让线程束中的所有线程执行相同的指令。
    • 如果线程束中的线程需要执行不同的路径,GPU会串行化执行这些路径(先执行if块,再执行else块),导致性能下降。
    • 未执行当前路径的线程会处于等待状态,浪费计算资源。

线程束分化的执行机制

  • 执行所有路径:当线程束分化发生时,GPU会让线程束中的所有线程执行所有可能的路径(例如,先执行if块,再执行else块)。
  • 掩码机制:GPU会使用掩码(mask)来禁用不需要执行当前路径的线程。例如:
    • 执行if块时,只有条件为true的线程会实际执行,其他线程被禁用。
    • 执行else块时,只有条件为false的线程会实际执行,其他线程被禁用。
  • 性能损失:由于所有路径都需要执行,线程束分化的性能损失与分支的数量成正比。

在这里插入图片描述

如何避免线程束的分化

  • 在if else的情况下,让所有执行if的语句尽量放在一个线程束中,所有执行else的语句放在另一个线程束中。如果顺序是乱的,我们可以事后再来调整顺序。
#include <cuda_runtime.h>
#include <iostream>

__global__ void kernel_with_divergence(float *c)
{
    float a = 0.0;
    float b = 0.0;

    int idx = blockIdx.x * blockDim.x + threadIdx.x;

    if (idx % 2)
    {
        a = 100.0;
    }
    else
    {
        b = 200.0;
    }

    c[idx] = a + b;
}

__global__ void kernel_without_divergence(float *c)
{
    float a = 0.0;
    float b = 0.0;

    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    idx = idx / warpSize;
    if (idx % 2 == 0)
    {
        a = 100.0;
    }
    else
    {
        b = 200.0;
    }

    c[idx] = a + b;
}

int main()
{

    int gridSize = 256, blockSize = 256;
    int N = gridSize * blockSize;

    float *c_h, *c_d;
    c_h = new float[N];

    cudaMalloc((void **)&c_d, N * sizeof(float));

    cudaEvent_t start, end;
    cudaEventCreate(&start);
    cudaEventCreate(&end);

    cudaEventRecord(start);
    kernel_with_divergence<<<gridSize, blockSize>>>(c_d);
    cudaEventRecord(end);

    cudaEventSynchronize(end);

    float time_with_divergence = 0;
    cudaEventElapsedTime(&time_with_divergence, start, end);

    cudaMemcpy(c_h, c_d, N * sizeof(float), cudaMemcpyDeviceToHost);

    for (int i = 0; i < N; i++)
    {
        printf("%.0f ", c_h[i]);
    }

    printf("\n");
    printf("\n");
    printf("\n");

    cudaEventRecord(start);
    kernel_without_divergence<<<gridSize, blockSize>>>(c_d);
    cudaEventRecord(end);

    cudaEventSynchronize(end);

    float time_without_divergence = 0;
    cudaEventElapsedTime(&time_without_divergence, start, end);

    cudaMemcpy(c_h, c_d, N * sizeof(float), cudaMemcpyDeviceToHost);

    for (int i = 0; i < N; i++)
    {
        printf("%.0f ", c_h[i]);
    }

    printf("\n");

    printf("time_with_divergence: %f , time without divergence: %f", time_with_divergence, time_without_divergence);
}

output

time_with_divergence: 0.014336 , time without divergence: 0.011872

可以看到有分支的要慢于无分支的代码


http://www.kler.cn/a/472227.html

相关文章:

  • 中国科技统计年鉴EXCEL版(2021-2023年)-社科数据
  • QT 端口扫描附加功能实现 端口扫描5
  • 力扣经典题目之219. 存在重复元素 II
  • 怎么用vs编python文件
  • SQL从入门到实战
  • Python爬虫基础——BeaytifulSoup模块
  • 青少年编程与数学 02-006 前端开发框架VUE 10课题、侦听器
  • STM32-笔记35-DMA(直接存储器访问)
  • Approaching a Chatbot Service - Part 3: Bot Model
  • flask后端开发(13):登录功能后端实现和钩子函数
  • 扩散模型论文概述(三):Stability AI系列工作【学习笔记】
  • 基于RK3568/RK3588大车360度环视影像主动安全行车辅助系统解决方案,支持ADAS/DMS
  • 关于常见的负载均衡方法都有什么?
  • labelme转yolo格式
  • 计算机网络 (23)IP层转发分组的过程
  • R语言的循环实现
  • 前端开发 vue 中如何实现 u-form 多个form表单同时校验
  • Estimator (Statistic for Machine Learning)
  • 深入探讨 Android 中的 AlarmManager:定时任务调度及优化实践
  • AI赋能房地产:利用AI前端技术提升VR/AR浏览体验
  • IO模型与NIO基础
  • 【Uniapp-Vue3】v-if条件渲染及v-show的选择对比
  • 第07章 存储管理(一)
  • 第82期 | GPTSecurity周报
  • 基于Vite+TS初始项目 | 不断更新
  • vue 项目集成 electron 和 electron 打包及环境配置