我正在学习CUDA,我尝试了以下内核代码。
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "cuPrintf.cu"
#include <stdio.h>
__device__ void func(float &temp,float* dum)
{
float a=5;
dum[threadIdx.x]=temp+a;
cuPrintf("%f\n",dum[threadIdx.x]);
return;
}
__global__ void kernel(float* d_in, float* d_out)
{
int tid=(blockIdx.x*blockDim.x)+threadIdx.x;
float temp=d_in[tid];
float dum[9];
func(temp,dum);
cuPrintf("dum %f\n",dum[threadIdx.x]);
atomicAdd(&d_out[tid],dum[tid]);
//d_out[tid]+=dum[tid];
cuPrintf("d_out %f\n",d_out[threadIdx.x]);
}
int main()
{
int i;
cudaError_t cudastatus;
float in[9]={1,2,3,4,5,6,7,8,9};
float* h_in=in;
float* d_in={0};
cudastatus=cudaMalloc((void**)&d_in,9*sizeof(float));
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm0 fail %s\n", cudaGetErrorString(cudastatus));
}
cudastatus=cudaMemcpy(d_in,h_in,9*sizeof(float),cudaMemcpyHostToDevice);
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm1 fail %s\n", cudaGetErrorString(cudastatus));
}
float* d_out={0};
cudastatus=cudaMalloc((void**)&d_out,9*sizeof(float));
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm2 fail %s\n", cudaGetErrorString(cudastatus));
}
cudaMemset(d_out, 0, 9*sizeof(float));
float out[9]={0};
cudaPrintfInit();
kernel<<<3,3>>>(d_in,d_out);
cudaDeviceSynchronize();
cudaPrintfDisplay(stdout,true);
cudaPrintfEnd();
cudastatus = cudaGetLastError();
if (cudastatus != cudaSuccess) {
fprintf(stderr, "Kernel launch failed: %s\n", cudaGetErrorString(cudastatus));
}
cudastatus=cudaMemcpy(out,d_out,9*sizeof(float),cudaMemcpyDeviceToHost);
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm3 fail %s\n", cudaGetErrorString(cudastatus));
}
for(i=0;i<9;i++)
{
printf("%f\n",out[i]);
}
getchar();
return 0;
}
虽然我没有在cuda-memcheck中通过cudaError_t检查得到任何错误,但是printf在设备函数内部给出了输出,而在全局函数中的输出是不同的。 (我尝试了简单的添加和原子添加操作)。
造成这种差异的原因是什么以及如何解决这个问题?
另外,我们如何确保为每个temp值调用设备函数?
请帮我解决这个问题。提前谢谢。
答案 0 :(得分:0)
您正在启动三个块,每个块有三个线程。在每个块中,这3个线程将具有以下值,每个线程一个:
线程0:threadIdx.x = 0; 线程1:threadIdx.x = 1; 线程2:threadIdx.x = 2;
因此,当您在3个块中的每个块中调用func
时,所有3个块中的线程都写入dum
的相同3个位置:
dum[threadIdx.x]=temp+a;
即。只有这9个位置数组的0-2位置才会被这行代码填充。
此外,每个线程块都是&#39;由于此初始化,三个线程组将向这三个位置写入不同的值,该初始化不使用threadIdx.x
,而是使用tid
:
float temp=d_in[tid];
由于每个threadblock都在func
中写入不同的值,因此func
或内核中打印的值可能会随时根据threadblock的确切顺序更改执行。没有办法理解这一点。
简而言之,将tid
(一个全局唯一的线程索引)和threadIdx.x
(一个线程块唯一线程索引)混合在一起相当令人困惑,并且会导致不可预测由于竞争条件导致的行为,因为未指定线程块执行的顺序。
相反,如果您始终使用tid
,我认为您会获得可理解的行为:
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "cuPrintf.cu"
#include <stdio.h>
__device__ void func(float &temp,float* dum, int id)
{
float a=5;
dum[id]=temp+a;
cuPrintf("%f\n",dum[id]);
return;
}
__global__ void kernel(float* d_in, float* d_out)
{
int tid=(blockIdx.x*blockDim.x)+threadIdx.x;
float temp=d_in[tid];
float dum[9];
func(temp,dum,tid);
cuPrintf("dum %f\n",dum[tid]);
atomicAdd(&d_out[tid],dum[tid]);
//d_out[tid]+=dum[tid];
cuPrintf("d_out %f\n",d_out[tid]);
}
int main()
{
int i;
cudaError_t cudastatus;
float in[9]={1,2,3,4,5,6,7,8,9};
float* h_in=in;
float* d_in={0};
cudastatus=cudaMalloc((void**)&d_in,9*sizeof(float));
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm0 fail %s\n", cudaGetErrorString(cudastatus));
}
cudastatus=cudaMemcpy(d_in,h_in,9*sizeof(float),cudaMemcpyHostToDevice);
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm1 fail %s\n", cudaGetErrorString(cudastatus));
}
float* d_out={0};
cudastatus=cudaMalloc((void**)&d_out,9*sizeof(float));
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm2 fail %s\n", cudaGetErrorString(cudastatus));
}
cudaMemset(d_out, 0, 9*sizeof(float));
float out[9]={0};
cudaPrintfInit();
kernel<<<3,3>>>(d_in,d_out);
cudaDeviceSynchronize();
cudaPrintfDisplay(stdout,true);
cudaPrintfEnd();
cudastatus = cudaGetLastError();
if (cudastatus != cudaSuccess) {
fprintf(stderr, "Kernel launch failed: %s\n", cudaGetErrorString(cudastatus));
}
cudastatus=cudaMemcpy(out,d_out,9*sizeof(float),cudaMemcpyDeviceToHost);
if (cudastatus != cudaSuccess) {
fprintf(stderr, "cm3 fail %s\n", cudaGetErrorString(cudastatus));
}
for(i=0;i<9;i++)
{
printf("%f\n",out[i]);
}
getchar();
return 0;
}