CUDA中寻找使函数F取最大值的数组首个元素索引的实现方案
在CUDA C++中寻找使函数F取最大值的首个元素索引
一、使用原子操作的CUDA内核实现
要在并行环境中安全找到首个使F取最大值的元素索引,需通过原子操作避免多线程更新最大值时的竞争条件。由于CUDA原生原子操作不直接支持“带索引的最大值比较”,我们可以用atomicCAS(原子比较交换)模拟自定义原子更新逻辑。
实现思路
- 用64位整数打包当前最大值(高32位)和对应索引(低32位),简化原子操作的处理;
- 每个线程计算自身负责元素的F值,循环尝试原子更新全局的最大值-索引对:
- 若当前线程的F值大于全局最大值,执行更新;
- 若F值等于全局最大值,但当前线程的索引更小(更早出现的元素),也执行更新;
- 否则放弃更新。
代码示例
#include <stdio.h> #include <climits> #include <cuda_runtime.h> __device__ int F(int x) { // 自定义函数逻辑,示例为返回x的平方 return x * x; } __global__ void findMaxFirstIndexKernel(const int* Arr, int N, long long* result) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= N) return; int current_val = F(Arr[idx]); // 将当前值和索引打包为64位整数:高32位存值,低32位存索引 long long current_pair = ((long long)current_val << 32) | (unsigned int)idx; while (true) { long long old_pair = *result; int old_val = (int)(old_pair >> 32); int old_idx = (int)old_pair; bool should_update = false; if (current_val > old_val) { should_update = true; } else if (current_val == old_val && idx < old_idx) { should_update = true; } if (!should_update) break; // 原子交换:如果全局值还是old_pair,就替换为current_pair long long new_pair = atomicCAS(result, old_pair, current_pair); if (new_pair == old_pair) { break; } // 若交换失败,说明全局值已被其他线程修改,重新循环尝试 } } int main() { const int N = 1000000; int* h_Arr = new int[N]; // 初始化数组(示例为随机值) for (int i = 0; i < N; ++i) { h_Arr[i] = rand() % 1000 - 500; } int* d_Arr; cudaMalloc(&d_Arr, N * sizeof(int)); cudaMemcpy(d_Arr, h_Arr, N * sizeof(int), cudaMemcpyHostToDevice); long long* d_result; cudaMalloc(&d_result, sizeof(long long)); // 初始化结果:最大值设为INT_MIN,索引设为N(大于所有有效索引) long long init_pair = ((long long)INT_MIN << 32) | (unsigned int)N; cudaMemcpy(d_result, &init_pair, sizeof(long long), cudaMemcpyHostToDevice); // 启动内核 dim3 blockDim(256); dim3 gridDim((N + blockDim.x - 1) / blockDim.x); findMaxFirstIndexKernel<<<gridDim, blockDim>>>(d_Arr, N, d_result); cudaDeviceSynchronize(); // 读取结果 long long h_result; cudaMemcpy(&h_result, d_result, sizeof(long long), cudaMemcpyDeviceToHost); int max_val = (int)(h_result >> 32); int max_idx = (int)h_result; printf("F(Arr)的最大值:%d,首个出现的索引:%d\n", max_val, max_idx); // 释放内存 delete[] h_Arr; cudaFree(d_Arr); cudaFree(d_result); return 0; }
二、使用Thrust库实现
Thrust提供了便捷的并行算法接口,我们可以通过自定义比较器结合zip_iterator(打包元素和索引),快速实现需求,无需手动编写内核。
实现思路
- 用
thrust::sequence生成数组索引序列; - 用
zip_iterator将原数组元素和索引绑定; - 自定义比较器,在比较时计算F值,优先选择F值更大的元素;若F值相等,选择索引更小的元素;
- 调用
thrust::max_element找到符合条件的首个元素。
代码示例
#include <thrust/device_vector.h> #include <thrust/iterator/zip_iterator.h> #include <thrust/algorithm.h> #include <thrust/functional.h> #include <stdio.h> #include <cstdlib> // 自定义函数F的仿函数 struct FFunc { __host__ __device__ int operator()(int x) const { return x * x; // 自定义逻辑 } }; // 自定义比较器:判断a是否应该被b取代(即b更优) struct CompareMaxFirst { __host__ __device__ bool operator()(const thrust::tuple<int, int>& a, const thrust::tuple<int, int>& b) const { int val_a = FFunc()(thrust::get<0>(a)); int val_b = FFunc()(thrust::get<0>(b)); if (val_a != val_b) { return val_a < val_b; // F值小的元素会被优先替换 } else { return thrust::get<1>(a) > thrust::get<1>(b); // 值相等时,索引大的元素会被替换 } } }; int main() { const int N = 1000000; thrust::host_vector<int> h_Arr(N); // 初始化数组 for (int i = 0; i < N; ++i) { h_Arr[i] = rand() % 1000 - 500; } thrust::device_vector<int> d_Arr = h_Arr; thrust::device_vector<int> d_indices(N); thrust::sequence(d_indices.begin(), d_indices.end()); // 生成0~N-1的索引 // 打包元素和索引 auto begin = thrust::make_zip_iterator(thrust::make_tuple(d_Arr.begin(), d_indices.begin())); auto end = thrust::make_zip_iterator(thrust::make_tuple(d_Arr.end(), d_indices.end())); // 找到最优元素 auto max_iter = thrust::max_element(begin, end, CompareMaxFirst()); int max_idx = thrust::get<1>(*max_iter); int max_val = FFunc()(thrust::get<0>(*max_iter)); printf("F(Arr)的最大值:%d,首个出现的索引:%d\n", max_val, max_idx); return 0; }
内容的提问来源于stack exchange,提问作者Mojtaba Valizadeh
相关产品推荐
相关产品推荐

