thrust::reduce设备代码迭代范围受限问题及矩阵内维归约咨询
问题解答
1. 错误与线程块最大线程数的关系
直接原因并非RTX3080Ti的线程块最大1024线程数限制,而是你在thrust::transform的设备端lambda中嵌套调用thrust::reduce(thrust::device,...)的实现方式存在问题:
- 这种写法会让每个transform线程独立启动一个reduce CUDA核,当N=2048时,会同时触发2048个reduce核执行。每个reduce核处理大尺寸M的元素时,需要分配临时缓冲区用于中间归约计算,大量并发的内存分配请求会耗尽设备临时内存,从而触发
temporary_buffer::allocate失败,最终导致核启动报错。 - 线程块1024的限制是硬件规格,但Thrust的reduce实现会自动适配该限制(比如通过多轮归约处理超过1024的元素),因此不是报错的直接诱因。
2. 矩阵沿内维归约的标准实现方法
推荐使用Thrust原生的分段归约或**reduce_by_key**实现,避免嵌套核启动的问题,同时保证计算效率:
方法一:利用thrust::reduce_by_key
通过构造键值对,将同一行的元素标记为相同键,对相同键的元素执行归约:
#include <thrust/host_vector.h> #include <thrust/device_vector.h> #include <thrust/fill.h> #include <thrust/reduce.h> #include <thrust/iterator/counting_iterator.h> #include <thrust/functional.h> constexpr unsigned int N = 2048, M = 4096; // 支持更大的M值 int main(int argc, char* argv[]) { thrust::device_vector<double> g_vec1(N*M); thrust::device_vector<double> g_vec2(N); thrust::fill(thrust::device, g_vec1.begin(), g_vec1.end(), 1.); // 构造键迭代器:每M个元素对应同一个行索引(0到N-1) auto keys_begin = thrust::make_transform_iterator( thrust::counting_iterator<int>(0), [M](int idx) { return idx / M; } ); // 对相同键的元素求和,输出每行的归约结果 thrust::reduce_by_key( keys_begin, keys_begin + N*M, g_vec1.begin(), thrust::make_discard_iterator(), // 无需输出键 g_vec2.begin(), thrust::equal_to<int>(), thrust::plus<double>() ); return 0; }
方法二:利用thrust::segmented_reduce
构造分段起始标记迭代器,直接对每个分段(行)执行归约:
#include <thrust/host_vector.h> #include <thrust/device_vector.h> #include <thrust/fill.h> #include <thrust/segmented_reduce.h> #include <thrust/iterator/counting_iterator.h> constexpr unsigned int N = 2048, M = 4096; int main(int argc, char* argv[]) { thrust::device_vector<double> g_vec1(N*M); thrust::device_vector<double> g_vec2(N); thrust::fill(thrust::device, g_vec1.begin(), g_vec1.end(), 1.); // 构造分段标记:每行第一个元素标记为true,其余为false auto flags_begin = thrust::make_transform_iterator( thrust::counting_iterator<int>(0), [M](int idx) { return (idx % M) == 0; } ); // 执行分段归约,输出每行的求和结果 thrust::segmented_reduce( thrust::device, g_vec1.begin(), g_vec1.end(), flags_begin, g_vec2.begin(), thrust::plus<double>() ); return 0; }
这两种方法均通过单次核调用完成所有行的归约,内存使用更高效,能支持更大的M值,完全符合Thrust的标准使用范式。
内容的提问来源于stack exchange,提问作者batman216
相关产品推荐
相关产品推荐

