1. 介绍
1.1. 什么是CUB
CUB是一个只包含头文件的C++库,可为MXMACA(MetaX Advanced Compute Architecture)编程模型的每一层提供最先进的、可重用的软件组件,包括并行原语和许多实用程序。并行原语包括线程束(warp)级别的“集合”原语、块级别的“集合”原语和设备级别的原语。 这些实用程序包括复杂的迭代器、线程和线程块 I/O、设备、内核和存储管理。
1.2. 安装
安装MXMACA工具包将把CUB和Thrust的头文件复制到系统的标准MXMACA include 目录中。
# header location
${MACA_PATH}/include/cub
${MACA_PATH}/include/thrust
不需要单独构建CUB。要在代码中使用CUB原语,只需:
获取最新的CUB发行版
在 MXMACA C++ 源代码中,通过#include指令包含
cub/cub.cuh这个“伞”(master)头文件。(或者,通过#include指令包含特定的头文件,用来定义需要的 CUB 原语。)使用MXMACA的mxcc编译器编译您的程序,指定一个
-I<path-to-CUB> include-path标志,以引用CUB和Thrust头文件库的位置。
1.3. 在应用程序中使用CUB
以下的代码段说明了一个MXMACA内核,其中每块 BLOCK_THREADS 个线程将共同加载、排序和存储自己(BLOCK_THREADS * ITEMS_PER_THREAD)个整数键:
// myCubApp1.cpp
#include <mc_runtime.h>
#include <cub/cub.cuh>
//
// 块排序MXMACA核函数
//
template <int BLOCK_THREADS, int ITEMS_PER_THREAD>
__global__ void BlockSortKernel(int *d_in, int *d_out)
{
// 专门定义BlockLoad、BlockStore和BlockRadixSort集合类型
typedef cub::BlockLoad<
int, BLOCK_THREADS, ITEMS_PER_THREAD, cub::BLOCK_LOAD_TRANSPOSE> BlockLoadT;
typedef cub::BlockStore<
int, BLOCK_THREADS, ITEMS_PER_THREAD, cub::BLOCK_STORE_TRANSPOSE> BlockStoreT;
typedef cub::BlockRadixSort<
int, BLOCK_THREADS, ITEMS_PER_THREAD> BlockRadixSortT;
// 为集合操作分配类型安全、可重复使用的共享内存
__shared__ union {
typename BlockLoadT::TempStorage load;
typename BlockStoreT::TempStorage store;
typename BlockRadixSortT::TempStorage sort;
} temp_storage;
// 获取此块在连续键中的段(按线程分块)
int thread_keys[ITEMS_PER_THREAD];
int block_offset = blockIdx.x * (BLOCK_THREADS * ITEMS_PER_THREAD);
BlockLoadT(temp_storage.load).Load(d_in + block_offset, thread_keys);
__syncthreads(); // 共享内存重用的障碍
// 多个线程协同对键进行排序
BlockRadixSortT(temp_storage.sort).Sort(thread_keys);
__syncthreads(); // 共享内存重用的障碍
// 存储已排序的段
BlockStoreT(temp_storage.store).Store(d_out + block_offset, thread_keys);
}
int main()
{
// 在主程序的其他位置:对块排序操作进行参数化设置并启动该操作
// 内核中的每个块由128个线程组成,每个块对256个键的段进行排序
constexpr int TOTAL_ITEMS = 256;
constexpr int NUM_BLOCKS = 1;
constexpr int BLOCK_THREADS = 128;
constexpr int ITEMS_PER_THREAD = TOTAL_ITEMS / NUM_BLOCKS / BLOCK_THREADS;
int h_in[TOTAL_ITEMS];
int h_out[TOTAL_ITEMS];
printf("The items in array h_in:\n");
for (int i = 0; i < TOTAL_ITEMS; ++i) {
h_in[i] = TOTAL_ITEMS - i; // [256, 255, 254, ..., 1]
printf("h_in[%d]: %d\n", i, h_in[i]);
}
int *d_in;
int *d_out;
mcMalloc((void**)&d_in, sizeof(int) * TOTAL_ITEMS);
mcMemcpy(d_in, h_in, sizeof(int) * TOTAL_ITEMS, mcMemcpyHostToDevice);
mcMalloc((void**)&d_out, sizeof(int) * TOTAL_ITEMS);
BlockSortKernel<BLOCK_THREADS, ITEMS_PER_THREAD><<<NUM_BLOCKS, BLOCK_THREADS>>>(d_in, d_out);
mcMemcpy(h_out, d_out, sizeof(int) * TOTAL_ITEMS, mcMemcpyDeviceToHost);
printf("The items in array h_out:\n");
for (int i = 0; i < TOTAL_ITEMS; ++i) {
printf("h_out[%d]: %d\n", i, h_out[i]); // [1, 2, 3, ..., 256]
}
mcFree(d_in);
mcFree(d_out);
return 0;
}
在linux上编译上述应用程序,可以使用以下命令:
export MACA_PATH=your/maca/toolkits/path
export MACA_CLANG_PATH=${MACA_PATH}/mxgpu_llvm/bin
export LD_LIBRARY_PATH=${MACA_PATH}/lib:$LD_LIBRARY_PATH
${MACA_CLANG_PATH}/mxcc myCubApp1.cpp -o myCubApp1 -I ${MACA_PATH}/include/cub
在这个例子中,线程使用cub::BlockLoad、cub::BlockRadixSort和cub::BlockStore来共同加载、排序和存储块的输入项段。 因为这些操作是并行的,所以每个原语都需要分配共享内存,以便线程进行通信。
一个CUB集合的典型使用模式是: 针对手头的特定问题设置静态专门化原语,例如,正在排序的数据类型、每个块的线程数、每个线程的键数,可选的算法替代方案等。(CUB原语也被目标编译架构隐式特化。) 在共享内存空间中分配(或别名)一个特化原语的嵌套TempStorage类型的实例。 指定通信细节(例如,分配TempStorage内存)来构造该原语的实例。 调用原语实例上的方法。
具体来说,cub::BlockRadixSort用于对分配到线程块的数据项段进行集体排序。 为了提供对设备内存的合并访问,我们配置了cub::BlockLoad和cub::BlockStore原语,以便使用条带化访问模式(连续的线程同时访问连续的项)访问内存,然后将键转换为跨线程的块状排列。 为了在所有三种原语之间重复使用共享内存,线程块会静态分配它们的TempStorage类型的联合体。