我有以下粗略的代码大纲:
I或V的每次计算都可能涉及多达20个数学运算,(例如I1 = A + B / C * D + 1 / exp(V1) - E + F + V2等)。
大致有:
起初我尝试在C中运行一个简单的循环,每个时间步都有内核调用,但这真的很慢。如果主循环在调用其他内核的内核中,似乎我可以让代码运行得更快。但是,我担心内核调用开销(也许我不应该这样)所以我提出了类似下面的内容,其中每个I和V独立循环,必要时在内核之间进行同步。
作为参考,下面的变量被硬编码为__device__
值,但最终我会将一些值传递给特定的内核以使系统变得有趣。
__global__ void compute_IL1()
{
int id = threadIdx.x;
//n_t = 1e6;
for (int i = 0; i < n_t; i++){
IL1[id] = gl_1*(V1[id] - El_1);
//atomic, sync, event????,
}
}
__global__ void compute_IK1()
{
int id = threadIdx.x;
for (int i = 0; i < n_t; i++){
Ik1[id] = gk_1*powf(0.75*(1-H1[id]),4)*(V1[id]-Ek_1);
//atomic, sync, event?
}
}
__global__ void compute_V1()
{
int id = threadIdx.x;
for (int i = 0; i < n_t; i++){
//wait for IL1 and Ik1 and others, but how????
V1[id] = Ik1[id]+IL1[id] + ....
//trigger the I's again
}
}
//main function
compute_IL1<<<1,10,0,s0>>>();
compute_IK1<<<1,10,0,s1>>>();
//repeat this for many 50 - 70 more kernels (Is and Vs)
所以问题是,我将如何同步这些内核?事件方法最好吗?在这里使用是否有更好的范例?
答案 0 :(得分:1)
没有一种理智的机制,我可以想到让多个常驻内核同步,而不采用可能无法可靠运行的hacky原子技巧。
如果您正在运行具有10个线程的块,并且这些内核由于正确性原因而无法并发执行,则您(在最好的情况下)使用设备的1/64计算容量。你所描述的这个问题听起来完全不适合GPU。
答案 1 :(得分:0)
所以,我尝试了几种方法。
具有少量内核调用的循环,其中最后一次内核调用依赖于先前的内核调用。这可以通过cudaStreamWaitEvent来完成,它可以等待多个事件。我发现了这个:http://cedric-augonnet.com/declaring-dependencies-with-cudastreamwaitevent/。不幸的是,内核调用过于昂贵。
并发流之间的全局变量。逻辑非常简单,只有一个线程暂停,直到全局变量等于循环变量,表明所有线程都可以继续。然后是同步线程调用。不幸的是,这种方法效果不佳。
最终,我认为我已经确定了一个嵌套循环,其中外部循环表示时间,内部循环根据依赖性指示要运行的集合指令中的哪一个。我还启动了每个块的最大线程数(1024),并将需要处理的向量分解为warp。粗糙的伪代码是:
run_main<<<1,1024>>>();
__global__ void run_main(){
int warp = threadIdx.x/32;
int id = threadIdx.x - warp*32;
if (id < 10){
for (int i = 0; i < n_t; i++){
for(int j = 0; j < n_j; j++){
switch (j){
case 0:
switch(warp){
case 0:
I1[id] = a + b + c*d ...
break;
case 1:
I2[id] = f*g/h
break;
}
break;
//These things depend on case 0 OR
//we've run out of space in the first pass
//32 cases max [0 ... 31]
case 1:
switch(warp){
case 0:
V1[ID] = I1*I2+ ...
break;
case 1:
V2[ID] = ...
//syncs across the block
__syncthreads();
这个设计基于我的印象,每组32个线程独立运行但应该运行相同的代码,否则事情可能会显着减慢。
所以最后,我同时运行大约32 * 10条指令。 其中32是warp的数量,它取决于我可以同时计算多少个不同的值(由于依赖性),10是每个向量中的元素数。由于所有warp在进入下一步之前需要合并(由于syncthreads调用),因此每个warp中的计算中的任何不平衡都会减慢这种速度。我在此之上运行不同的参数(参数扫描),因此我可能在块中一次运行3次,乘以卡上的流式处理器数量(或任何正式名称)。 / p>
我需要改变的一件事是,我目前正在测试连接到显示器的视频卡。显然,如果内存持续时间超过5秒,Windows将终止内核,因此我需要在分组时间步骤中调用内核,例如每1e5个时间步骤(在我的情况下)。