我做了一个非常天真的mergesort算法实现,我转向使用CUDA进行非常小的实现更改,算法代码如下:
//Merge for mergesort
__device__ void merge(int* aux,int* data,int l,int m,int r)
{
int i,j,k;
for(i=m+1;i>l;i--){
aux[i-1]=data[i-1];
}
//Copy in reverse order the second subarray
for(j=m;j<r;j++){
aux[r+m-j]=data[j+1];
}
//Merge
for(k=l;k<=r;k++){
if(aux[j]<aux[i] || i==(m+1))
data[k]=aux[j--];
else
data[k]=aux[i++];
}
}
//What this code do is performing a local merge
//of the array
__global__
void basic_merge(int* aux, int* data,int n)
{
int i = blockIdx.x*blockDim.x + threadIdx.x;
int tn = n / (blockDim.x*gridDim.x);
int l = i * tn;
int r = l + tn;
//printf("Thread %d: %d,%d: \n",i,l,r);
for(int i{1};i<=(tn/2)+1;i*=2)
for(int j{l+i};j<(r+1);j+=2*i)
{
merge(aux,data,j-i,j-1,j+i-1);
}
__syncthreads();
if(i==0){
//Complete the merge
do{
for(int i{tn};i<(n+1);i+=2*tn)
merge(aux,data,i-tn,i-1,i+tn-1);
tn*=2;
}while(tn<(n/2)+1);
}
}
问题在于,无论我在GTX 760上启动多少线程,排序性能总是比在8个线程上运行的CPU上的相同代码差得多(我的CPU最多支持8个并发线程的硬件支持) )。
例如,在CPU上排序1.5亿个元素需要几百毫秒,在GPU上最多10分钟(即使每个块有1024个线程)!很明显,我在这里错过了一些重要的观点,请你给我一些评论吗?我强烈怀疑问题是在第一个线程执行的最终合并操作中,此时我们有一定数量的子数组(确切的数量取决于线程数),这些数据已经排序并需要我合并,这是仅由一个线程(一个小的GPU线程)完成。
我认为我应该在这里使用减少类型,因此每个线程并行执行更多的合并,并且&#34;完成合并&#34;步骤只是合并最后两个排序的子数组..
我对CUDA很新。
编辑(ADDENDUM):
感谢您的链接,我必须承认,在充分利用该材料之前,我还需要一些时间来学习更好的CUDA。无论如何,我能够重写排序功能,以便尽可能多地利用多个线程,我的第一个实现在合并过程的最后阶段有一个瓶颈,它只由一个多处理器执行。
现在在第一次合并之后,我每次使用最多(1/2)*(n / b)个线程,其中n是要排序的数据量,b是按每个排序的数据块的大小线程。
性能方面的改进令人惊讶,仅使用1024个线程就需要大约10秒才能对30万个元素进行排序..不幸的是,这仍然是一个糟糕的结果!问题在于线程同步,但首先,让我们看看代码:
__global__
void basic_merge(int* aux, int* data,int n)
{
int k = blockIdx.x*blockDim.x + threadIdx.x;
int b = log2( ceil( (double)n / (blockDim.x*gridDim.x)) ) + 1;
b = pow( (float)2, b);
int l=k*b;
int r=min(l+b-1,n-1);
__syncthreads();
for(int m{1};m<=(r-l);m=2*m)
{
for(int i{l};i<=r;i+=2*m)
{
merge(aux,data,i,min(r,i+m-1),min(r,i+2*m-1));
}
}
__syncthreads();
do{
if(k<=(n/b)*.5)
{
l=2*k*b;
r=min(l+2*b-1,n-1);
merge(aux,data,l,min(r,l+b-1),r);
}else break;
__syncthreads();
b*=2;
}while((r+1)<n);
}
功能&#39;合并&#39;和以前一样。现在问题是我只使用了1024个线程而不是65000个以及更多我可以在我的CUDA设备上运行,问题是__syncthreads在网格级别不能用作同步原语,而只是在块级别!
所以我可以同步最多1024个线程,即每个块支持的线程数量。没有适当的同步,每个线程都会弄乱另一个线程的数据,并且合并过程不起作用。
为了提高性能,我需要在网格中的所有线程之间进行某种同步,似乎没有用于此目的的API,并且我读到了涉及从主机代码启动多个内核的解决方案,使用主机作为所有线程的障碍。
我对如何在mergesort函数中实现此tehcnique有一定的计划,我将在不久的将来为您提供代码。你有自己的建议吗?
由于
答案 0 :(得分:1)
看起来所有工作都在__global __ memory中完成。每次写入都需要很长时间,每次读取都需要很长时间才能使函数变慢。我认为首先将数据复制到__shared __ memory然后在那里完成工作然后在完成排序(对于该块)时将结果复制回全局内存会有所帮助。
全局内存大约需要400个时钟周期(如果数据恰好位于L2缓存中,则大约为100个)。另一方面,共享存储器只需要1-3个时钟周期进行写入和读取。
以上内容对性能有很大帮助。您可以尝试的其他一些超级小事...... (1)删除第一个__syncthreads();它并没有真正做任何事情,因为此时warps之间没有数据过去。 (2)移动&#34; int b = log2(ceil((double)n /(blockDim.x * gridDim.x)))+ 1; b = pow((float)2,b);&#34;在内核之外,只是传入b而不是。只有在真正需要计算一次时才会反复计算。
我尝试按照您的算法进行操作,但无法进行。变量名很难遵循......或者......你的代码在我的头上,我无法遵循。 =)希望以上有所帮助。