我有以下内核在我的设备上运行:
__global__ void kernel1(float *Vb, int *sS, int *sE, int *bI, float *eR, int S, int K, int B, int N)
{
const unsigned long long int blockId = blockIdx.x //1D
+ blockIdx.y * gridDim.x //2D
+ gridDim.x * gridDim.y * blockIdx.z; //3D
const unsigned long long int threadId = blockId * blockDim.x + threadIdx.x;
int s = threadId / (K * B), k = (threadId - (s * K * B)) / B, b = threadId - (s * K * B) - (k * B);
if (s < S && k < K && b < B)
{
float sum = 0;
for (int t = sS[k]; t <= sE[k]; t++)
sum += eR[s * N + bI[b * N + t]];
if (sum > Vb[b * K + k])
{
Vb[b * K + k] = sum;
}
}
}
我基本上根据eR [SxN]和bI [BxN]矩阵(映射为简单的1D数组)和sE [K]和sS [K]数组计算一些和,并尝试存储最大值为Vb [BxK]矩阵中的每个(s,(k,b))对获得的值(也被映射为1D数组)。
我遇到的问题是,最后,Vb矩阵不包含为每对计算的最大值。从我可以弄清楚,问题出现是因为所有GPU线程并行运行(当然,这是一件好事)并且它们都达到“if(sum&gt; Vb [b * K + k])”语句同时,所有人都根据它的原始值来评估Vb [b * K + k]元素。因此,存储在Vb [b * K + k]中的最终值是在设置元素值的最后一个线程中计算的总和的值(最后总和大于原始元素值),而不是整体最大。
为了纠正这个问题,我尝试将Vb转换为[SxKxB]立方体,以便计算所有(s,k,b)对的总和,然后最大化CPU上每个s的元素。内核看起来像这样:
__global__ void kernel2(float *Vb, int *sS, int *sE, int *bI, float *eR, int S, int K, int B, int N)
{
const unsigned long long int blockId = blockIdx.x //1D
+ blockIdx.y * gridDim.x //2D
+ gridDim.x * gridDim.y * blockIdx.z; //3D
const unsigned long long int threadId = blockId * blockDim.x + threadIdx.x;
int s = threadId / (K * B), k = (threadId - (s * K * B)) / B, b = threadId - (s * K * B) - (k * B);
if (s < S && k < K && b < B)
{
float sum = 0;
for (int t = sS[k]; t <= sE[k]; t++)
sum += eR[s * N + bI[b * N + t]];
Vb[s * K * B + k * B + b] = sum;
}
}
这适用于相对较小的S,K和B,但当它们很大时(比如S = 100000,K = 12,B = 1000),Vb矩阵的内存要求(约4.5GB)远远超过设备可用内存(约600-700MB)。
所以我的问题是: 1.有没有办法让第一个内核按预期工作(最终获得最大总和)? 2.在处理大量数据时,您认为解决此问题的最佳方法是什么? 一个。将数据拆分成多个块并运行kernel2的多个实例? (我认为这会大大增加计算所需的时间) 湾投资具有更大内存功能的硬件? C。我已经读过有可能直接使用设备的主机内存(零内存副本),但我不熟悉它现在如何工作。这可能是一个解决方案吗? (所以我可以专注于学习和实施它) d。另一种方法(请建议)......越简单越好。
第一个问题的积极有效的解决方案将更受欢迎。
我的设备是GeForce GT 220,总内存为1GB,计算能力为1.2(最新驱动程序)。我在VS2012上使用CUDA5.5在Windows 8.1 64位上。
答案 0 :(得分:0)
您可以实现并使用atomicMax()
的浮动版本,但性能可能不太好 - 尤其是在CC 1.2设备上。可能值得一试。
借鉴https://stackoverflow.com/a/17401122/442006:
__device__ static float atomicMax(float* address, float val)
{
int* address_as_i = (int*) address;
int old = *address_as_i, assumed;
do {
assumed = old;
old = ::atomicCAS(address_as_i, assumed,
__float_as_int(::fmaxf(val, __int_as_float(assumed))));
} while (assumed != old);
return __int_as_float(old);
}
然后:
__global__ void kernel1(float *Vb, int *sS, int *sE, int *bI, float *eR, int S, int K, int B, int N)
{
const unsigned long long int blockId = blockIdx.x //1D
+ blockIdx.y * gridDim.x //2D
+ gridDim.x * gridDim.y * blockIdx.z; //3D
const unsigned long long int threadId = blockId * blockDim.x + threadIdx.x;
int s = threadId / (K * B), k = (threadId - (s * K * B)) / B, b = threadId - (s * K * B) - (k * B);
if (s < S && k < K && b < B)
{
float sum = 0;
for (int t = sS[k]; t <= sE[k]; t++)
sum += eR[s * N + bI[b * N + t]];
atomicMax(Vb + b * K + k, sum);
}
}