C代码的半随机慢

时间:2013-11-18 01:31:50

标签: c++ c

我有一个更长的代码,尽可能减少,同时保持问题的存在。我的代码针对不同的参数值运行MCMC计算。对于某些值组合,代码运行时间要长得多,比典型情况慢大约100倍。但是,它不应该,因为操作的数量不依赖于参数值。

我在AMD64 Linux机器上运行它,在Gentoo上使用GCC 4.8.1编译glibc-2.17。编译标志无关紧要。我还在一个使用较旧的AMD64处理器的不同Gentoo盒子上进行了测试,结果是一样的。

我做了很多测试:

  • 我尝试用Valgrind进行调试,但没有发现内存问题或其他令人讨厌的事情。
  • 其次,我尝试在修复有问题的参数值的情况下运行代码,但是没有遇到缓慢的问题。
  • 我尝试在迭代之间放置sleep(4),但没有任何改变。

问题显示迭代何时命中k = 1i = j = 0,转换为mu[0] = -0.05mu[1] = -0.05mu[2] = 0.05。正如我所说,对所有迭代使用这个固定值消除了我所看到的问题。

以下是解决问题的方法:

  • 更改限制。
  • 修复mu[]系数。
  • 从计算中删除dW3
  • 删除rand()
  • 删除q
  • 的计算
  • 删除s[j]
  • 的更新

我已经阅读了一些关于slowpow的退出,因此尝试通过编写我自己的版本来消除exp。这解决了我对这个MWE的问题,但是当重新实现的exp放在生产代码中时却没有。

问题:导致半随机缓慢的原因是什么?

MWE的代码如下。所有关于如何进行的帮助和建议将不胜感激。

注意:此代码使用g++进行编译,尽管它基本上是C。更改编译器不会改变任何内容。

关于分支预测:使用

删除其中一个if语句
q = exp(dW);        
q = q / (1.0 + q); 

无论dW的值是什么,都不会改变代码的行为;如果这确实是由于分支预测,则必须归因于第二个if


#include <cstdio>
#include <cstdlib>
#include <cmath>

inline int index(int const i, int const j, int const n)
{
    return (i + n) % n + ((j + n) % n) * n;
}

void get_sample(int* s, int n, double* mu)
{
    for (int i = 0; i < 10 * n * n; i++)
    {
        int j = i % (n * n); 
        int x = j % n;
        int y = (j - x) / n;

        double dW1 = mu[0] * (s[index(x - 1, y, n)] + s[index(x + 1, y, n)] + s[index(x, y - 1, n)] + s[index(x, y + 1, n)]);
        double dW2 = mu[1] * (s[index(x - 1, y - 1, n)] + s[index(x + 1, y - 1, n)] + s[index(x + 1, y + 1, n)] + s[index(x - 1, y + 1, n)]);
        double dW3 = mu[2] * (s[index(x - 1, y, n)] * s[index(x - 1, y - 1, n)] * s[index(x, y - 1, n)] + s[index(x - 1, y, n)] * s[index(x - 1, y + 1, n)] * s[index(x, y + 1, n)]
                                        + s[index(x, y + 1, n)] * s[index(x + 1, y + 1, n)] * s[index(x + 1, y, n)] + s[index(x + 1, y, n)] * s[index(x + 1, y - 1, n)] * s[index(x, y - 1, n)]);

        double dW = 2.0 * (dW1 + dW2 + dW3);

        double q;
        if (dW < 0.0)
        {   
            q = exp(dW);

            q = q / (1.0 + q); 
        }
        else
        {
            q = exp(-dW);

            q = 1.0 / (1.0 + q);
        } 

        double p = ((double) rand()) / ((double) RAND_MAX);

        if (p < q)
        {
            s[j] = 1;
        }
        else
        {
            s[j] = -1;
        }
    }
}

int main(int argc, char** argv)
{
    double mu[3];

    double limits[6] = {-0.05, 0.8, -0.05, 0.45, -0.45, 0.05};

    int s[16];

    for (int i = 0; i < 16; i++)
    {
        s[i] = -1;
    }

    for (int k = 0; k < 2; k++)
    {
        for (int j = 0; j < 2; j++)
        {
            for (int i = 0; i < 2; i++)
            {               
                mu[0] = limits[0] + ((limits[1] - limits[0]) * i);
                mu[1] = limits[2] + ((limits[3] - limits[2]) * j);
                mu[2] = limits[4] + ((limits[5] - limits[4]) * k);

                printf(" Computing (% .6lf, % .6lf, % .6lf)...\n", mu[0], mu[1], mu[2]);

                for (int sample = 0; sample < 1000; sample++)
                {
                    get_sample(s, 4, mu);
                }                           
            }
        }
    }               

    return 0;
}

2 个答案:

答案 0 :(得分:3)

  

但是,它不应该,因为操作的数量不依赖于参数值。

但浮点运算的速度确实取决于参数值。如果在计算中引入NaN或其他异常值(我没有查看代码),则会大大降低浮点性能。

编辑:我在rdtsc周围手动分析(简单exp()计数)并且很容易将“好”和“坏”案件分开。当我打印出坏的情况时,它就在dW ~= 0。如果你将这种情况分开,你甚至可以获得表现:

    double q;
    if (dW < -0.1e-15)
    {
        q = exp(dW);

        q = q / (1.0 + q);
    }
    else if (dW > 0.1e-15)
    {
        q = exp(-dW);

        q = 1.0 / (1.0 + q);
    }
    else
    {
        q = 0.5;
    }

答案 1 :(得分:0)

如果我是对的并且分支预测是问题,那么你应该尝试

void get_sample(int* s, int n, double* mu)
{
    for (int i = 0; i < 10 * n * n; i++)
    {
        int j = i % (n * n); 
        int x = j % n;
        int y = (j - x) / n;

        double dW1 = mu[0] * (s[index(x - 1, y, n)] + s[index(x + 1, y, n)] + s[index(x, y - 1, n)] + s[index(x, y + 1, n)]);
        double dW2 = mu[1] * (s[index(x - 1, y - 1, n)] + s[index(x + 1, y - 1, n)] + s[index(x + 1, y + 1, n)] + s[index(x - 1, y + 1, n)]);
        double dW3 = mu[2] * (s[index(x - 1, y, n)] * s[index(x - 1, y - 1, n)] * s[index(x, y - 1, n)] + s[index(x - 1, y, n)] * s[index(x - 1, y + 1, n)] * s[index(x, y + 1, n)]
                                        + s[index(x, y + 1, n)] * s[index(x + 1, y + 1, n)] * s[index(x + 1, y, n)] + s[index(x + 1, y, n)] * s[index(x + 1, y - 1, n)] * s[index(x, y - 1, n)]);

        double dW = 2.0 * (dW1 + dW2 + dW3);

        double q;
        q = exp(dW *((dW>0)*2-1);

        q = ((dW>0)*q + (dW<=0)) / (1.0 + q); 

        double p = ((double) rand()) / ((double) RAND_MAX);

        s[j] = (p<q)*2-1;
    }
}

我也想知道一个好的编译器是否应该不进行这样的转换......