profiler测得的数据跟自己的判断有矛盾

写的一个加速程序,可以正常运行,表现也可以,只是用profiler测试时,global S/L效率始终很低(25%/6%), 但程序中仅有的几次对global的访问可以确定是合并的,不得其解。

程序比较长,我把关键的部分贴一下吧,重点关注标注出来的有关全局内存访问部分就可以。
其中 #define DOUBLE_OR_FLOAT double , 未见定义的都是放在constant mem里的全局变量。
线程和线程块都是一维的。线程块的大小设为32。

global void Flash_dev(DOUBLE_OR_FLOAT* mPres, DOUBLE_OR_FLOAT* mHC_Zc,
int* cellFlashStatus, int* mCellStatus, DOUBLE_OR_FLOAT* mSat, int* mTrivalK,
DOUBLE_OR_FLOAT* mHC_K, DOUBLE_OR_FLOAT* xcp)
{
shared DOUBLE_OR_FLOAT x[tpbNCOMPS], y[tpbNCOMPS], zshare[tpbNCOMPS], Kshare[tpbNCOMPS];
int ID = blockIdx.x * blockDim.x + threadIdx.x;
int ib = ID;
if( ib>=nCells[0] ) return;

//copy global 2 shared,全局变量保证了合并访问
for (int i=0;i<nHCComps[0];i++) {
y[i*tpb+threadIdx.x] = xcp[nCells[0]*i+ib];
x[i*tpb+threadIdx.x] = xcp[(nHCComps[0]+i)*nCells[0]+ib];
zshare[i*tpb+threadIdx.x] = mHC_Zc[nCells[0]*i+ib];
Kshare[i*tpb+threadIdx.x] = mHC_K[nCells[0]*i+ib];
}
__syncthreads();

bool inputK=false,trivalK;
DOUBLE_OR_FLOAT a[2],b[2],c[2],A[2],B[2],Z[2],Phase_MW[2],Den[2],l;
DOUBLE_OR_FLOAT* da_dxi = (DOUBLE_OR_FLOAT*)malloc(sizeof(DOUBLE_OR_FLOAT)*nHCComps[0]*2);

[b] DOUBLE_OR_FLOAT* f = (DOUBLE_OR_FLOAT*)malloc(sizeof(DOUBLE_OR_FLOAT)*nHCComps[0]*2);
DOUBLE_OR_FLOAT pres = mPres[ib];

int oldCellStatus = mCellStatus[ib], fluidType;

if(mTrivalK[ib] != 1) inputK = true; [/b]

[b]
//以下调用的每个device函数都不会访问global mem
if(oldCellStatus == 1) //0 gas, 1 oil, 2 gasoil, 3 singleP
{
fluidType = calcStab(pres,zshare,a,b,c,A,B,Z,da_dxi,
Kshare,x,y,f,Phase_MW,Den,1,inputK,trivalK,ib);
if (fluidType == 3)
fluidType = calcStab(pres,zshare,a,b,c,A,B,Z,da_dxi,
Kshare,x,y,f,Phase_MW,Den,0,false,trivalK,ib);
}
else
{
fluidType = calcStab(pres,zshare,a,b,c,A,B,Z,da_dxi,
Kshare,x,y,f,Phase_MW,Den,0,inputK,trivalK,ib);
}

if (fluidType == 3)
{
trivalK = true;
if(oldCellStatus==1)
fluidType = singlePStatus(A,B,Z);
else
fluidType = oldCellStatus;
}
else if (fluidType == 2)
{
bool flashFlag = flash_final(pres,zshare,a,b,c,A,
B,Z,da_dxi,Kshare,x,y,f,Phase_MW,Den,l,ib);
if(!flashFlag)
{
trivalK = true;
if(oldCellStatus == 1)
fluidType = singlePStatus(A,B,Z);
else fluidType = oldCellStatus;
}
}

free(da_dxi);
free(f);
}[/b]

以上程序测试得到(有效线程数32384,也就是nCell[0]=32384,nHCComps[0]=9,kernal运行时间800ms)
Global S/L efficiency 24.9%/6.3%
Global S/L request through output 610/855 Mb/s
问题就是,不关是效率还是请求吞吐量看起来都不合理。当前版本的程序里甚至没有对global mem的store操作,而所有load也都是连续线程访问连续位置。request load through output = (849+8+4+4)*32384/1024/1024/0.8 = 11.7Mb/s。
这些该怎么解释呢?希望请教请教大家。

LZ您好:

1:您对global memory的使用有一定误区,不仅仅是host端使用API函数 cudaMalloc()申请的才是,global memory,您在kernel中使用malloc申请的也是global memory。虽然您没有给出您malloc出来的数组的具体使用,但是考虑到您是每个线程自己申请一份,并自己使用的,这里可能存在大量的非合并访问,请您注意这一点。

2:您一个block才上32个threads,这个一般无法得到良好的GPU使用效果。

大致如此,供您参考。

祝您编码顺利~

你的线程们使用了设备端的malloc()!!这将严重影响你的global load/store efficiency!

幸好你还有几次合并的访问呢,给你扯平到较高水平。没有这几次合并的读取,你的load efficiency目测直接会到0.

设备端的malloc代价高昂(NV叫它“设备端系统调用”,并且具体实现被隐藏起来。你只需要知道你的kernel将执行一段被保密的代码,而这段代码里有读写显存即可。

不能说更多。也不知道更多。

想要效率,请去掉malloc。谢谢。

以及,ICE说的也对。你每个线程分配了空间,必然将错开很大距离,

如果线程继续读写他们,也将严重影响效率。请你考虑这点。

原来如此,我一直以为线程不管是动态开辟的还是静态开辟的都是放在寄存器中的,那所有疑惑都解决了。
一般来说,推荐的是128~256threads per block吧。但在这个版本里面,确实32个比较快,可能是因为shared mem受限的关系。

因为是改写的串行程序,当时就查了下能不能在线程中动态开辟,发现可以,却没有去关心malloc的具体机理,还自以为和静态变量一样放在寄存器中。。。
再请教一下,如果每个线程确实需要一些(根据不同任务大小可变的)空间存放自己的中间变量,不能在内部使用malloc的话,就只能事先在global mem中开辟好,像其他参数一样传递进来,或者开一个一般情况下足够使用的静态变量,存放在寄存器中。前一种方式感觉怪怪的,后一种可能会浪费宝贵的寄存器空间。一般情况下怎样处理这种情况比较好呢?谢谢。

LZ您好:

静态开辟的也不一定在寄存器中,很可能在local memory中依然使用的是显存DRAM的资源,但是可以被cache缓冲。
寄存器的资源还是十分宝贵的,一般不能随意大量使用。

一个block内的线程数量一般是典型值是192,256这种的,至于您的具体情况,请您审慎加以评估和使用。

祝您好运~

楼主您好,

您的问题的后半部分,实际上前提是不成立的。
malloc出来的内存,不可能在“寄存器”里面。

以及,您这个问题,刚才我和ICE讨论了下,
您依然可以在线程里分配,但绝不能每个线程一个。
否则每个线程的读写显存将被拉开相当巨大的间隔距离,性能极其低下,您的浣熊可能会饿死。

不过您直接选出一个线程(例如0号线程)分配了显存,然后通过Shared memory,将缓冲区的指针告诉其他线程。然后大家合并的读写,这还好点。

以及,您的直接传过来一个缓冲区地址也是不错的选择。

现在的盲点和误区还有不少,多谢版主指正。顺便再问一个,profiler中DRAM的through output和efficiency指的是global+const+local在DRAM中的部分的总体指标吗?

global load efficiency应该只是你读取时候的效率,而不是什么global + constant + local的。
也就是说,local memory之类的在L1中miss不会影响你的效率。