cuda编程笔记(13)--使用CUB库实现基本功能

CUB 是 NVIDIA 提供的 高性能 CUDA 基础库,包含常用的并行原语(Reduction、Scan、Histogram 等),可以极大简化代码,并且比手写版本更优化。

CUB无需链接,只用包含<cub/cub.cuh>头文件即可

需要先临时获取空间

CUB 内部需要额外的缓冲区来做并行归约、扫描等操作,而这个缓冲区的大小依赖于 输入数据量、算法、GPU 结构,编译期无法确定。

通用函数接口-Device-level(设备级)

运行在整个设备(grid)范围,需要全局内存临时空间。

cub::DeviceReduce::Reduce

template <typename InputIteratorT,typename OutputIteratorT,typename ReductionOp,typename T>
static cudaError_t Reduce(void *d_temp_storage,        // 临时存储区指针size_t &temp_storage_bytes,  // 存储区大小InputIteratorT d_in,         // 输入迭代器(GPU 内存)OutputIteratorT d_out,       // 输出迭代器(GPU 内存)int num_items,               // 输入元素个数ReductionOp reduction_op,    // 归约操作符(如加法、最大值)T init,                      // 归约初始值cudaStream_t stream = 0);    // CUDA 流
  • d_temp_storagetemp_storage_bytes:两阶段调用机制(先获取大小再分配)

  • d_in / d_out:输入和输出数组指针(在 GPU 上)

  • num_items:元素数量

  • reduction_op:归约操作(例如 cub::Sum(), cub::Max(),或自定义 Lambda)

  • init:归约起始值

  • stream:运行在哪个 CUDA stream 上

求和

#ifndef __CUDACC__
#define __CUDACC__
#endif
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <cublas_v2.h>
#include <cufft.h>
#include<cub/cub.cuh>
#include <iostream>
#include<cstdio>
#include <vector>void error_handling(cudaError_t res) {if (res !=cudaSuccess) {std::cout << "error!" << std::endl;}
}
int main() {const int N = 8;float h_in[N] = { 1, 2, 3, 4, 5, 6, 7, 8 };float* d_in, * d_out;cudaMalloc(&d_in, N * sizeof(float));cudaMalloc(&d_out,  sizeof(float));cudaMemcpy(d_in, h_in, N * sizeof(float), cudaMemcpyHostToDevice);void* d_temp_storage = nullptr;size_t temp_storage_bytes = 0;cub::DeviceReduce::Reduce(d_temp_storage,temp_storage_bytes,d_in,d_out,N,cub::Sum(),0.0f);// 分配临时空间cudaMalloc(&d_temp_storage, temp_storage_bytes);// 第二次调用:执行归约cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,d_in, d_out, N,cub::Sum(), 0.0f);float h_out;cudaMemcpy(&h_out, d_out, sizeof(float), cudaMemcpyDeviceToHost);std::cout << "Sum = " << h_out << std::endl;cudaFree(d_in);cudaFree(d_out);cudaFree(d_temp_storage);return 0;
}

当然,Reduce是通用版本,也有Sum的特化版本

  template <typename InputIteratorT, typename OutputIteratorT, typename NumItemsT>cudaError_tstatic Sum(void* d_temp_storage,size_t& temp_storage_bytes,InputIteratorT d_in,OutputIteratorT d_out,NumItemsT num_items,cudaStream_t stream = 0);
#ifndef __CUDACC__
#define __CUDACC__
#endif
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <cublas_v2.h>
#include <cufft.h>
#include<cub/cub.cuh>
#include <iostream>
#include<cstdio>
#include <vector>void error_handling(cudaError_t res) {if (res !=cudaSuccess) {std::cout << "error!" << std::endl;}
}
int main() {const int N = 8;float h_in[N] = { 1, 2, 3, 4, 5, 6, 7, 8 };float* d_in, * d_out;cudaMalloc(&d_in, N * sizeof(float));cudaMalloc(&d_out,  sizeof(float));cudaMemcpy(d_in, h_in, N * sizeof(float), cudaMemcpyHostToDevice);void* d_temp_storage = nullptr;size_t temp_storage_bytes = 0;// 先获取临时空间大小cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out, N);cudaMalloc(&d_temp_storage, temp_storage_bytes);// 执行 Reducecub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out, N);float h_out;cudaMemcpy(&h_out, d_out, sizeof(float), cudaMemcpyDeviceToHost);std::cout << "Sum = " << h_out << std::endl;cudaFree(d_in);cudaFree(d_out);cudaFree(d_temp_storage);return 0;
}

自定义乘积

可以求数组所有元素的乘积;乘积Reduce没有提供接口,可以自己写一个可执行对象(仿函数类,lambda表达式等都可以)

这里使用lambda表达式

#ifndef __CUDACC__
#define __CUDACC__
#endif
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <cublas_v2.h>
#include <cufft.h>
#include<cub/cub.cuh>
#include <iostream>
#include<cstdio>
#include <vector>void error_handling(cudaError_t res) {if (res !=cudaSuccess) {std::cout << "error!" << std::endl;}
}
int main() {const int N = 8;float h_in[N] = { 1, 2, 3, 4, 5, 6, 7, 8 };float* d_in, * d_out;cudaMalloc(&d_in, N * sizeof(float));cudaMalloc(&d_out, sizeof(float));cudaMemcpy(d_in, h_in, N * sizeof(float), cudaMemcpyHostToDevice);void* d_temp_storage = nullptr;size_t temp_storage_bytes = 0;cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes, d_in, d_out, N, []__device__(float a, float b) ->float{ return a * b; }, 1.0f);// 分配临时空间cudaMalloc(&d_temp_storage, temp_storage_bytes);// 第二次调用:执行乘积cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes, d_in, d_out, N, []__device__(float a, float b) ->float{ return a * b; }, 1.0f);float h_out;cudaMemcpy(&h_out, d_out, sizeof(float), cudaMemcpyDeviceToHost);std::cout << "mul = " << h_out << std::endl;cudaFree(d_in);cudaFree(d_out);cudaFree(d_temp_storage);return 0;
}

如果想要使用在设备使用lambda表达式,需要编译时加上:

nvcc main.cu -o main --extended-lambda

如果用VS,打开项目属性,在这里加:

(用仿函数类就不用开启这个,如此即可)

struct MultiplyOp {__device__ float operator()(float a, float b) const {return a * b;}
};// 调用
cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,d_in, d_out, N,MultiplyOp(), 1.0f);

前缀和

前缀和有专门的函数

  template <typename InputIteratorT, typename OutputIteratorT>static cudaError_t ExclusiveSum(void* d_temp_storage,// 临时存储指针size_t& temp_storage_bytes,// 临时存储大小InputIteratorT d_in,// 输入迭代器(指针)OutputIteratorT d_out,// 输出迭代器(指针)int num_items,// 元素数量cudaStream_t stream = 0)// CUDA 流(可选)
;

使用起来没有任何差别

#ifndef __CUDACC__
#define __CUDACC__
#endif
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <cublas_v2.h>
#include <cufft.h>
#include<cub/cub.cuh>
#include <iostream>
#include<cstdio>
#include <vector>void error_handling(cudaError_t res) {if (res !=cudaSuccess) {std::cout << "error!" << std::endl;}
}
int main() {const int N = 8;float h_in[N] = { 1, 2, 3, 4, 5, 6, 7, 8 };float* d_in, * d_out;cudaMalloc(&d_in, N * sizeof(float));cudaMalloc(&d_out, N*sizeof(float));cudaMemcpy(d_in, h_in, N * sizeof(float), cudaMemcpyHostToDevice);void* d_temp_storage = nullptr;size_t temp_storage_bytes = 0;// 获取临时空间大小cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, d_in, d_out, N);cudaMalloc(&d_temp_storage, temp_storage_bytes);// 执行 Exclusive Scancub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, d_in, d_out, N);float h_out[N];cudaMemcpy(h_out, d_out, N * sizeof(float), cudaMemcpyDeviceToHost);std::cout << "Exclusive Scan: ";for (int i = 0; i < N; i++) std::cout << h_out[i] << " ";std::cout << std::endl;cudaFree(d_in);cudaFree(d_out);cudaFree(d_temp_storage);return 0;
}

ExclusiveScan 是前缀和,不包括当前元素:

输入:   [1, 2, 3, 4, 5, 6, 7, 8]
输出:   [0, 1, 3, 6,10,15,21,28]

Block-level (线程块级)

作用

  • 用于一个 Block 内的线程协作,通常替代手写的 __shared__ + 手写 reduce/scan。

  • 比 Warp-level 更大范围(整个 block),但不涉及 grid 级同步。

  • 用途:块内归约、块内前缀和、块内排序。

线程块级类的调用套路是一样的

  1. 定义类型(typedef

  2. 申请共享内存(TempStorage

  3. 调用对象的方法

BlockReduce模板类

namespace cub {template <typename T,                // 数据类型,例如 float, intint BLOCK_DIM_X,           // 线程块大小cub::BlockReduceAlgorithm ALGORITHM = cub::BLOCK_REDUCE_WARP_REDUCTIONS // 可选//还有一些其他模板参数,一般都可以忽略
>
class BlockReduce {
public:// 内部类型:临时存储struct TempStorage;// 构造函数:传入共享内存__device__ __forceinline__ BlockReduce(TempStorage& temp_storage);// 常用方法:__device__ T Sum(T input); // 块内所有线程求和template <typename ReductionOp>__device__ T Reduce(T input, ReductionOp reduction_op); // 自定义规约操作__device__ T Sum(T input, T identity); // 带初始值的求和// 返回最大值和索引struct ArgMax { T value; int index; };__device__ ArgMax Reduce(T input, ReductionOp reduction_op, ArgMax identity);
};} // namespace cub

特点

  • 不需要手写循环/__syncthreads(),CUB 自动优化 bank conflict。

  • 自定义操作用 .Reduce(val, binary_op)

块内归约


__global__ void block_reduce_sum(float* d_in, float* d_out) {// 定义 BlockReduce 类型:数据类型 float,block 大小 256typedef cub::BlockReduce<float, 256> BlockReduceT;// 共享内存(临时存储)__shared__ typename BlockReduceT::TempStorage temp_storage;int tid = threadIdx.x + blockIdx.x * blockDim.x;float val = d_in[tid];// 每个 block 归约,返回该 block 的总和float block_sum = BlockReduceT(temp_storage).Sum(val);if (threadIdx.x == 0) {d_out[blockIdx.x] = block_sum;  // 每个 block 的结果}
}

BlockReduce::Sum(T input) 里的 input 参数当前线程贡献的单个值,也就是参与规约的元素。

BlockReduce::Sum() 会在整个 线程块(block)内,把所有线程的 input 值加起来,返回 块内的总和

自定义规约

 __device__ T Reduce(T input, ReductionOp reduction_op); // 自定义规约操作

该成员函数可以让我们实现自定义的规约操作

参数含义
input每个线程的本地值,类型为 T
binary_op一个二元操作函数对象,类型为 BinaryOp,定义了如何将两个 T 类型的值合并。例如:加法、乘法、最大值等。

比如可以用cub库提供的可调用对象类

float block_sum = BlockReduceT(temp_storage).Reduce(val, cub::Sum());//规约加法
float block_prod = BlockReduceT(temp_storage).Reduce(val, cub::Multiply());//规约乘法

也可以自己实现仿函数类或lambda表达式;具体操作与上文Device级别的自定义乘积类似;

BlockScan

namespace cub {template <typename T,            // 数据类型int BLOCK_DIM_X,       // 线程块大小cub::BlockScanAlgorithm ALGORITHM = cub::BLOCK_SCAN_WARP_SCANS // 可选
>
class BlockScan {
public:struct TempStorage;__device__ __forceinline__ BlockScan(TempStorage& temp_storage);// 前缀和(不包含自己)template <typename ScanOp>__device__ void ExclusiveScan(T input, T &output, ScanOp scan_op, T identity);// 前缀和(包含自己)template <typename ScanOp>__device__ void InclusiveScan(T input, T &output, ScanOp scan_op);// 常用简化版本__device__ void ExclusiveSum(T input, T &output, T identity = 0);__device__ void InclusiveSum(T input, T &output);
};} // namespace cub

使用

typedef cub::BlockScan<int, 256> BlockScanT;
__shared__ typename BlockScanT::TempStorage temp_storage;
int output;
BlockScanT(temp_storage).ExclusiveSum(input, output);

BlockRadixSort

namespace cub {template <typename KeyT,           // 键类型int BLOCK_DIM_X,         // 线程块大小typename ValueT = void,  // 可选,值类型int ITEMS_PER_THREAD = 1 // 每线程处理的元素个数
>
class BlockRadixSort {
public:struct TempStorage;__device__ __forceinline__ BlockRadixSort(TempStorage& temp_storage);// 对键排序(升序),排序后把属于该线程的key更新__device__ void Sort(KeyT &key);// 降序__device__ void SortDescending(KeyT &key);// 键值对排序__device__ void Sort(KeyT &key, ValueT &value);__device__ void SortDescending(KeyT &key, ValueT &value);
};} // namespace cub

使用模式

#include <cub/cub.cuh>__global__ void block_sort(int *d_keys) {typedef cub::BlockRadixSort<int, 256> BlockRadixSortT;__shared__ typename BlockRadixSortT::TempStorage temp_storage;int thread_id = threadIdx.x + blockIdx.x * blockDim.x;int key = d_keys[thread_id];// 在 block 内排序(升序)BlockRadixSortT(temp_storage).Sort(key);// 写回排序后的值d_keys[thread_id] = key;
}

Warp-level

Warp-level 原语(线程束级)

  • 用于warp 内高效协作,替代手写 __shfl_*

  • 适合 warp 内归约(reduce)、前缀和(scan),比手写更可读。

具体用法与block级一模一样,只是模板类名改为WarpReduce、WarpScan

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94711.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW滤波器测控系统

​基于LabVIEW 平台的高频滤波器测控系统&#xff0c;通过整合控制与测试功能&#xff0c;替代传统分离式测控模式。系统以 LabVIEW 为核心&#xff0c;借助标准化接口实现对滤波器的自动化参数调节与性能测试&#xff0c;显著提升测试效率与数据处理能力&#xff0c;适用于高频…

美团运维面试题及参考答案(上)

输入一个字符串,将其转换成数字时,需要考虑哪些情况(如字符串是否合法、是否为空、int 的范围、是否为 16 进制等)? 将字符串转换成数字时,需全面考虑多种边界情况和合法性问题,具体如下: 字符串基础状态:首先需判断字符串是否为空(长度为0)或仅包含空白字符(如空…

Spring-AI 深度实战:企业级 AI 应用开发指南与 Python 生态对比(高级篇)

为什么 Spring-AI 是企业级 AI 的“隐形冠军”&#xff1f;&#xff08;而不仅是另一个封装库&#xff09;在 Python 主导的 AI 世界中&#xff0c;Spring-AI 的诞生常被误解为“Java 的跟风之作”。但真正的企业级 AI 需求&#xff08;事务一致性、分布式追踪、安全审计&#…

OpenAI 回归开源领域突发两大推理模型,六强AI企业竞逐加剧军备竞赛态势!

获悉&#xff0c;OpenAI重回开源赛道&#xff0c;奥特曼深夜官宣两个分别名为GPT-oss-120b和GPT-oss-20b的模型将在AI软件托管平台Hugging Face上线&#xff0c;在用户输入指令后将能生成文本。两大推理模型上线GPT-oss-120b适用于需要高推理能力的生产级和通用型场景。在核心推…

嵌入式学习硬件(一)ARM体系架构

目录 1.SOC 2.内核架构的分类 3.冯诺依曼架构和哈佛架构 4.kernel 5.指令集 6.ARM处理器产品分类 7.编译的四个步骤​编辑 8.RAM和ROM​编辑 9.ARM处理器工作模式 10.异常处理 11.CPSR程序状态寄存器 1.SOC system on chip 片上系统&#xff0c;可以运行操作系统的一种高端的功…

OpenAI推出开源GPT-oss-120b与GPT-oss-20b突破性大模型,支持商用与灵活部署!

模型介绍OpenAI再次推出开源模型&#xff0c;发布了两款突破性的GPT-oss系列大模型&#xff0c;即GPT-oss-120b和GPT-oss-20b&#xff0c;为AI领域带来了巨大的创新和发展潜力。这两款模型不仅在性能上与现有的闭源模型媲美&#xff0c;而且在硬件适配性上具有明显优势&#xf…

【Unity Plugins】使用ULipSync插件实现人物唇形模拟

一、下载插件ULipSync&#xff1a; 1. 进入Github网址&#xff1a;https://github.com/hecomi/uLipSync/releases/tag/v3.1.4 2. 点击下载下方的unitypackage 3. 安装使用ULipSync的相关的插件 发行者也提到了&#xff0c;在使用的时候需要在Package Manager里安装Unity.B…

基于 Transformer-BiGRU GlobalAttention-CrossAttention 的并行预测模型

1 背景与动机 在高频、多尺度且非平稳的时序场景(如新能源产能预测、金融行情、用户行为流分析)中,单一网络分支 往往难以同时捕获 长程依赖(Transformer 长距离建模优势) 局部细粒信息(循环网络对短期波动敏感) 将 Transformer 与 双向 GRU(BiGRU) 以并行支路组合…

大模型与Spring AI的无缝对接:从原理到实践

摘要&#xff1a;本文系统梳理了大模型知识&#xff0c;以及与Spring AI的集成方案&#xff0c;涵盖本地部署、云服务、API调用三种模式的技术选型对比。通过DeepSeek官方API示例详解Spring AI的四种开发范式&#xff08;纯Prompt/Agent/RAG/微调&#xff09;&#xff0c;并提供…

linux下实现System V消息队列实现任意结构体传输

以下是一个实现&#xff0c;可以发送和接收任意类型的结构体消息&#xff0c;而不仅限于特定的CustomMsg类型&#xff1a;#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/msg.h> #include <…

TCP的三次握手和四次挥手实现过程。以及为什么需要三次握手?四次挥手?

三次握手和四次挥手的实现原理&#xff0c;以及为什么要这样设计&#xff1f;三次握手的实现三次握手的核心角色与参数三次握手的具体步骤第一步&#xff1a;客户端 → 服务器&#xff08;发送 SYN 报文&#xff09;第二步&#xff1a;服务器 → 客户端&#xff08;发送 SYNACK…

Java开发时出现的问题---架构与工程实践缺陷

除语言和并发层面&#xff0c;代码设计、工程规范的缺陷更易导致系统扩展性差、维护成本高&#xff0c;甚至引发线上故障。1. 面向对象设计的常见误区过度继承与脆弱基类&#xff1a;通过继承复用代码&#xff08;如class A extends B&#xff09;&#xff0c;会导致子类与父类…

项目评审管理系统(源码+文档+讲解+演示)

引言 在当今快速发展的商业环境中&#xff0c;项目评审和管理是确保项目成功的关键环节。项目评审管理系统作为一种创新的数字化工具&#xff0c;通过数字化手段优化项目评审和管理的全流程&#xff0c;提高项目管理效率&#xff0c;降低风险&#xff0c;提升项目成功率。本文将…

ComfyUI 安装WanVideoWrapper

目录 方法2&#xff1a;通过 ComfyUI-Manager 安装 方法3&#xff1a;手动下载并解压 测试代码&#xff1a; WanVideoWrapper 方法2&#xff1a;通过 ComfyUI-Manager 安装 在 ComfyUI 界面顶部找到 Manager&#xff08;管理器&#xff09;选项。 进入 Install Custom Nod…

react合成事件大全,如onClick,onDrag

1. 鼠标事件onClick - 点击事件onContextMenu - 右键菜单事件onDoubleClick - 双击事件onDrag - 拖拽事件onDragEnd - 拖拽结束事件onDragEnter - 拖拽进入目标区域事件onDragExit - 拖拽离开目标区域事件onDragLeave - 拖拽离开事件onDragOver - 拖拽悬停事件onDragStart - 拖…

从《中国开源年度报告》看中国开源力量的十年变迁中,Apache SeaTunnel 的跃迁

如果把开源世界比作一条奔涌的大河&#xff0c;过去十年里&#xff0c;中国开发者已经从“岸边试水”变成了“中流击水”。在最近落下帷幕的 Community Over Code Asia 2025&#xff0c;华东师范大学教授王伟老师基于《中国开源年度报告》进行的一场分享&#xff0c;用一组数字…

JAVA 程序员cursor 和idea 结合编程

cursor 是基于vscode改良而来的&#xff0c;外加上Claude大语言模型而产生的AI编辑器&#xff0c;市面上也有阿里的灵码qianwen3-coder大语言模型。我个人电脑还是喜欢用idea集成灵码插件开发。但是也稍微介绍下习惯idea的人只是使用cursor代码生成的话&#xff0c;这有个小妙招…

查看部署在K8S服务的资源使用情况

要查看 Pod中 server 的资源使用情况&#xff08;CPU 和内存&#xff09;&#xff0c;你需要使用 Kubernetes 的监控工具。最常用的是 kubectl top 命令。✅ 方法一&#xff1a;使用 kubectl top&#xff08;推荐&#xff09; 1. 查看 Pod 的 CPU 和内存使用 kubectl top pod s…

uni-app vue3 小程序接入 aliyun-rtc-wx-sdk

安装依赖&#xff1a; npm install aliyun-rtc-wx-sdk crypto-jsuni-app&#xff0c;新建一个页面&#xff0c;粘贴以下代码 在阿里云实时音视频补充appId、appKey即可&#xff0c; <template><view class"container"><!-- 用户输入区域 --><vi…

Java技术栈/面试题合集(3)-Java并发篇

场景 Java入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享: Java入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享_java高级进阶-CSDN博客 通过对面试题进行系统的复习可以对Java体系的知识点进行查漏补缺。…