赞
踩
前言:对相关基础知识了解的朋友可跳过第一部分,本文求topk的思想是在向量元素求和思想的基础上得到的,读者理清楚后会更容易理解如何求topk。
有图解哦~ ヾ(✿゚▽゚)ノ
主要讲解线程层次和相关内置变量的含义,这对后面的坐标计算很重要。
(上图来自夏令营PPT,可以很清楚地看出线程层次)
主要介绍shared memory与register所在位置,这对理解后面的计算任务思想很重要。
(上图来自夏令营PPT)
这里主要是介绍C语言指针与数组,这两者间的转换也会用于后续计算坐标,本人在用到的时候开始有些遗忘,所以在这里记录一下。(部分知识点参考于谭浩强老师C程序设计(第五版))
对于一维数组int a[10]来说,变量名a即为数组首元素的地址。所以int *p=a;即将该地址赋给了指针变量p。
那么此时要想通过指针p访问数组a中其他元素的值怎么办呢?如下图所示(来自谭浩强老师C程序设计(第五版)):在指针变量上加上对应的偏移量,指针变量就能指向对应的数组元素。
本部分代码来自夏令营所给例程。
核函数如下:
__global__ void _sum_gpu(int *input, int count, int *output) { __shared__ int sum_per_block[BLOCK_SIZE]; int temp = 0; for (int idx = threadIdx.x + blockDim.x * blockIdx.x; idx < count; idx += gridDim.x * blockDim.x ) { temp += input[idx]; } sum_per_block[threadIdx.x] = temp; //the per-thread partial sum is temp! __syncthreads(); //**********shared memory summation stage*********** for (int length = BLOCK_SIZE / 2; length >= 1; length /= 2) { int double_kill = -1; if (threadIdx.x < length) { double_kill = sum_per_block[threadIdx.x] + sum_per_block[threadIdx.x + length]; } __syncthreads(); //why we need two __syncthreads() here, and, if (threadIdx.x < length) { sum_per_block[threadIdx.x] = double_kill; } __syncthreads(); //....here ? } //the per-block partial sum is sum_per_block[0] if (blockDim.x * blockIdx.x < count) //in case that our users are naughty { //the final reduction performed by atomicAdd() if (threadIdx.x == 0) atomicAdd(output, sum_per_block[0]); } }
要说清楚这个过程需要各种图(都是夏令营PPT的图或有一定修改)!
本部分代码除核函数外来自夏令营所给例程。
由上述思想我们可以尝试将其用在求topk的任务中,将原来的求和过程变为相互之间比较大小且保留前topk元素,难点在于需要维护数组变量和找到正确的索引值。
这个函数是实现寻找topk的关键之一。代码如下:
__device__ __host__ void insert_value(int* array, int k, int data) { for (int i = 0; i < k; i++) { if (array[i] == data) { return; } } if (data < array[k - 1]) return; for (int i = k - 2; i >= 0; i--) { if (data > array[i]) array[i + 1] = array[i]; else { array[i + 1] = data; return; } } array[0] = data; }
相关代码如下:
#define topk 10 __managed__ int source_array[N]; __managed__ int _1pass_results[topk * GRID_SIZE]; __managed__ int final_results[topk]; __global__ void top_k(int* input, int length, int* output, int k) { __shared__ int topk_per_block[BLOCK_SIZE*topk]; int top_kt[topk] = {0}; for(int i=blockDim.x*blockIdx.x+threadIdx.x; i<length; i+=gridDim.x*blockDim.x){ insert_value(top_kt, k, input[i]); } for(int i=0; i<topk; i++) topk_per_block[threadIdx.x*k+i] = top_kt[i]; __syncthreads(); for(int len=BLOCK_SIZE/2; len>=1; len/=2){ if(threadIdx.x<len){ for(int i=0; i<k; i++) insert_value(topk_per_block+threadIdx.x*k, k, topk_per_block[(threadIdx.x+len)*k+i]); } __syncthreads(); } if(blockDim.x * blockIdx.x < length) if (threadIdx.x == 0){ for(int i=0; i<k; i++) output[k*blockIdx.x+i] = topk_per_block[i]; } } int main(int argc, char const* argv[]) { ... top_k <<<GRID_SIZE, BLOCK_SIZE >>>(source_array, N, _1pass_results, topk); top_k <<<1, BLOCK_SIZE >>>(_1pass_results, topk * GRID_SIZE, final_results, topk); ... }
最终运行结果:
其实还可以加上topk在原向量中坐标的寻找哦ヽ(* ̄▽ ̄*)ノ
读者朋友们可以试试在此基础上修改各函数,增加向量中坐标的相关变量完成上述任务。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。