【问题标题】:Speeding up Monte Carlo Pi加速蒙特卡洛 Pi
【发布时间】:2013-11-12 02:23:43
【问题描述】:

我编写了一个 C++ 程序,用于通过“将随机点放入四分之一圆并计算它们等”来计算 pi。现在我的程序在我看来有点慢,我已经考虑过一些改进来加快它(源代码如下)。
我的第一个想法是使用 OpenMP 使其成为多线程,即将 (I) 和 (II) 之间的代码拆分为多个线程,这样我就可以进行近十倍的循环,而不必等待更长时间(在八核系统上)。
我的另一个想法是使用全局变量和指针,这样我只需要复制指针而不是整数元组。缺点是(idk)?
那么,我还能做些什么来加快程序的速度呢?我主要使用 Windows,但我也可以使用 Unix/Linux。
非常感谢!

代码部分:

    #include <cstdlib>
    #include <iostream>
    #include <tuple>
    #include <math.h>
    #include <time.h>
    #include <omp.h>
    #include <sys/time.h>

    #define RAND_MAX 32000
    #define LOOPS 1000000

    inline std::tuple<int, int> Throw_points(void)
    {

        int i = 0, j = 0;
        i = rand() % 1000;
        j = rand() % 1000;
        return std::make_tuple(i, j);
    }

    inline bool is_in_circle(std::tuple<int, int> point)
    {
        if ((pow(std::get<0>(point), 2) + pow(std::get<1>(point), 2)) <= pow(1000, 2))
            return true;
        else
            return false;
    }

    inline double pi(void)
    {
        srand(time(NULL));
        long long int in_circle = 0;
        long long int out_circle = 0;
        for (int i = 0; i < LOOPS; i++)
        {
            if (is_in_circle(Throw_points()))
                in_circle++;
            out_circle++;
        }
        return double(in_circle) / double(out_circle) * 4;
    }

拨打pi()

【问题讨论】:

  • 计算π的方法很慢; IIRC 我的数学课程,精度是1/sqrt(LOOPS)(但我可能是错的)。所以阅读一些数学书籍来更快地计算π!并阅读approximations of π 上的维基页面
  • 不会有多大帮助,但是你应该缓存pow(1000, 2)的结果,或者最后用1000*1000替换它,这比pow快很多,编译器会缓存结果给你。
  • @BasileStarynkevitch:我必须使用这种方法,否则我已经切换了......
  • 然后使用openmp#pragma-s
  • 另外,在is_in_circle的函数参数中加一个&。你现在继续复制你的元组,而你可以通过引用传递它。 @ViníciusGobboA.deOliveira 关于省略 pow(...) 的评论也适用于其他平方运算:我希望 std::get&lt;0&gt;(point) * std::get&lt;0&gt;(point)pow(std::get&lt;0&gt;(point), 2) 快得多。

标签: c++ multithreading


【解决方案1】:

我只是玩了一下这个。实际上,cmets 中对原始帖子(包括我自己的)的所有建议几乎没有任何区别。

但是,摆脱元组

inline void Throw_points(int&i, int&j)
{
    i = rand() % 1000;
    j = rand() % 1000;
}

inline bool is_in_circle(int i, int j)
{
    return i*i + j*j < 1000000;        
}

将程序加快了 5 倍。

顺便说一下,我使用了这里的 boost::progress_timer 解决方案:How to get the time elapsed running a function in C++

【讨论】:

    【解决方案2】:

    性能观察。使用分析工具;这会告诉您代码在哪里花费时间。一般来说,这总是令人惊讶的。

    如果你在 gcc 土地使用 gprof

    【讨论】:

      【解决方案3】:

      一些随机观察:

      • 乘法平方可能比调用pow 更快。特别是,您不想每次都计算常数 pow(1000,2)
      • int 计数器可能比 long long 快​​ - 您已经将循环数限制为由 int 表示。
      • 通过引用传递可能会更快。或者它可能会更慢,因为类型很小。或者它可能没有任何区别,因为函数应该是内联的。
      • if (X) return true; else return false; 而不是 return X; 很奇怪,但可能不会影响性能。
      • rand() 对于蒙特卡罗模拟可能不够随机;它的目的是快速,但质量不高。不幸的是,好的伪随机生成器非常慢。 C++11 库有多种选择。

      如果您确实将其设为多线程,请确保每个线程都有不同的随机种子;否则,他们只会重复彼此的工作。您将无法使用 rand(),因为它不是线程安全的。

      【讨论】:

      • 使用 rand 是前提条件,而且 pi 相当不错(3.145 等)
      • 是的,但我认为 Mike 的意思是 rand() 不是高质量的 PRNG。
      • @PeteBecker:我知道,但我们只需要计算 pi 到 komma 之后的第二个数字...
      • @arc_lupus:如果这个任务足够好,那很好。但值得记住的是,当您需要统计良好的随机性时,它并不是很好。
      • 关于rand() 的优点。对于这个特定的任务,它是足够随机的,但对于现实生活中的问题,我根本不会相信它。除了使用rand 之外,一个可能更严重的问题是使用 % 运算符来限制范围:某些值会比其他值(稍微)更频繁地出现。
      猜你喜欢
      • 2020-08-27
      • 2019-08-04
      • 2018-02-10
      • 2014-03-26
      • 1970-01-01
      • 2021-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多