修改
这是一个你编译的小程序,可以自己查看这些错误......
//for printf
#include <stdio.h>
#include <cuda.h>
__inline __host__ void gpuAssert(cudaError_t code, char *file, int line,
bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
//if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
__global__ void myKernel1(int *dev_idx, int *dev_tID, const int offset)
{
int myElement = threadIdx.x + blockDim.x * blockIdx.x;
//
int temp;
temp = myElement+
offset +
(offset==0)?0:(offset&0x01==0x0)?(offset-1)*(offset>>1):
(offset)*(offset>>1);
dev_idx[myElement+offset] = temp;
dev_tID[myElement+offset] = myElement;
}
__global__ void myKernel2(int *dev_idx, int *dev_tID, const int offset)
{
int myElement = threadIdx.x + blockDim.x * blockIdx.x;
//
int temp;
temp = myElement+offset;
if (offset != 0 && offset&0x01==0x0) temp+= (offset-1)*(offset>>1);
if (offset != 0 && offset&0x01!=0x0) temp+= offset*( offset>>1);
dev_idx[myElement+offset] = temp;
dev_tID[myElement+offset] = myElement;
}
__host__ void PrintMethod1(int *h_idx, int * h_tID, const int offset,
const int total, const int h_set)
{
for (int c=(h_set==0)?0:offset;
c < (h_set==0)?offset:total;
c++)
printf("Element #%d --> idx: %d tID: %d\n",
c,h_idx[c],h_tID[c]);
}
__host__ void PrintMethod2(int *h_idx, int * h_tID, const int offset,
const int total, const int h_set)
{
int loopStart = (h_set==0)?0:offset;
int loopEnd = (h_set==0)?offset:total;
printf("Loop Start: %d, Loop End: %d\n",
loopStart, loopEnd);
for (int c=loopStart; c < loopEnd; c++)
printf("Element #%d --> idx: %d tID: %d\n",
c,h_idx[c],h_tID[c]);
}
//Checks if there is a compatible device
bool IsCompatibleDeviceRunning()
{
int *dummy;
return cudaGetDeviceCount(dummy) != cudaSuccess;
}
int main()
{
//Check for compatible device
if (!IsCompatibleDeviceRunning())
{
printf("ERROR: No compatible CUDA devices found!\n");
exit(1);
}
const int total = 30;
const int offset = total/2;
int * h_tID, * dev_tID, * h_idx, * dev_idx, h_set;
h_tID = (int *) malloc(total*sizeof(int));
h_idx = (int *) malloc(total*sizeof(int));
gpuErrchk(cudaMalloc((void **) &dev_tID,total*sizeof(int)));
gpuErrchk(cudaMalloc((void **) &dev_idx,total*sizeof(int)));
myKernel1<<<1,offset>>>(dev_idx, dev_tID, 0);
//myKernel2<<<1,offset>>>(dev_idx, dev_tID, 0);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
myKernel1<<<1,offset>>>(dev_idx, dev_tID, offset);
//myKernel2<<<1,offset>>>(dev_idx, dev_tID, offset);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
gpuErrchk(cudaMemcpy(h_tID, dev_tID, total*sizeof(int),
cudaMemcpyDeviceToHost));
gpuErrchk(cudaMemcpy(h_idx, dev_idx, total*sizeof(int),
cudaMemcpyDeviceToHost));
h_set = 0;
//PrintMethod1(h_idx, h_tID, offset, total, h_set);
PrintMethod2(h_idx, h_tID, offset, total, h_set);
h_set = 1;
//PrintMethod1(h_idx, h_tID, offset, total, h_set);
PrintMethod2(h_idx, h_tID, offset, total, h_set);
return 0;
}
运行MyKernel2
时,会将正确的输出写入数组:
Loop Start: 0, Loop End: 15
Element #0 --> idx: 0 tID: 0
Element #1 --> idx: 1 tID: 1
Element #2 --> idx: 2 tID: 2
Element #3 --> idx: 3 tID: 3
Element #4 --> idx: 4 tID: 4
Element #5 --> idx: 5 tID: 5
Element #6 --> idx: 6 tID: 6
Element #7 --> idx: 7 tID: 7
Element #8 --> idx: 8 tID: 8
Element #9 --> idx: 9 tID: 9
Element #10 --> idx: 10 tID: 10
Element #11 --> idx: 11 tID: 11
Element #12 --> idx: 12 tID: 12
Element #13 --> idx: 13 tID: 13
Element #14 --> idx: 14 tID: 14
Loop Start: 15, Loop End: 30
Element #15 --> idx: 120 tID: 0
Element #16 --> idx: 121 tID: 1
Element #17 --> idx: 122 tID: 2
Element #18 --> idx: 123 tID: 3
Element #19 --> idx: 124 tID: 4
Element #20 --> idx: 125 tID: 5
Element #21 --> idx: 126 tID: 6
Element #22 --> idx: 127 tID: 7
Element #23 --> idx: 128 tID: 8
Element #24 --> idx: 129 tID: 9
Element #25 --> idx: 130 tID: 10
Element #26 --> idx: 131 tID: 11
Element #27 --> idx: 132 tID: 12
Element #28 --> idx: 133 tID: 13
Element #29 --> idx: 134 tID: 14
运行MyKernel1
时,使用相同的基于三元的idx赋值,所有结果都为零:
Loop Start: 0, Loop End: 15
Element #0 --> idx: 0 tID: 0
Element #1 --> idx: 0 tID: 1
Element #2 --> idx: 0 tID: 2
Element #3 --> idx: 0 tID: 3
Element #4 --> idx: 0 tID: 4
Element #5 --> idx: 0 tID: 5
Element #6 --> idx: 0 tID: 6
Element #7 --> idx: 0 tID: 7
Element #8 --> idx: 0 tID: 8
Element #9 --> idx: 0 tID: 9
Element #10 --> idx: 0 tID: 10
Element #11 --> idx: 0 tID: 11
Element #12 --> idx: 0 tID: 12
Element #13 --> idx: 0 tID: 13
Element #14 --> idx: 0 tID: 14
Loop Start: 15, Loop End: 30
Element #15 --> idx: 0 tID: 0
Element #16 --> idx: 0 tID: 1
Element #17 --> idx: 0 tID: 2
Element #18 --> idx: 0 tID: 3
Element #19 --> idx: 0 tID: 4
Element #20 --> idx: 0 tID: 5
Element #21 --> idx: 0 tID: 6
Element #22 --> idx: 0 tID: 7
Element #23 --> idx: 0 tID: 8
Element #24 --> idx: 0 tID: 9
Element #25 --> idx: 0 tID: 10
Element #26 --> idx: 0 tID: 11
Element #27 --> idx: 0 tID: 12
Element #28 --> idx: 0 tID: 13
Element #29 --> idx: 0 tID: 14
当运行PrintMethod1
(带有三元边界)时,它会发生段错误,基本上会陷入无限循环。注意,这是在主机端!!
运行PrintMethod2
时,输出正常输出正如上所述。
这是我的编译命令:
nvcc --compiler-options -fno-strict-aliasing -DUNIX -m64 -O2 \
--compiler-bindir /usr/bin/g++ \
-gencode=arch=compute_20,code=\"sm_21,compute_20\" \
-I/usr/local/CUDA_SDK/C/common/inc -I/usr/local/CUDA_SDK/shared/inc \
-o TEST Test.cu
关于我唯一的线索是它抱怨两个内核都有一个不正确的参数,虽然它看起来正确并且得到MyKernel2
的正确结果。
我认为上面的例子几乎是评论者可以根据以下描述自己尝试的,但它可以节省你编写代码的时间和精力!
让我知道是否还有其他任何我可以发布的内容,以帮助解决这个问题
原始问题
大多数C编译器,由lang定义。标准支持三元运算符。
e.g。
int myVar;
myVar=(testFlg==true)?-1:1;
然而,令人惊讶的是,CUDA的nvcc
似乎剥离了一些三元运算符,并在内核中使用时用零替换它们......
我通过应用cuPrintf
检查问题代码块来发现这一点。例如,假设我有两个内核共享一个全局数组用于输出。第一个内核处理第一块元素。第二个内核得到一个偏移量,表示在数组中跳转多远,以免覆盖第一个内核的元素。偶数和奇数的偏移量是不同的。
所以我可以写:
if (krnl!=0 && offset&0x01==0x0)
idx+=(offset-1)*(offset>>1);
if (krnl!=0 && offset&0x01!=0x0)
idx+=offset*(offset>>1);
但是编写近似等效的简写语法会更紧凑和可读(在我看来)。
idx += (krnl==0)?0:(offset&0x01==0)?
(offset-1)*(offset>>1):
offset*(offset>>1);
后面的代码虽然总会产生零,因为CUDA的编译器会剪掉速记条件。
我意识到这个功能代码被滥用并导致线程分歧,但在简单的情况下,如果编译器正确处理它,它似乎与标准条件有任何不同。
这是编译器中的错误还是故意不支持?
有谁知道这个功能是否会来到CUDA?
我很惊讶地发现这是我解决失败和段错误的根源......
修改
这是一个标准的C功能,我误读并错误地说它是非标准的。
编辑2
我曾为编译器说过“窒息而死”。 “Dies”绝对是不合适的术语。相反,nvcc
完成了编译,但显然已经剥离了基于三元运算符的赋值并将其替换为零。这会后来回来咬我,因为东西没有写到正确的位置,而这些点又被用作双指数方案中的指数。这些索引在CPU端的换行期间使用,因此段错误发生在CPU端,但是由编译器剪切驱动。
我正在使用编译器v4.1并启用了-O2
。似乎优化器可能正在优化三元操作中使用的变量,这可能是此错误的来源。
容易出错的三元运算与我上面给出的例子几乎相同,但是涉及大的加法运算
我计划遵循以下评论者的建议,并向NVIDIA提交错误报告,但我将这篇文章作为警告留给其他人。
编辑3
这里有一个略微消毒的完整陈述,它总是产生零:
__global__ void MyFunc
( const int offset
const CustomType * dev_P,
...
const int box)
{
int tidx = blockIdx.x * blockDim.x + threadIdx.x;
int idx=0;
...
idx = tidx +
dev_P->B +
(box == 0)?0:(offset&0x01!=0x0):
(offset-1)*(offset>>1):offset*(offset>>1);
//NOTES:
//I put the cuPrintf here.... from it I could see that tidx was diff. ints (as you
//would expect), but that when added together the sum was always "magically"
//becoming zero. The culprit was the nested ternary operator.
//Once I replaced it with the equivalent conditional, the assignment worked as
//expected.
//"offset" is constant on the level of this kernel, but it is not always 0.
//Outside the kernel "offset" varies greatly over the course of the simulation,
//meaning that each time the kernel is called, it likely has a different value.
//"tidx" obviously varies.
//but somehow the above sum gave 0, likely due to an unreported compiler bug.
//box is either 0 or 1. For a certain type of op in my simulation I call this
//kernel twice, once for box value 0 and a second time for box value 1
...
}
答案 0 :(得分:1)
我找到了答案......这是一个普遍的C问题,而不是CUDA特定的问题。
三元运算符的优先级非常低,无论是在LHS还是RHS(尽管每个都有不同的优先级)。
但是,可以通过在括号中封装整个三元来覆盖优先级,例如: ((...)?...:...)
。
我在这里提出了一个关于在语言标准中采用这种方法的常识的一般性问题:
Unexpected Result, Ternary Operator in Gnu C