2011-06-07 37 views
5

我正在嘗試計算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」 - 用於1線: 「3.1416192時間= 2337」

+2

您是否在多核系統上運行? – ribram 2011-06-07 19:35:59

+0

我在intel core 2上運行。 – 2011-06-07 19:40:45

+1

他在加入之前就開始了他們,所以這很好。如果你只有2個內核,那麼使用2個以上的線程是沒有用的。由於上下文切換的開銷,您的應用程序純粹受CPU限制,因此比內核多的線程會減慢速度。 – ribram 2011-06-07 19:54:42

回答

9

你的線程可以戰鬥/等待對於同步的Math.random(),應該爲每個線程創建一個java.util.Random實例。同樣在這種情況下,只有當您有多個核心/ CPU時,纔會發生多線程加速。

Math.random()的Javadoc:

這種方法被適當地同步 ,以允許由一個以上的線程 正確使用。然而,如果許多線程需要 來以高速率生成僞隨機數,則它可以減少每個線程擁有其自己的 僞隨機數生成器的爭用 。

+0

中使用了以下內容,非常感謝。它的工作原理:) – 2011-06-07 19:44:19

+1

我將'Math.random()'改爲'Random'的本地實例,並得到以下輸出:'3.1411296 time = 253' Core i3 M350 2.27GHZ/8GB RAM – 2011-06-07 19:56:11

1

這是另一種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()使用的Random本地實例每個Runnable

final private Random rnd; 

...  

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

這是新的輸出我得到...

3.1419284 time = 235 

我想你也許可能下降的時間更多一些使用Futures而不必同步在PiCalculator這麼多。