【问题标题】:Monte Carlo to calculate Pi on multiple ThreadsMonte Carlo 在多个线程上计算 Pi
【发布时间】:2015-06-19 21:44:00
【问题描述】:

我用 Java 编写了一个简单的程序来通过蒙特卡洛方法计算 Pi。由于您需要大量滴水才能使 Pi 变得“精确”,而且它当然会变慢,所以我决定实施多线程。现在我的问题是:有什么方法可以加快计算速度?并且一次只计算每个物理线程一次迭代,还是我的多线程概念完全错误?

代码如下:

public class PiMC{
    public static void main(String[] args) {
        ExecutorService exec=Executors.newCachedThreadPool();
        //Future object is used to get result from a thread
        Future<Double> result0=exec.submit(new Thread1());
        Future<Double> result1=exec.submit(new Thread1());
        Future<Double> result2=exec.submit(new Thread1());
        Future<Double> result3=exec.submit(new Thread1());
        Future<Double> result4=exec.submit(new Thread1());
        Future<Double> result5=exec.submit(new Thread1());
        Future<Double> result6=exec.submit(new Thread1());
        Future<Double> result7=exec.submit(new Thread1());

        try
        {
            System.out.println(((result0.get() + result1.get() + result2.get() + result3.get() + result4.get()+ result5.get() + result6.get() + result7.get()) / Long.MAX_VALUE) * 4);
        }
        catch(InterruptedException e){System.out.println(e);}
        catch(ExecutionException e){System.out.println(e);}
    }
}

class Thread1 implements Callable {
    @Override
    public Double call() throws Exception {
        long drops = Long.MAX_VALUE / 8;
        //long drops = 500;
        double in = 0;
        for (long i = 0; i <= drops; i++) {
            double x = Math.random();
            double y = Math.random();
            if (x * x + y * y <= 1) {
                in++;
            }
        }
        return in;
    }
}

【问题讨论】:

  • 我不明白这个问题。对于初学者, ExecutorService 将默认为逻辑处理器数量的池大小 - 所以如果你只有 2 个内核,那么它​​只会同时运行两个线程。多线程只会在每个线程的 call/run 方法中运行一次代码。
  • 您看不到加速的另一个可能原因是Math.random() 在内部使用相同的java.util.Random 实例,这会导致一些资源争用。来自Math.random()的API文档:However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.
  • 我为一个 8 核的朋友扩展了它,以便他可以更快地计算它,但他报告说他有 105% 的工作量。我的负荷也很高。两个随机数生成和计算不应该只需要一点线程的容量吗?好的,API的问题可能是一个原因,有没有办法解决这个问题?
  • @Acru 如果您想保持较小的负载,为什么要使用并行计算?多线程当然会最大化 CPU 利用率(如果做得好的话),这就是我们使用并行计算的目的。
  • 如果您将 CPU 想象成具有多个入口(这些是核心)的排水管,那就更容易了。每个线程都是您要倒入排水管的一桶水。如果您有 8 个进水口和只有 1 个水桶,那么您将只使用 1/8 的进水口,因为您不能将 1 个水桶中的水同时倒入两个进水口。如果您有 8 个入口和 8 个水桶,则可以充分利用排水管。如果你有 12 个桶,那么地板上总会有 4 个排成一列,等待清空。

标签: java multithreading montecarlo pi


【解决方案1】:

您知道 Long.MAX_VALUE / 8 有多大吗?它是 (2^63 - 1) / 8 大约 1e18... 相当大的数字(即使当今最好的计算机填满整个建筑物也至少需要 1000 秒,请参阅评论)。

更好的方法是将先前计算的值与 pi 的当前值进行比较并进行比较。如果差为 0(因为精度有限而发生 -> eps 是最小的数字 > 0 其中 1 + eps != 1)取消执行并返回值:

int sum = 0, drops = 0;
double pi = 0, oldPi;
do {
     oldPi = pi;
     double x = Math.random(), y = Math.random();
     if (x * x + y * y <= 1)
         sum++;
     drops++;
     pi = 4.0 * sum / drops;
} while (pi != oldPi || pi < 3); // pi < 3 to avoid problems when the first
// drops are outside of the circle, pi == 0 would also work, BUT setting
// pi to a value different from 0 at the beginning can still fail with only pi != oldPi

如果你想使用多个线程,这很困难,因为 pi 值的更新必须同步,我猜你不会有太多收获。然而,多个线程可以独立计算 pi,您可以比较(或平均)结果。

【讨论】:

  • 是的,但我希望计算的简单性能使程序足够快,以便在合理的时间内解决。
  • 我将每个线程的(假想)圆中出现的水滴加在一起,然后从中计算出 Pi。
  • 很遗憾没有。最好的超级计算机在 peta FLOP/s(每秒浮点运算)领域。因此,如果你可以一滴那么快,它仍然需要 1000 秒(而一滴实际上需要不止一次操作)。此外,如果您达到最大精度,则继续没有意义(假设算法收敛到某个数字)。
  • 好吧,看来我低估了要做的工作,高估了工人,谢谢。
猜你喜欢
  • 2020-09-09
  • 1970-01-01
  • 2020-12-20
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 1970-01-01
  • 2021-03-11
相关资源
最近更新 更多