【问题标题】:How to speed up calculation using multiple threads?如何使用多线程加速计算?
【发布时间】:2011-06-07 19:27:04
【问题描述】:

我正在尝试计算 Pi,但我真正想要实现的是使用多个线程时的效率。该算法很简单:我在单位正方形中随机生成点,然后计算其中有多少点位于正方形内接的圆圈中。 (更多:http://math.fullerton.edu/mathews/n2003/montecarlopimod.html) 我的想法是水平分割正方形并为它的每个部分运行不同的线程。 但我得到的不是加速,而是延迟。任何想法为什么?代码如下:

public class TaskManager {

public static void main(String[] args) {

    int threadsCount = 3;
    int size = 10000000;
    boolean isQuiet = false;

    PiCalculator pi = new PiCalculator(size);   
    Thread tr[] = new Thread[threadsCount];

    long time = -System.currentTimeMillis();

    int i;
    double s = 1.0/threadsCount;
    int p = size/threadsCount;

    for(i = 0; i < threadsCount; i++) {
        PiRunnable r = new PiRunnable(pi, s*i, s*(1.0+i), p, isQuiet);
        tr[i] = new Thread(r);
    }

    for(i = 0; i < threadsCount; i++) {
        tr[i].start();
    }

    for(i = 0; i < threadsCount; i++) {
        try {
            tr[i].join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }       

    double myPi = 4.0*pi.getPointsInCircle()/pi.getPointsInSquare();
    System.out.println(myPi + " time = " + (System.currentTimeMillis()+time));
}
}

public class PiRunnable implements Runnable {

PiCalculator pi;
private double minX;
private double maxX;
private int pointsToSpread;

public PiRunnable(PiCalculator pi, double minX, double maxX, int pointsToSpread, boolean isQuiet) {
    super();
    this.pi = pi;
    this.minX = minX;
    this.maxX = maxX;
    this.pointsToSpread = pointsToSpread;
}


@Override
public void run() {
    int n = countPointsInAreaInCircle(minX, maxX, pointsToSpread);  
    pi.addToPointsInCircle(n);
}

public int countPointsInAreaInCircle (double minX, double maxX, int pointsCount) {
    double x;
    double y;

    int inCircle = 0;

    for (int i = 0; i < pointsCount; i++) { 
        x = Math.random() * (maxX - minX) + minX;
        y = Math.random();          

        if (x*x + y*y <= 1) {
            inCircle++;
        }
    }

    return inCircle;

}

}


public class PiCalculator {

private int pointsInSquare;
private int pointsInCircle;

public PiCalculator(int pointsInSquare) {
    super();
    this.pointsInSquare = pointsInSquare;
}

public synchronized void addToPointsInCircle (int pointsCount) {
    this.pointsInCircle += pointsCount;
}

public synchronized int getPointsInCircle () {
    return this.pointsInCircle;
}

public synchronized void setPointsInSquare (int pointsInSquare) {
    this.pointsInSquare = pointsInSquare;
}

public synchronized int getPointsInSquare () {
    return this.pointsInSquare;
}

}

一些结果: - 对于 3 个线程:“3.1424696 时间 = 2803” -for 1 个线程:“3.1416192 时间 = 2337”

【问题讨论】:

  • 您是否在多核系统上运行?
  • 我在 Intel core 2 duo 上运行。
  • 他在加入之前就开始了所有这些,所以这没关系。如果您只有 2 个内核,那么使用超过 2 个线程是没有用的。您的应用程序完全受 CPU 限制,因此线程数多于内核数只会由于上下文切换的开销而减慢速度。
  • 我在我的版本中使用了以下final int threadsCount = Runtime.getRuntime().availableProcessors();

标签: java multithreading performance


【解决方案1】:

您的线程可能正在争夺/等待同步的Math.random(),您应该为每个线程创建一个 java.util.Random 实例。此外,在这种情况下,只有当您拥有多个内核/cpu 时才会发生多线程加速。

来自Math.random()的javadoc:

此方法已正确同步 允许多人正确使用 线。但是,如果很多线程需要 在 a 处生成伪随机数 很大的速率,它可以减少争用 每个线程都有自己的 伪随机数生成器。

【讨论】:

  • 我将Math.random() 更改为Random 的本地实例并得到以下输出3.1411296 time = 253 Core i3 M350 2.27GHZ / 8GB RAM
【解决方案2】:

这是一个备用的main 方法,它使用java.util.concurrency 包而不是手动管理线程并等待它们完成。

public static void main(final String[] args) throws InterruptedException
{

    final int threadsCount = Runtime.getRuntime().availableProcessors();
    final int size = 10000000;
    boolean isQuiet = false;

    final PiCalculator pi = new PiCalculator(size);
    final ExecutorService es = Executors.newFixedThreadPool(threadsCount);

    long time = -System.currentTimeMillis();

    int i;
    double s = 1.0 / threadsCount;
    int p = size / threadsCount;

    for (i = 0; i < threadsCount; i++)
    {
        es.submit(new PiRunnable(pi, s * i, s * (1.0 + i), p, isQuiet));
    }

    es.shutdown();

    while (!es.isTerminated()) { /* do nothing waiting for threads to complete */ }

    double myPi = 4.0 * pi.getPointsInCircle() / pi.getPointsInSquare();
    System.out.println(myPi + " time = " + (System.currentTimeMillis() + time));
}

我还更改了Math.random() 以对每个Runnable 使用Random 的本地实例。

final private Random rnd;

...    

x = this.rnd.nextDouble() * (maxX - minX) + minX;
y = this.rnd.nextDouble();

这是我得到的新输出...

3.1419284 time = 235

我认为您可能可以使用Futures 减少更多时间,而不必在PiCalculator 上进行太多同步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-31
    • 2018-07-02
    • 2021-06-27
    • 2020-09-01
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多