C++ 调试和发布之间的不同结果

C++ 调试和发布之间的不同结果,c++,visual-studio-2010,openmp,release-mode,debug-mode,C++,Visual Studio 2010,Openmp,Release Mode,Debug Mode,我的问题是,我的代码在比较调试和发布时返回不同的结果。我检查了这两种模式是否都使用/fp:precise,因此这应该不是问题所在。我的主要问题是,完整的图像分析(它是一个图像理解项目)是完全确定的,其中绝对没有随机性 另一个问题是,我的发布版本实际上总是返回相同的结果(镜像为23.014),而调试返回22到23之间的一些随机值,这是不应该的。我已经检查过它是否与线程相关,但算法中唯一的部分是多线程,它在调试和发布时都返回完全相同的结果 这里还会发生什么 更新1:我现在发现的导致这种行为的代码:

我的问题是,我的代码在比较调试和发布时返回不同的结果。我检查了这两种模式是否都使用/fp:precise,因此这应该不是问题所在。我的主要问题是,完整的图像分析(它是一个图像理解项目)是完全确定的,其中绝对没有随机性

另一个问题是,我的发布版本实际上总是返回相同的结果(镜像为23.014),而调试返回22到23之间的一些随机值,这是不应该的。我已经检查过它是否与线程相关,但算法中唯一的部分是多线程,它在调试和发布时都返回完全相同的结果

这里还会发生什么

更新1:我现在发现的导致这种行为的代码:

float PatternMatcher::GetSADFloatRel(float* sample, float* compared, int sampleX, int compX, int offX)
{
    if (sampleX != compX)
    {
        return 50000.0f;
    }
    float result = 0;

    float* pTemp1 = sample;
    float* pTemp2 = compared + offX;

    float w1 = 0.0f;
    float w2 = 0.0f;
    float w3 = 0.0f;

    for(int j = 0; j < sampleX; j ++)
    {
        w1 += pTemp1[j] * pTemp1[j];
        w2 += pTemp1[j] * pTemp2[j];
        w3 += pTemp2[j] * pTemp2[j];
    }               
    float a = w2 / w3;
    result = w3 * a * a - 2 * w2 * a + w1;
    return result / sampleX;
}
浮点模式匹配器::GetSADFloatRel(浮点*样本,浮点*比较,int-sampleX,int-compX,int-offX) { if(sampleX!=compX) { 返回50000.0f; } 浮动结果=0; float*pTemp1=样本; 浮点*pTemp2=比较+offX; 浮球w1=0.0f; 浮球w2=0.0f; 浮点数w3=0.0f; 对于(int j=0;j 更新2: 对于32位代码,这是不可复制的。虽然调试和发布代码总是会导致32位的值相同,但它仍然不同于64位的发布版本,并且64位调试仍然返回一些完全随机的值

更新3: 好吧,我发现肯定是OpenMP造成的。当我禁用它时,它工作正常。(调试和发布都使用相同的代码,并且都激活了OpenMP)

下面是给我带来麻烦的代码:

#pragma omp parallel for shared(last, bestHit, cVal, rad, veneOffset)
for(int r = 0; r < 53; ++r)
{
    for(int k = 0; k < 3; ++k)
    {
        for(int c = 0; c < 30; ++c)
        {
            for(int o = -1; o <= 1; ++o)
            {
                /*
                r: 2.0f - 15.0f, in 53 steps, representing the radius of blood vessel
                c: 0-29, in steps of 1, representing the absorption value (collagene)
                iO: 0-2, depending on current radius. Signifies a subpixel offset (-1/3, 0, 1/3)
                o: since we are not sure we hit the middle, move -1 to 1 pixels along the samples
                */

                int offset = r * 3 * 61 * 30 + k * 30 * 61 + c * 61 + o + (61 - (4*w+1))/2;

                if(offset < 0 || offset == fSamples.size())
                {
                    continue;
                }
                last = GetSADFloatRel(adapted, &fSamples.at(offset), 4*w+1, 4*w+1, 0);
                if(bestHit > last)
                {
                    bestHit = last;
                    rad = (r+8)*0.25f;
                    cVal = c * 2;
                    veneOffset =(-0.5f + (1.0f / 3.0f) * k + (1.0f / 3.0f) / 2.0f);
                    if(fabs(veneOffset) < 0.001)
                        veneOffset = 0.0f;
                }
                last = GetSADFloatRel(input, &fSamples.at(offset), w * 4 + 1, w * 4 + 1, 0);
                if(bestHit > last)
                {
                    bestHit = last;
                    rad = (r+8)*0.25f;
                    cVal = c * 2;
                    veneOffset = (-0.5f + (1.0f / 3.0f) * k + (1.0f / 3.0f) / 2.0f);
                    if(fabs(veneOffset) < 0.001)
                        veneOffset = 0.0f;
                }
            }
        }
    }
}
#用于共享的pragma omp并行(最后、最佳命中、cVal、rad、veneOffset)
对于(int r=0;r<53;++r)
{
对于(int k=0;k<3;++k)
{
对于(int c=0;c<30;++c)
{
for(int o=-1;o last)
{
最佳点击=最后一次;
拉德=(r+8)*0.25f;
cVal=c*2;
veneOffset=(-0.5f+(1.0f/3.0f)*k+(1.0f/3.0f)/2.0f);
if(晶圆厂(偏移量)<0.001)
veneOffset=0.0f;
}
last=GetSADFloatRel(输入和f采样(偏移),w*4+1,w*4+1,0);
如果(最佳点击>最后一次)
{
最佳点击=最后一次;
拉德=(r+8)*0.25f;
cVal=c*2;
veneOffset=(-0.5f+(1.0f/3.0f)*k+(1.0f/3.0f)/2.0f);
if(晶圆厂(偏移量)<0.001)
veneOffset=0.0f;
}
}
}
}
}

注意:在释放模式和OpenMP激活的情况下,我得到的结果与禁用OpenMP相同。调试模式和OpenMP激活会得到不同的结果,OpenMP停用会得到与发布相同的结果。

至少有两种可能性:

  • 启用优化可能会导致编译器重新排序操作。与调试模式下执行的顺序相比,这可能会在浮点计算中引入微小的差异,在调试模式下,操作不会重新排序。这可以解释调试和发布之间的数字差异,但不能解释调试模式下一次运行与下一次运行之间的数字差异
  • 您的代码中存在与内存相关的错误,例如读/写超过数组边界、使用未初始化的变量、使用未分配的指针等。请尝试通过内存检查器(如优秀的Valgrind)运行它,以识别此类问题。与内存相关的错误可能是非确定性行为的原因

  • 如果您在Windows上,则不可用(遗憾的是),但您可以查找替代列表。

    需要仔细检查的一件事是所有变量都已初始化。很多时候,未优化的代码(调试模式)将初始化内存。

    我会说变量初始化在调试中,而不是在发布中。但您的结果不会支持这一点(发布时的可靠结果)

    您的代码是否依赖于任何特定的偏移量或大小?调试生成将在某些分配周围放置保护字节

    它可能与浮点相关吗

    调试浮点堆栈不同于为提高效率而构建的版本


    看看这里:

    几乎任何未定义的行为都可以解释这一点:未初始化 变量、恶意指针、同一对象的多个修改 在没有中间序列点的情况下 结果有时是不可产生的,这在某种程度上有助于 未初始化的变量,但也可能因指针问题或 限制错误

    请注意,优化可能会改变结果,尤其是在Intel上。 优化可以更改哪些中间值溢出到内存,以及 如果您没有仔细使用括号,甚至计算顺序 用一种表情。(我们都知道,在机器浮点中,
    (a+
    b) +c)!=a+(b+c)
    )结果仍然应该是确定的: 您将根据优化程度得到不同的结果,
    但是对于任何一组优化标志,您都应该得到相同的结果。

    要详细说明我的评论,以下代码很可能是问题的根源:

    #pragma omp parallel for shared(last, bestHit, cVal, rad, veneOffset)
    {
        ...
        last = GetSADFloatRel(adapted, &fSamples.at(offset), 4*w+1, 4*w+1, 0);
        if(bestHit > last)
        {
    
    last
    仅在再次读取之前分配给,因此如果您确实需要并行区域外最后一次迭代的值,则它很适合作为
    lastprivate
    变量。否则,只需将其设置为私有

    获取
    #pragma omp critical
    if (bestHit > last)
    {
        bestHit = last;
        rad = (r+8)*0.25f;
        cVal = c * 2;
        veneOffset =(-0.5f + (1.0f / 3.0f) * k + (1.0f / 3.0f) / 2.0f);
        if(fabs(veneOffset) < 0.001)
            veneOffset = 0.0f;
    }
    
    #pragma omp parallel private(last)
    {
        int rBest = 0, kBest = 0, cBest = 0;
        float myBestHit = bestHit;
    
        #pragma omp for
        for(int r = 0; r < 53; ++r)
        {
            for(int k = 0; k < 3; ++k)
            {
                for(int c = 0; c < 30; ++c)
                {
                    for(int o = -1; o <= 1; ++o)
                    {
                        /*
                        r: 2.0f - 15.0f, in 53 steps, representing the radius of blood vessel
                        c: 0-29, in steps of 1, representing the absorption value (collagene)
                        iO: 0-2, depending on current radius. Signifies a subpixel offset (-1/3, 0, 1/3)
                        o: since we are not sure we hit the middle, move -1 to 1 pixels along the samples
                        */
    
                        int offset = r * 3 * 61 * 30 + k * 30 * 61 + c * 61 + o + (61 - (4*w+1))/2;
    
                        if(offset < 0 || offset == fSamples.size())
                        {
                            continue;
                        }
                        last = GetSADFloatRel(adapted, &fSamples.at(offset), 4*w+1, 4*w+1, 0);
                        if(myBestHit > last)
                        {
                            myBestHit = last;
                            rBest = r;
                            cBest = c;
                            kBest = k;
                        }
                        last = GetSADFloatRel(input, &fSamples.at(offset), w * 4 + 1, w * 4 + 1, 0);
                        if(myBestHit > last)
                        {
                            myBestHit = last;
                            rBest = r;
                            cBest = c;
                            kBest = k;
                        }
                    }
                }
            }
        }
        #pragma omp critical
        if (bestHit > myBestHit)
        {
            bestHit = myBestHit;
            rad = (rBest+8)*0.25f;
            cVal = cBest * 2;
            veneOffset =(-0.5f + (1.0f / 3.0f) * kBest + (1.0f / 3.0f) / 2.0f);
            if(fabs(veneOffset) < 0.001)
            veneOffset = 0.0f;
        }
    }