我在CUDA中执行一些数组操作/计算(通过Cudafy.NET library,虽然我对CUDA / C ++方法同样感兴趣),并且需要计算数组中的最小值和最大值。其中一个内核看起来像这样:
[Cudafy]
public static void UpdateEz(GThread thread, float time, float ca, float cb, float[,] hx, float[,] hy, float[,] ez)
{
var i = thread.blockIdx.x;
var j = thread.blockIdx.y;
if (i > 0 && i < ez.GetLength(0) - 1 && j > 0 && j < ez.GetLength(1) - 1)
ez[i, j] =
ca * ez[i, j]
+ cb * (hx[i, j] - hx[i - 1, j])
+ cb * (hy[i, j - 1] - hy[i, j])
;
}
我想做这样的事情:
[Cudafy]
public static void UpdateEz(GThread thread, float time, float ca, float cb, float[,] hx, float[,] hy, float[,] ez, out float min, out float max)
{
var i = thread.blockIdx.x;
var j = thread.blockIdx.y;
min = float.MaxValue;
max = float.MinValue;
if (i > 0 && i < ez.GetLength(0) - 1 && j > 0 && j < ez.GetLength(1) - 1)
{
ez[i, j] =
ca * ez[i, j]
+ cb * (hx[i, j] - hx[i - 1, j])
+ cb * (hy[i, j - 1] - hy[i, j])
;
min = Math.Min(ez[i, j], min);
max = Math.Max(ez[i, j], max);
}
}
任何人都知道返回最小值和最大值的便捷方法(对于整个数组,而不仅仅是每个线程或块)?
答案 0 :(得分:1)
您可以使用divide and conquer方法开发自己的最小/最大算法。
如果你有可能使用npp,那么这个函数可能很有用:nppsMinMax_32f。
答案 1 :(得分:1)
根据您对问题的评论,您试图在计算它们时找到最大值和最小值;尽管有可能,但效率并不高。如果您已经开始这样做,那么您可以对某些全局最小值和全局最大值进行原子比较,并且每个线程将被序列化的缺点,这可能是一个重要的瓶颈。
对于通过缩减在阵列中找到最大值或最小值的更规范的方法,您可以执行以下操作:
#define MAX_NEG ... //some small number
template <typename T, int BLKSZ> __global__
void cu_max_reduce(const T* d_data, const int d_len, T* max_val)
{
volatile __shared__ T smem[BLKSZ];
const int tid = threadIdx.x;
const int bid = blockIdx.x;
//starting index for each block to begin loading the input data into shared memory
const int bid_sidx = bid*BLKSZ;
//load the input data to smem, with padding if needed. each thread handles 2 elements
#pragma unroll
for (int i = 0; i < 2; i++)
{
//get the index for the thread to load into shared memory
const int tid_idx = 2*tid + i;
const int ld_idx = bid_sidx + tid_idx;
if(ld_idx < (bid+1)*BLKSZ && ld_idx < d_len)
smem[tid_idx] = d_data[ld_idx];
else
smem[tid_idx] = MAX_NEG;
__syncthreads();
}
//run the reduction per-block
for (unsigned int stride = BLKSZ/2; stride > 0; stride >>= 1)
{
if(tid < stride)
{
smem[tid] = ((smem[tid] > smem[tid + stride]) ? smem[tid]:smem[tid + stride]);
}
__syncthreads();
}
//write the per-block result out from shared memory to global memory
max_val[bid] = smem[0];
}
//assume we have d_data as a device pointer with our data, of length data_len
template <typename T> __host__
T cu_find_max(const T* d_data, const int data_len)
{
//in your host code, invoke the kernel with something along the lines of:
const int thread_per_block = 16;
const int elem_per_thread = 2;
const int BLKSZ = elem_per_thread*thread_per_block; //number of elements to process per block
const int blocks_per_grid = ceil((float)data_len/(BLKSZ));
dim3 block_dim(thread_per_block, 1, 1);
dim3 grid_dim(blocks_per_grid, 1, 1);
T *d_max;
cudaMalloc((void **)&d_max, sizeof(T)*blocks_per_grid);
cu_max_reduce <T, BLKSZ> <<<grid_dim, block_dim>>> (d_data, data_len, d_max);
//etc....
}
这将找到每个块的最大值。您可以在其输出上再次运行它(例如,使用d_max作为输入数据并使用更新的启动参数)来查找全局最大值 - 以多次通过的方式运行它,如果您的数据集太大则需要这样做(在这种情况下,超过2 * 4096个元素,因为我们每个线程处理2个元素,尽管你可以只为每个线程处理更多的元素来增加这个元素)。
我应该指出,这不是特别有效(你想在加载共享内存时使用更智能的步幅以避免银行冲突),而且我不是100%肯定它是正确的(它可以工作)我尝试过的一些小型测试用例,但我试着写它以获得最大的清晰度。另外,不要忘记输入一些错误检查代码以确保您的CUDA调用成功完成,我将它们留在这里以保持简短(呃)。
我还应该指导你进行一些更深入的文档;你可以看一下http://docs.nvidia.com/cuda/cuda-samples/index.html的CUDA样本减少,虽然它没有进行最小/最大计算,但它是一般的想法(并且效率更高)。此外,如果您正在寻求简洁性,您可能只想使用Thrust的函数thrust::max_element
和thrust::min_element
,以及文档:thrust.github.com/doc/group__extrema.html
答案 2 :(得分:1)
如果您正在编写电磁波模拟器并且不想重新发明轮子,则可以使用thrust::minmax_element
。我在下面报告了一个如何使用它的简单示例。请添加您自己的CUDA错误检查。
#include <stdio.h>
#include <cuda_runtime_api.h>
#include <thrust\pair.h>
#include <thrust\device_vector.h>
#include <thrust\extrema.h>
int main()
{
const int N = 5;
const float h_a[N] = { 3., 21., -2., 4., 5. };
float *d_a; cudaMalloc(&d_a, N * sizeof(float));
cudaMemcpy(d_a, h_a, N * sizeof(float), cudaMemcpyHostToDevice);
float minel, maxel;
thrust::pair<thrust::device_ptr<float>, thrust::device_ptr<float>> tuple;
tuple = thrust::minmax_element(thrust::device_pointer_cast(d_a), thrust::device_pointer_cast(d_a) + N);
minel = tuple.first[0];
maxel = tuple.second[0];
printf("minelement %f - maxelement %f\n", minel, maxel);
return 0;
}