3月14日圓周率日—使用並行計算求圓周率π

關於圓周率你們再熟悉不過了:
咱們從課本上學習到早在一千多年前,祖沖之將圓周率計算到3.1415926到3.1415927之間…計算機誕生後,計算圓周率被用來檢測計算機的硬件性能,晝夜燃燒cpu看會不會出問題…另一些人也想看看這個無限延伸的神祕數字背後是否有規律,能發現一些宇宙的祕密… java

 提起圓周率,不能不說起Fabrice Bellard,他被認爲是一位計算機天才,在業界有着重要的影響。1996年他編寫了一個簡潔可是完整的C編譯器和一個Java虛擬機Harissa。Fabrice Bellard發明的TinyCC是GNU/Linux環境下最小的ANSI C語言編譯器,是目前號稱編譯速度最快的C編譯器。Fabrice Bellard傑做衆多且涉及普遍,1998年編寫了一個簡潔的OpenGL實現TinyGL,2003年開發了Emacs克隆QEmacs,2005年還設計了一個廉價的數字電視系統。 算法

Fabrice Bellard使用一臺普通的臺式電腦,完成了衝擊由超級計算機保持的圓周率運算記錄的壯舉,他使用臺式機將圓周率計算到了小數點後2.7萬億位,超過了由目前排名世界第47位的T2K Open超級計算機於去年8月份創造的小數點後2.5萬億位的記錄。

Bellard使用的電腦是一臺基於2.93GHz Core i7處理器的電腦,這部電腦的內存容量是6GB,硬盤則使用的是五塊RAID-0配置的1.5TB容量的希捷7200.11,系統運行64位Red Hat Fedora 10操做系統,文件系統則使用Linux的ext4.

此次計算出來的圓周率數據佔去了1137GB的硬盤容量,Bellard花了103天的時間計算出了這樣的結果。

計算圓周率的方法有不少種:
微積分割圓法求:


或者利用便於計算機計算的丘德諾夫斯基公式法求: 分佈式



不過這些計算方法都比較複雜,難以讓讀者理解和使用並行計算來求,所幸數學上的泰勒級數是個好東西,它將微積分的東西改爲用無限級數來表示,這樣很容易進行並行計算分解:

π=4*∑(-1)^n+1/(2n-1) 或者寫爲: π=4*( 1-1/3+1/5-1/7+…)
也能夠獲得:πn =πn-1+(-1)^n+1/(2n-1),也就是能夠經過迭代前面的π值去求當前π值。


咱們根據上面公式先寫個單機程序來求:
public class PiTest
{
	public static void main(String[] args)
	{
		double pi=0.0;
		for(double i=1.0;i<1000000001d;i++){
			pi += Math.pow(-1,i+1)/(2*i-1);
		}
		System.out.println(4*pi);
	}
}

運行以上程序,並對照pi的標準值:3.141592653589793238462643383279…
若是i<10000,獲得pi = 3.141 6926635905345 (從紅色部分之後不精確了)
若是i<1000000,獲得pi = 3.14159 36535907742 (從紅色部分之後不精確了)
若是i<1000000000,獲得pi = 3.14159265 25880504(從紅色部分之後不精確了)
……
能夠看到,當迭代的輪數越大,求出的π值越精確。

因爲是無限累加,咱們能夠很容易改爲並行程序求解,好比i=4n,能夠分紅4段並行求解,再將4部分和合並起來獲得最終π值。假設咱們有4臺計算機,並行計算設計以下: 

 
咱們這裏經過fourinone提供的各類並行計算模式去設計,第一次使用能夠參考 分佈式計算上手demo指南,開發包下載地址: http://code.google.com/p/fourinone/

程序實現:
PiWorker:是一個π計算工人實現,咱們能夠看到它經過命令行輸入一個計算π值的起始值和結束值,咱們同時啓動4個PiWorker實例,啓動時指定不一樣的起始結束參數。

PiCtor:是一個π計算包工頭實現,它的實現很簡單,獲取到線上工人後,經過doTaskBatch進行階段計算,等待每一個工人計算完成後,將各工人返回的π計算結果合併累加。

運行步驟:
一、啓動ParkServerDemo(它的IP端口已經在配置文件指定)
java -cp fourinone.jar; ParkServerDemo


二、運行4個PiWorker,將迭代100,000,000輪的計算拆分到4個工人並行完成,這裏方便演示是在同一臺機器上,現實應用中能夠在多臺計算機上完成。
java  -cp fourinone.jar; PiWorker localhost 2008 1 250000000
java  -cp fourinone.jar; PiWorker localhost 2009 250000000 500000000
java  -cp fourinone.jar; PiWorker localhost 2010 500000000 750000000
java  -cp fourinone.jar; PiWorker localhost 2011 750000000 100000000


三、運行PiCtor
java  -cp fourinone.jar; PiCtor

 
能夠看到,4個工人實例在同臺機器並行完成計算π值的時間爲29秒,若是是運行單機程序PiTest完成的時間在45秒,精準度都是到小數點後8位「3.14159265」,可是耗時上有明顯差距,若是多機多實例,效率還會進一步提高,並行計算性能提高分析能夠參考「使用並行計算大幅提高遞歸算法效率」。

完整demo源碼以下:
// ParkServerDemo
import com.fourinone.BeanContext;
public class ParkServerDemo
{
	public static void main(String[] args)
	{
		BeanContext.startPark();
	}
}

//PiWorker 性能

import com.fourinone.MigrantWorker;
import com.fourinone.WareHouse;

public class PiWorker extends MigrantWorker
{
	public double m=0.0,n=0.0;
	
	public PiWorker(double m, double n){
		this.m = m;
		this.n = n;
	}
	
	public WareHouse doTask(WareHouse inhouse)
	{
		double pi=0.0;
		for(double i=m;i<n;i++){
			pi += Math.pow(-1,i+1)/(2*i-1);
		}
		
		System.out.println(4*pi);
		inhouse.setObj("pi",4*pi);

		return inhouse;
	}
	
	public static void main(String[] args)
	{
		PiWorker mw = new PiWorker(Double.parseDouble(args[2]),Double.parseDouble(args[3]));
		mw.waitWorking(args[0],Integer.parseInt(args[1]),"PiWorker");
	}
}

//PiCtor
import com.fourinone.Contractor;
import com.fourinone.WareHouse;
import com.fourinone.WorkerLocal;
import java.util.Date;

public class PiCtor extends Contractor
{
	public WareHouse giveTask(WareHouse inhouse)
	{
		WorkerLocal[] wks = getWaitingWorkers("PiWorker");
		System.out.println("wks.length:"+wks.length);
		
		WareHouse[] hmarr = doTaskBatch(wks, inhouse);
		
		double pi=0.0;
		for(WareHouse result:hmarr){
			pi = pi + (Double)result.getObj("pi");
		}
		
		System.out.println("pi:"+pi);
		return inhouse;
	}
	
	public static void main(String[] args)
	{
		PiCtor a = new PiCtor();
		long begin = (new Date()).getTime();
		a.giveTask(new WareHouse());
		long end = (new Date()).getTime();
		System.out.println("time:"+(end-begin)/1000+"s");
		a.exit();
	}
}
相關文章
相關標籤/搜索