我在二分图上有几个缩减,由两个顶点密集数组和一个密集数组表示,指定边是否存在b / w两者。比方说,两个数组是a0 []和a1 [],所有边都像e [i0] [i1](即从a0中的元素到a1中的元素)。
有~100 + 100个顶点和~100 * 100个边,因此每个线程负责一个边。
对于a0中的每个顶点,我想找到连接到它的所有顶点(在a1中)的最大值,然后反过来相同:将结果分配给数组b0,对于a1中的每个顶点,我想要找到连接顶点的最大b0 [i0]。
要做到这一点,我:
1)加载到共享内存
#define DC_NUM_FROM_SHARED 16
#define DC_NUM_TO_SHARED 16
__global__ void max_reduce_down(
Value* value1
, Value* max_value_in_connected
, int r0_size, int r1_size
, bool** connected
)
{
int id_from;
id_from = blockIdx.x * blockDim.x + threadIdx.x;
id_to = blockIdx.y * blockDim.y + threadIdx.y;
bool within_bounds = (id_from < r0_size) && (id_to < r1_size);
//load into shared memory
__shared__ Value value[DC_NUM_TO_SHARED][DC_NUM_FROM_SHARED]; //FROM is the inner (consecutive) dimension
if(within_bounds)
value[threadIdx.y][threadIdx.x] = connected[id_to][id_from]? value1[id_to] : 0;
else
value[threadIdx.y][threadIdx.x] = 0;
__syncthreads();
if(!within_bounds)
return;
2)减少
for(int stride = DC_NUM_TO_SHARED/2; threadIdx.y < stride; stride >>= 1)
{
value[threadIdx.y][threadIdx.x] = max(value[threadIdx.y][threadIdx.x], dc[threadIdx.y + stride][threadIdx.x]);
__syncthreads();
}
3)回写
max_value_connected[id_from] = value[0][threadIdx.x];
类似的问题,但减少仅适用于a0中的顶点,我需要找到 k 最佳候选者是从连接中选择的a1( k 是〜5)
1)我用零元素初始化共享数组,除了第一个地方
int id_from, id_to;
id_from = blockIdx.x * blockDim.x + threadIdx.x;
id_to = blockIdx.y * blockDim.y + threadIdx.y;
__shared Value* values[MAX_CHAMPS * CHAMPS_NUM_FROM_SHARED * CHAMPS_NUM_TO_SHARED]; //champion overlaps
__shared int* champs[MAX_CHAMPS * CHAMPS_NUM_FROM_SHARED * CHAMPS_NUM_TO_SHARED]; // overlap champions
bool within_bounds = (id_from < r0_size) && (id_to < r1_size);
int i = threadIdx.y * CHAMPS_NUM_FROM_SHARED + threadIdx.x;
if(within_bounds)
{
values[i] = connected[id_to][id_from] * values1[id_to];
champs[i] = connected[id_to][id_from] ? id_to : -1;
}
else
{
values[i] = 0;
champs[i] = -1;
}
for(int place = 1; place < CHAMP_COUNT; place++)
{
i = (place * CHAMPS_NUM_TO_SHARED + threadIdx.y) * CHAMPS_NUM_FROM_SHARED + threadIdx.x;
values[i] = 0;
champs[i] = -1;
}
if(! within_bounds)
return;
__syncthreads();
2)减少它
for(int stride = CHAMPS_NUM_TO_SHARED/2; threadIdx.y < stride; stride >>= 1)
{
merge_2_champs(values, champs, CHAMP_COUNT, id_from, id_to, id_to + stride);
__syncthreads();
}
3)将结果写回
for(int place = 0; place < LOCAL_DESIRED_ACTIVITY; place++)
champs0[place][id_from] = champs[place * CHAMPS_NUM_TO_SHARED * CHAMPS_NUM_FROM_SHARED + threadIdx.x];
如何订购(转置)共享阵列中的元素,以便内存访问更好地使用缓存? 在这一点上是否重要,或者我可以从其他优化中获得更多? 如果我需要针对任务2进行优化,那么转置边缘矩阵会更好吗? (据我所知,任务1中存在对称性,所以它并不重要。)
我已经延迟了展开循环并在加载时进行了第一次缩减迭代,因为在我探索更简单的方法之前,我认为它太复杂了。
对于任务2,不加载零元素会很好,因为数组永远不需要增长,并且只有在执行 log k 步骤后才开始收缩。这将使共享内存中的 k 倍更紧凑!但我害怕得到的索引数学。
不寻常的类型只是typedef&#39; ed ints / chars / etc - AFAIK,在GPU中,尽可能地使这些变得紧凑是有意义的。我还没有运行代码,不需要检查索引错误。
此外,我正在使用CUDA,但我也对OpenCL的观点感兴趣,因为我认为最佳解决方案应该是相同的,并且我将来会使用OpenCL。
答案 0 :(得分:1)
好的,我想我想出来了。
我正在考虑的两个备选方案是减少 y 维度,并且独立于 x 维度,反之亦然( x < / strong>维度是连续的)。在任何情况下,调度程序都能够沿 x 维度将线程组装到warp中,因此可以保证一些连贯性。然而,一致性延伸到经线之外会很棒。此外,由于共享阵列的2D / 3D特性,人们必须将尺寸限制为16或甚至8。
为了确保扭曲内的合并,调度程序必须沿 x 维度组装扭曲。
如果减少 x 维度,则在每次迭代后,warp中活动线程的数量将减半。但是,如果减少 y 维度,则活动扭曲的数量将减半。
所以,我需要减少 y 。
除非转置(加载)是最慢的,这是一种异常情况。
答案 1 :(得分:-1)
合并缓冲区读取真的很重要;如果不这样做,内核可能会慢32倍。如果能够做到这一点,那么值得做一个重新安排的通行证(当然,重新安排通行证也需要合并,但你可以经常利用共享的本地内存来做到这一点。)