Izračunavanje broja PI i implementacija preko ParallelFX


Uvod

Pretražujući po arhivi staroj i više od 20 godina, slučajno sam naletio na jedan matematički časopis „Matematika – stručno metodički časopis“ Zagreb 1987 godina. Pored ostalog u časopisu se nalazi i članak o računanju broja PI pomoću računara. Pomenuti članak daje i izvornji kod u PASCAL-u za izračunavanje broja PI na 1000 decimala.

Odmah se javila ideja na koji način ovaj problem ponovno aktivirati te ga osvježiti novim tehnologijama. Prije bilo kakvog upuštanja u ovaj problem, naravno „konsultovao“ se sa Google, te potražio aktuelnosti povodom ovog problema.

Na wikipediji sam pronašao Machine-ov algoritam za računanje broja PI (dosta svjež članak 16. Feb. 2009).

Nadalje, našao sam podatak ko drži Ginisov rekord u izračunavanju broja decimala, to je japanski profesor sa 1,24 triliona cifara.

Nadalje, postoji na internetu bezbroj primjera sa source codeom izračunavanja broja PI u skoro svakom programskoj jeziku. Na kraju ovo posta biće pobrojani neki zanimljivi linkovi u vezi ovog problema.

Teorija o broju PI

PI je realan broj koji definišemo kao omjer obima i dijametra kruga . Često ovu konstantu zovu još i Archimedes-ova konstanta ili Ludolph-ov broj. Prije 13 godina otkrivena je BBP formula.

John Machine engleski naučnik prvi je razvio efikasan algoritam za izračunavanje broja PI. Osnovna vrijednost ove formule je da brže konvergira od tada dostupnih formula poput Leibnic – ove formule, i postaje vrlo praktična pri izračunavanju. Machine-ova formula za izračunavanje broja Pi data je sljedećim izrazom:

……………………(1)

Ovom formulom Machine je došao do 100 cifara broja PI. Da bi se gornja formula mogla koristiti razvija se u Taylorov red na sljedeći način:

clip_image006_asdtrwe12345 , gdje je …………………(2)

Pored osnovne formule za izračunavanje broja PI, postoji čitav niz formula koje su se kasnije razvijale a sve imaju sljedeći zapis:

, gdje su i cijeli brojevi.

Shodno gornjem zapisu definisane su Machine formule sa dva i više izraza.

Formule sa dva izraza:

…………………(3)

…………………(4)

…………………(5)

Formule sa 4 izraza:

clip-image010 …………………(6)

clip-image024 …………………(7)

Formule sa 6 i 7 izraza i koje su najefikasnije u izračunu broja PI:

clip-image026 …………………(8)

clip-image028 …………………(9)

Više informacija oovim formulama može se naći na wikipedia.

Implementacija u C#

S obzirom da želimo računati decimale broja PI veće od broja decimala standardnih typova podataka u .NET, potrebno je implementirati apstraktni tip koji će imati mogućnost prikaza broja sa više od 100 decimala odnosno cifara.

Postoji bezbroj implementacija ovakovog tipa podataka na internetu. Na CodeProject postoji C++ verzija izračunavanja broja PI na više hiljada decimala. Također, na CodeProject postoji C# članak o računanju sa velikim brojevima. U ovaj blog ne bi stale sve implementacije koje su napravljene u C, C++, PASCAL, JAVA i td o računanju broja PI, a koje se nalaze na internetu.

Implementacija koju sam odabrao za ovaj post je implementacija je od Julian-a M Bucknall Chief Technology Officer u Developer Express. Članak koji je on napisao o izračunavanju broja PI nalazi se na ovom linku. On u svom članku objašnjava implementaciju izračunavanja broja PI ,te prezentira metode osnovnih algebarskih operacija: +, -, *, /. Inače, svaka implementacija koja manipuliše sa velikim brojavima bazira se na računanju odnosno sabiranju, oduzimanju djeljenju i množenju na način kako smo to radili u osnovnoj školi, ili tačnije pismeno sabiranje, oduzimanje, dijeljenje i množenje.

Kada želimo dva broja sabrati, tada činimo na sljedeći način:

123546546
+ 25487888
_________________
149034434

Sabiranje radimo tako što krećemo od jedinica te zbrajanje potpisujemo na mjesto jedinica rezultata. Ukoliko je zbir veći od deset pišemo jedinice a desetice pamtimo i prenosimo je u sljedeće sabiranje. Na analogan način vrše se ostale algebarske operacije. Julian je u svom članku potanko opisao implementaciju koju nećemo ovdje ponavljati.

Analiza klase BigNumber

Julianova BigNumber klasa sadrži osnovne operacije, nekoliko metoda verifikacije te ArcTan metodu koja vrijednost arctg funkcije razvija u Taylorov red i izračunava svaki član pojedinačno. Ova metoda uzima argumente UInt32 multiplicand, UInt32 reciprocal, gdje se prvi argument množi sa arctg, a drugi je nazivnik broja nad kojim se vrši arctg operacija.

U primjeru koji je dao korištena ja Machinova formula (1). Analizirajući formulu (1) vidi se da drugi izraz formule znatno brže konvergira u odnosu na prvi. U testovima koje sam provodio za 50.000 decimala prvi izraz konvergira za otprilike 17 sekundi dok drugi izraz za oko 4 sekunde. Više detalja kasnije u postu.

Implementacija Paralelnog izračunavanja

1. Paralelizam kod operacija +,-,*,/

Sagledavajući klasu i analizirajući gdje bi se mogla izvršiti implementacija paralelnog izračunavanja otpala je prva varijanta da se paralelizam primjeni u metodama osnovnih operacija. Ovdje je situacija da se pri izračunavanju polazi od jedinica, kada se jedinice izračunaju prelazi se na desetice i td. Primjećujemo da se ne može vršiti paralelno izračunavanje jedinica i desetica i sl.

2. Paralelizam kod izračunavanja ArcTan metode

Druga varijanta također poslije dublje analize, nije polučila značajniju efikasnost iz razloga što se ova metoda izvršava dosta sekvencijalno. Npr. Prvo se izračuna x, zatim se u while petlji računaju članovi taylorovog reda na način da se na osnovu prethodnog dobija novi član reda. Opet i u ovom slučaju paralelizam se ne bi isplatio implementirati.

3. Paralelizam kod izračunavanja članova izraza machinove formue

Jedina mogućnost koja je ostala je ta da se izrazi u formuli (1) paraleliziraju. Ovo je moguće jer su neovisni jedan od drugog. Poslije izračunavanja izraza dolazi njihovo sublimiranje, zavisno od operacije sabiranja ili oduzimanja.

Da bi paralelizirali gornje koristićemo metodu Parallel.Invoke (stara Do metoda iz decembarskog CTP-a). Svi testovi koji su rađeni implementirani su sa Junskim CTP izdanjem ParallelFX.

Test 1: Sekvencijalna i paralelna implementacija formule (1)

Poslije implementacije pokrećemo prvi test :

1. Formula (1)

2. Broj decimala 10.000

3. PC Intel Core 2 Quad 2,4 MHz

Rezultat testa:

clip-image030

Iz prezentiranog testa vidi se da nismo puno uštedjeli vremena. S obzirom da se radi o 4 jezgre ipak ova implementacije je nezadovoljavajuća.

Obrazloženje: Parallel.Invoke metoda poziva dvije metode, odnosno paralelno se izvršavanju dvije metode, maksimalna efikasnost implementacije bila bi 100%. Međutim test je pokazao svega 20%. Razlog tome leži u činjenici da drugi izraz clip_image1006_asdtrwe12345 brže konvergira od prvog izraza. To znači da se drugi izraz završio za 0,153 sec, dok se prvi izraz izvršava ostatak vremena. Obzirom da Invoke metoda mora sačekati sve niti da se završe pa tek onda da se nastavi izvršavanje koda.

Iz ovoga zaključujemo da moramo promijeniti formulu, te uzeti formulu koja ima više izraza i u kojoj izrazi imaju približno jednaku konvergenciju. U idealnom slučaju na posmatranom PC mogli bi dobiti punu brzinu kada bi izrazi imali istu konvergenciju i kada bi broj izraza u formulu iznosio 4. Tada bi vrijeme paralelnog i sekvencijalnog izvršavanja bilo 4:1 što bi bio maximalni efekat.

Iz tog razloga potrebno je izabrati jednu od formula 6,7 ili 8. Formula 9 nije upotrebljiva jer bi bilo potrebno posebno implementirati dijeljenje velikog broja sa velikim brojem što će možda biti predmet jednog drugog blog posta.

S druge strane konvergencije izraza u formulama 6,7 i 8 dosta brže konvergiraju u odnosu na formulu (1). Da bi dokazali konvergenciju napravimo sljedeći test:

Test 2: Sekvencijalna usporedba konvergencija Formula (1) i (7)

1. Formula (1) (7)

2. Broj decimala 10.000

3. PC Intel Core 2 Quad 2,4

4. Sekvencijalna implementacija

Rezultat testa:

clip-image034

Iz testa se vidi da formula (7) brze konvergira od formule (1). Mijenjanjem formule bez paralelizma smo obezbijedili brze izvršavanje za oko 15 %.

Test 3 Paralelna implementacije formule (7)

1. Formula (7)

2. Broj decimala 10.000

3. PC Intel Core 2 Quad 2,4

Rezultat testa:

clip-image034

Primjenom formule 7 na 10.000 decimala efikasnost se popela na 100%.

Test na 100.000 decimala rezultat je pokazao sljedeće:

clip-image038

Iz prethodnog testa mogli smo vidjeti da kada se broj decimala povećava, povećava se i razlika u vremenu izvršavanja. Razlika u vremenima 1. i 2. prethodnog testa je utjecaj formiranja niti kod paralelnog izvršavanja Invoke metode.

Test 4. Paralelna implementacija formule (8)

1. Formula (8)

2. Broj decimala 10.000

3. PC Intel Core 2 Quad 2,4

clip-image040

Zadnjim testom vidimo da formula 8 sporije konvergira od formule 7, kada je u pitanju sekvencijalna implementacija, dok je paralelna implementacija dosta brža. Razlog tome je što u formuli (8) postoji više od jednog izraza koji sporije konvergira u odnosu na formulu (7), a obzirom da invoke metoda optimizira sve paralelne niti i koristi „work stealing“ tj. kada se izraz sa najbržom konvergencijom izvrši nit se ne uništava nego se posuđuje izrazu koji čeka na izvršavanje.

Rezultati su čak bolji od prezentiranog kada se koristi više decimala, što ostavljam čitaocu na testiranje.

Zaključak

Ovim člankom prezentiran je način kako operacije koje zahtjevaju značajno procesorsko vrijeme implementirati i iskoristiti višejezgrene procesore, a samim tim i smanjiti vrijeme izvršavanja. Takodjer, je prikazan jedan od načina primjene ParallelFX u sekvencijalnim implementacijama. Implementacija paralelnog izvršavanja koda moguća je samo u slučajevima kada je vrijeme izvršavanja sekvencijalnog koda značajno dugo, a vrijeme potrebno za formiranje niti za paralelno izvršavanje zanemarljivo u odnosu na ukupno vrijeme. Također , pokazan je jedan od načina na koji je potrebno pristupiti problemu kada želimo primjenjivati ParallelFX. Prezentacija pokazuje da ParallelFX biblioteka ne zahtjeva značajnu izmjenu koda prilikom implementacije, što predstavlja najvažniju činjenicu prezentiranu ovim člankom. Svaki sekvencijalni kod se ne može uspješno paralelno implementirati. Faktori koji utiču su mnogobrojni, i različiti od slučaja do slučaja. S toga prije svake implementacije nužna je detaljna analiza.

Source code ovog članaka sa implementacijom svih testova koji su provedeni nalazi se ovdje.

About Bahrudin Hrnjica

PhD in Mechanical Engineering, Microsoft MVP for .NET. Likes .NET, Math, Mechanical Engineering, Evolutionary Algorithms, Blogging.

Posted on 02/03/2009, in .NET, C# and tagged , , . Bookmark the permalink. 4 Comments.

  1. Good topic, interesting articles, easy to understand writing – that’s what I’ve been searching for and I’m happy I found it on your website! Definitely a pleasure to add your site to my favorites and be going to read that often!

  2. Reblogged this on Bahrudin Hrnjica Blog and commented:

    How to calculate Pi number by using ParalleFx and C#

  1. Pingback: C# 4.0 LINQ to Object - Zip operator - Blog o C++ i C#

  2. Pingback: C# 4.0 LINQ to Object – Zip operator « Bahrudin Hrnjica Web Page

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s