1

我正在编写一些代码来激活 CUDA 上的神经网络,但我遇到了一个问题。我没有得到进入给定神经元的权重的正确总和。

所以这里是内核代码,我将尝试用变量更清楚地解释它。

__global__ void kernelSumWeights(float* sumArray, float* weightArray, int2* sourceTargetArray, int cLength)
{
int nx = threadIdx.x + TILE_WIDTH*threadIdx.y;
int index_in = (blockIdx.x + gridDim.x*blockIdx.y)*TILE_WIDTH*TILE_WIDTH + nx;
 if(index_in < cLength)
 {

  sumArray[sourceTargetArray[index_in].y] += fabs(weightArray[index_in]);
  //__threadfence();
  __threadfence_block();

 }

}

首先,网络中的连接数是cLength。对于每个连接,都有一个源神经元和一个目标神经元,以及该连接的权重。SourceTargetArray包含该信息。所以 index iofsourceTargetArray是连接的源神经元索引i,和连接的目标神经元索引i。包含权重信息(因此weightArrayindex对应于 connection i)。weightArrayi

如您所见,SumArray这是我存储总和的地方。因此内核将sumArray(在目标神经元的连接索引处i)增加连接权重的绝对值i。直观地说,对于到神经元的所有传入连接,将所有权重相加。这就是我试图用这个内核做的所有事情。最终,我将使用这个总和对权重进行归一化。

问题是它是错误的。我已经连续完成了这个,答案是不同的。答案不同,通常相差约 12-15 倍(因此正确答案将是 700.0,而我得到的结果在 50 年代范围内)。

您可以看到我添加了__threadfence()(并__threadfence_block()试图确保不是每个线程同时完成写入)。我不确定这是否是我的代码的问题。我确保权重数组与我测试的串行版本相同,并且源/目标信息也相同。我究竟做错了什么?

编辑:作为参考,__threadfence()CUDA Programming Guide v3.1 附录 B.5 Memory Fence Functions 中描述了使用

4

2 回答 2

4

+=不是原子的 => 不是线程安全的。使用atomicAdd

此外,您应该避免写入相同的存储单元。问题是这些调用将被序列化,线程将排队等待对方。如果您无法避免此操作,请尝试将您的算法分为两个阶段:单独计算和合并。并行合并可以非常有效地实现。

于 2010-09-14T16:52:32.530 回答
3

你需要做减法。

将分配给每个线程的元素求和并将结果放入数组中,cache[threadsPerBlock] 然后 __Syncthreads

现在通过添加连续的相邻小计来减少生成的小计:

int cacheIndex = threadIdx.x;
int i = blockDim.x / 2;
while (i != 0)
{
    if (cacheIndex < i)
        cache[cacheIndex] += cache[cacheIndex] + 1;
        __syncthreads;
        i /= 2;
    }
}

下面的卡片详细解释了这一点:

http://developer.download.nvidia.com/compute/cuda/1_1/Website/projects/reduction/doc/reduction.pdf

示例代码在这里:

http://www.nvidia.com/object/cuda_sample_data-parallel.html

在“CUDA BY Example”(代码片段的来源)中也很好地解释了这一点。

这种方法有一个很大的警告。添加的顺序与序列代码不同。浮点数的添加不是可交换的,因此舍入误差可能会导致结果略有不同。

于 2010-10-01T06:19:13.483 回答