t.start(); | t.start(); | ||||
s.solve(sieve); | s.solve(sieve); | ||||
t.end(); | t.end(); | ||||
System.out.println("Test "+i+": "+t.prettyTime()); | |||||
} | } | ||||
return Timer.median(timers); | return Timer.median(timers); | ||||
} | } | ||||
static Timer testFactor(Solver s, int maxNum, Sieve sieve) { | |||||
static Timer testFactor(Solver s, long maxNum, Sieve sieve) { | |||||
Timer[] timers = new Timer[nTests]; | Timer[] timers = new Timer[nTests]; | ||||
for (int i = 0; i < nTests; ++i) { | for (int i = 0; i < nTests; ++i) { | ||||
t.start(); | t.start(); | ||||
long n = maxNum * maxNum; | long n = maxNum * maxNum; | ||||
for (long j = n; j >= n - 100; --j) { | for (long j = n; j >= n - 100; --j) { | ||||
Timer t2 = new Timer().start(); | |||||
long[] factors = s.factor(sieve, j); | long[] factors = s.factor(sieve, j); | ||||
t2.end(); | |||||
if (j > (n - 5) || j < (n - 95)) { | if (j > (n - 5) || j < (n - 95)) { | ||||
if (j == n - 95) | |||||
if (j == n - 96) | |||||
System.out.println("............."); | System.out.println("............."); | ||||
System.out.println( | System.out.println( | ||||
t2.prettyTime()+" "+ | |||||
Util.factors(j, factors)); | Util.factors(j, factors)); | ||||
} | } | ||||
} | } | ||||
t.end(); | t.end(); | ||||
System.out.println("Test "+i+": "+t.prettyTime()); | |||||
} | } | ||||
return Timer.median(timers); | return Timer.median(timers); | ||||
Sequential sseq = new Sequential(); | Sequential sseq = new Sequential(); | ||||
Parallel spar = new Parallel(); | Parallel spar = new Parallel(); | ||||
boolean findPrimes = false; | |||||
boolean findPrimes = true; | |||||
boolean factor = true; | boolean factor = true; | ||||
int maxNum = Integer.parseInt(args[0]); | int maxNum = Integer.parseInt(args[0]); | ||||
if (findPrimes) { | if (findPrimes) { | ||||
System.out.println("\nFinding primes sequentially..."); | System.out.println("\nFinding primes sequentially..."); | ||||
Timer seq = testFindPrimes(sseq, maxNum); | Timer seq = testFindPrimes(sseq, maxNum); | ||||
System.out.println("Sequential median: "+seq.prettyTime()); | |||||
System.out.println("Sequential: "+seq.prettyTime()); | |||||
System.out.println("\nFinding primes in parallel..."); | System.out.println("\nFinding primes in parallel..."); | ||||
Timer par = testFindPrimes(sseq, maxNum); | |||||
System.out.println("Parallel median: "+par.prettySpeedup(seq)); | |||||
Timer par = testFindPrimes(spar, maxNum); | |||||
System.out.println("Parallel: "+par.prettySpeedup(seq)); | |||||
} | } | ||||
if (factor) { | if (factor) { | ||||
Sieve sieve = new Sieve(maxNum); | Sieve sieve = new Sieve(maxNum); | ||||
spar.solve(sieve); | spar.solve(sieve); | ||||
System.out.println("\nFactorizing sequentially..."); | |||||
System.out.println("\nFactoring sequentially..."); | |||||
Timer seq = testFactor(sseq, maxNum, sieve); | Timer seq = testFactor(sseq, maxNum, sieve); | ||||
System.out.println("Sequential median: "+seq.prettyTime()); | |||||
System.out.println("Sequential: "+seq.prettyTime()); | |||||
System.out.println("\nFactorizing in parallel..."); | |||||
System.out.println("\nFactoring in parallel..."); | |||||
Timer par = testFactor(spar, maxNum, sieve); | Timer par = testFactor(spar, maxNum, sieve); | ||||
System.out.println("Parallel median: "+par.prettySpeedup(seq)); | |||||
System.out.println("Parallel: "+par.prettySpeedup(seq)); | |||||
} | } | ||||
spar.stopThreads(); | spar.stopThreads(); |
FactorMonitor monitor; | FactorMonitor monitor; | ||||
int step; | int step; | ||||
int offset; | int offset; | ||||
int num; | |||||
long num; | |||||
long currentPrime; | |||||
long biggestPossible; | |||||
Sieve sieve; | Sieve sieve; | ||||
boolean ready = false; | boolean ready = false; | ||||
boolean done = false; | boolean done = false; | ||||
// Offset should start at 0 | |||||
synchronized public void start(FactorMonitor monitor, int step, int offset) { | synchronized public void start(FactorMonitor monitor, int step, int offset) { | ||||
this.monitor = monitor; | this.monitor = monitor; | ||||
this.step = step; | this.step = step; | ||||
this.offset = offset; | this.offset = offset; | ||||
this.num = monitor.currentNum; | this.num = monitor.currentNum; | ||||
this.biggestPossible = (long)Math.sqrt(num); | |||||
this.sieve = monitor.sieve; | this.sieve = monitor.sieve; | ||||
ready = true; | |||||
notify(); | notify(); | ||||
this.ready = true; | |||||
this.done = false; | |||||
findFirstPrime(); | |||||
} | |||||
void findFirstPrime() { | |||||
currentPrime = 3; | |||||
for (int i = 0; i < offset; ++i) { | |||||
currentPrime += 2; | |||||
while (!monitor.isPrime(currentPrime)) | |||||
currentPrime += 2; | |||||
} | |||||
} | } | ||||
synchronized public void stop() { | synchronized public void stop() { | ||||
notify(); | notify(); | ||||
} | } | ||||
void nextPrime() { | |||||
if (currentPrime > biggestPossible) | |||||
return; | |||||
for (int i = 0; i < step; ++i) { | |||||
currentPrime += 2; | |||||
while (!monitor.isPrime(currentPrime) && currentPrime <= biggestPossible) | |||||
currentPrime += 2; | |||||
if (currentPrime > biggestPossible) { | |||||
this.ready = false; | |||||
monitor.foundBiggest(); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
void findFactor() { | |||||
boolean found = false; | |||||
while (!found && this.ready) { | |||||
if ((num % currentPrime) == 0) { | |||||
found = true; | |||||
if (!monitor.addFactor(currentPrime, num)) { | |||||
this.num = monitor.currentNum; | |||||
findFirstPrime(); | |||||
return; | |||||
} | |||||
} | |||||
nextPrime(); | |||||
} | |||||
} | |||||
synchronized public void run() { | synchronized public void run() { | ||||
while (!ready && !done) | while (!ready && !done) | ||||
try { wait(); } catch (InterruptedException ex) {} | try { wait(); } catch (InterruptedException ex) {} | ||||
while (!done) { | while (!done) { | ||||
while (!monitor.isDone()) { | |||||
num = monitor.currentNum; | |||||
int limit = num / 2; | |||||
for (int i = offset; i <= limit; i += (step * 2)) { | |||||
if (i == 1) | |||||
continue; | |||||
if (num % i == 0 && sieve.isPrime(i)) { | |||||
if (!monitor.addFactor(i, num)) { | |||||
this.num = monitor.currentNum; | |||||
} | |||||
} | |||||
} | |||||
while (!monitor.isDone() && this.ready) { | |||||
findFactor(); | |||||
} | } | ||||
ready = false; | ready = false; | ||||
} | } | ||||
class FactorMonitor { | class FactorMonitor { | ||||
int currentNum; | |||||
Sieve sieve; | Sieve sieve; | ||||
long original; | |||||
long currentNum; | |||||
int biggestLeft; | |||||
boolean done = false; | boolean done = false; | ||||
ArrayList<Integer> arr = new ArrayList<>(); | |||||
ArrayList<Long> arr = new ArrayList<>(); | |||||
FactorMonitor(Sieve sieve, int num) { | |||||
FactorMonitor(Sieve sieve, long num, int nThreads) { | |||||
this.sieve = sieve; | this.sieve = sieve; | ||||
this.original = num; | |||||
this.currentNum = num; | this.currentNum = num; | ||||
this.biggestLeft = nThreads; | |||||
// We don't want to have to deal with the fact that 2 is a prime | |||||
if (num % 2 == 0) | if (num % 2 == 0) | ||||
addFactor(2, num); | addFactor(2, num); | ||||
} | } | ||||
synchronized boolean addFactor(int factor, int num) { | |||||
synchronized boolean addFactor(long factor, long num) { | |||||
// If the thread is working with a stale num, | // If the thread is working with a stale num, | ||||
// just discard it and tell the thread to restart. | // just discard it and tell the thread to restart. | ||||
if (num != currentNum) { | |||||
if (num != currentNum) | |||||
return false; | return false; | ||||
} | |||||
while ((currentNum % factor) == 0) { | while ((currentNum % factor) == 0) { | ||||
currentNum /= factor; | currentNum /= factor; | ||||
Integer i = new Integer(factor); | |||||
Long i = new Long(factor); | |||||
arr.add(i); | arr.add(i); | ||||
} | } | ||||
notify(); | notify(); | ||||
return true; | return true; | ||||
} | } | ||||
synchronized void foundBiggest() { | |||||
this.biggestLeft -= 1; | |||||
if (biggestLeft == 0) { | |||||
done = true; | |||||
notify(); | |||||
} | |||||
} | |||||
boolean isPrime(long num) { | |||||
if (num > sieve.maxNum) | |||||
return false; | |||||
else | |||||
return sieve.isPrime((int)num); | |||||
} | |||||
synchronized boolean isDone() { | synchronized boolean isDone() { | ||||
if (done) { | if (done) { | ||||
return true; | return true; | ||||
} else if (sieve.isPrime(currentNum)) { | |||||
} else if (isPrime(currentNum)) { | |||||
done = true; | done = true; | ||||
if (currentNum != 1) | if (currentNum != 1) | ||||
arr.add(new Integer(currentNum)); | |||||
else | |||||
notify(); | |||||
arr.add(new Long(currentNum)); | |||||
notify(); | |||||
return true; | return true; | ||||
} else { | } else { | ||||
return false; | return false; | ||||
while (!isDone()) | while (!isDone()) | ||||
try { wait(); } catch (InterruptedException ex) {} | try { wait(); } catch (InterruptedException ex) {} | ||||
Long curr = new Long(currentNum); | |||||
if (currentNum > (long)Math.sqrt(original) && !arr.contains(curr)) | |||||
arr.add(curr); | |||||
long[] a = new long[arr.size()]; | long[] a = new long[arr.size()]; | ||||
for (int i = 0; i < a.length; ++i) { | for (int i = 0; i < a.length; ++i) { | ||||
a[i] = arr.get(i); | a[i] = arr.get(i); | ||||
FactorWorker[] factorWorkers = null; | FactorWorker[] factorWorkers = null; | ||||
Thread[] factorThreadPool = null; | Thread[] factorThreadPool = null; | ||||
public long[] factor(Sieve sieve, long lnum) { | |||||
int num = (int)lnum; | |||||
public long[] factor(Sieve sieve, long num) { | |||||
int nThreads = Runtime.getRuntime().availableProcessors(); | int nThreads = Runtime.getRuntime().availableProcessors(); | ||||
nThreads = Math.min(nThreads, 8); | nThreads = Math.min(nThreads, 8); | ||||
if (sieve.isPrime(num)) | |||||
if (num <= sieve.maxNum && sieve.isPrime((int)num)) | |||||
return new long[] { num }; | return new long[] { num }; | ||||
// Initiate thread pool | // Initiate thread pool | ||||
} | } | ||||
} | } | ||||
FactorMonitor monitor = new FactorMonitor(sieve, num); | |||||
FactorMonitor monitor = new FactorMonitor(sieve, num, nThreads); | |||||
// Start workers | // Start workers | ||||
for (int i = 0; i < nThreads; ++i) { | for (int i = 0; i < nThreads; ++i) { | ||||
FactorWorker w = factorWorkers[i]; | FactorWorker w = factorWorkers[i]; | ||||
w.start(monitor, nThreads, (i * 2) + 1); | |||||
w.start(monitor, nThreads, i); | |||||
} | } | ||||
long[] arr = monitor.getFactors(); | long[] arr = monitor.getFactors(); |
if (num < sieve.maxNum && sieve.isPrime((int)num)) | if (num < sieve.maxNum && sieve.isPrime((int)num)) | ||||
return new long[] { num }; | return new long[] { num }; | ||||
int sqrt = (int)Math.sqrt(num); | |||||
ArrayList<Long> arr = new ArrayList<>(); | ArrayList<Long> arr = new ArrayList<>(); | ||||
while (true) { | while (true) { | ||||
for (long i = 2; true; ++i) { | |||||
if (num % i == 0) { | |||||
arr.add(new Long(i)); | |||||
num /= i; | |||||
long prime; | |||||
for (prime = 2; prime <= sqrt; ++prime) { | |||||
if (num % prime == 0) { | |||||
arr.add(new Long(prime)); | |||||
num /= prime; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (sieve.isPrime((int)num)) { | |||||
if (prime > sqrt) { | |||||
arr.add(new Long(num)); | arr.add(new Long(num)); | ||||
break; | break; | ||||
} | } | ||||
if (num <= sqrt && sieve.isPrime((int)num)) { | |||||
if (num != 1) | |||||
arr.add(new Long(num)); | |||||
break; | |||||
} | |||||
} | } | ||||
long[] a = new long[arr.size()]; | long[] a = new long[arr.size()]; |
String desc; | String desc; | ||||
{ desc = "Sequential and Parallel solvers produce the same primes"; | { desc = "Sequential and Parallel solvers produce the same primes"; | ||||
int maxNum = 100000; | |||||
int maxNum = 200000; | |||||
Sieve sieve1 = new Sieve(maxNum); | Sieve sieve1 = new Sieve(maxNum); | ||||
Sieve sieve2 = new Sieve(maxNum); | Sieve sieve2 = new Sieve(maxNum); | ||||
} | } | ||||
{ desc = "Sequential and parallel produces the same factors"; | { desc = "Sequential and parallel produces the same factors"; | ||||
int maxNum = 1000; | |||||
int maxNum = 10000000; | |||||
Sieve sieve = new Sieve(maxNum); | Sieve sieve = new Sieve(maxNum); | ||||
Sequential s1 = new Sequential(); | Sequential s1 = new Sequential(); | ||||
s2.solve(sieve); | s2.solve(sieve); | ||||
boolean failed = false; | boolean failed = false; | ||||
for (long i = 900; i < maxNum; ++i) { | |||||
long num = maxNum * maxNum; | |||||
for (long i = num - 100; i < num; ++i) { | |||||
long[] arr1 = s1.factor(sieve, i); | long[] arr1 = s1.factor(sieve, i); | ||||
long[] arr2 = s2.factor(sieve, i); | long[] arr2 = s2.factor(sieve, i); | ||||
elli ~/tmp $ java Main 2000000000 | |||||
Max prime: 2000000000 | |||||
Finding primes sequentially... | |||||
Sequential: 9.83s | |||||
Finding primes in parallel... | |||||
Parallel: 6.92s (1.42x speedup) | |||||
Factoring sequentially... | |||||
4000000000000000000 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 | |||||
3999999999999999999 = 3 * 31 * 64516129 * 666666667 | |||||
3999999999999999998 = 2 * 432809599 * 4620969601 | |||||
3999999999999999997 = 421 * 9501187648456057 | |||||
3999999999999999996 = 2 * 2 * 3 * 3 * 3 * 3 * 7 * 11 * 13 * 19 * 37 * 52579 * 333667 | |||||
............. | |||||
3999999999999999904 = 2 * 2 * 2 * 2 * 2 * 1061 * 117813383600377 | |||||
3999999999999999903 = 3 * 101 * 241 * 54777261958561 | |||||
3999999999999999902 = 2 * 49965473 * 40027640687 | |||||
3999999999999999901 = 19 * 2897 * 72670457642207 | |||||
3999999999999999900 = 2 * 2 * 3 * 5 * 5 * 89 * 1447 * 1553 * 66666667 | |||||
Sequential: 1176.19s | |||||
Factoring in parallel... | |||||
4000000000000000000 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 | |||||
3999999999999999999 = 3 * 31 * 64516129 * 666666667 | |||||
3999999999999999998 = 2 * 432809599 * 4620969601 | |||||
3999999999999999997 = 421 * 9501187648456057 | |||||
3999999999999999996 = 2 * 2 * 3 * 3 * 3 * 3 * 7 * 11 * 13 * 19 * 37 * 52579 * 333667 | |||||
............. | |||||
3999999999999999904 = 2 * 2 * 2 * 2 * 2 * 1061 * 117813383600377 | |||||
3999999999999999903 = 3 * 101 * 241 * 54777261958561 | |||||
3999999999999999902 = 2 * 49965473 * 40027640687 | |||||
3999999999999999901 = 19 * 2897 * 72670457642207 | |||||
3999999999999999900 = 2 * 2 * 3 * 5 * 5 * 89 * 1447 * 1553 * 66666667 | |||||
Parallel: 356.50s (3.30x speedup) | |||||
Kompilering: `javac *.java` | |||||
Kjøring: `java Main <tall>` | |||||
# Eksempelutskrift | |||||
I filen output.txt finner du et eksempel på å kjøre `java Main 2000000000`. | |||||
# Hvordan jeg har parallellisert | |||||
## Sil | |||||
Silen har jeg parallellisert ved å først generere de sqrt(n) første | |||||
primtallene sekvensielt, og så lager jeg n tråder som hver får en kopi av | |||||
det arrayet som har løst de sqrt(n) første tallene. Hver tråd krysser av | |||||
en del av tallene som ikke primtall. Når alle trådene er ferdige, slår jeg | |||||
sammen arrayene til de forskjellige trådene ved å ORe hver byte i hvert array, | |||||
slik at bare de tallene som ingen har markert som ikke primtall blir igjen. | |||||
Jeg bruker OR istedenfor AND fordi jeg lar 0 bety at tallet er primtall og | |||||
1 bety at det ikke er primtall, fordi det gjør at jeg slipper å gå igjennom | |||||
hele arrayet og sette hver byte til 11111111 når jeg oppretter silen, | |||||
siden et array av tall i Java starter som et array av 0. | |||||
## Faktorisering | |||||
Hver tråd får en del av primtallene opp til sqrt(n). Tråden går igjennom disse | |||||
primtallene, og når den finner et primtall som n kan deles på, gir den det | |||||
tallet til en monitor, som legger det til i en liste over faktorer og deler n | |||||
på primtallet. Alle trådene må deretter oppdatere hvilket tall de jobber med | |||||
og begynne på nytt. Når trådene har kommet til et tall som er større enn | |||||
sqrt(n) uten å finne faktorer, stopper tråden. Når alle trådene har stoppet, | |||||
vet monitoren at den er ferdig. Hvis tallet monitoren har kommet frem til så | |||||
langt etter å ha latt trådene faktorisere, er større enn sqrt(n), legges dette | |||||
tallet til listen faktorer, siden vi da vet at det er en faktor. | |||||
Måten primtallene fordeles på, er at hver tråd tar hvert n-te primtall, hvor n | |||||
er antall tråder, og hver tråd gis en unik startposisjon. Hvis det for eksempel | |||||
er 2 tråder, vil tråd 1 ta primtall 1(3), 3(7), 5(13), etc, og tråd 2 vil ta | |||||
primtall 2(5), 4(11), 6(17), etc. (I trådene later jeg som at alle primtall er | |||||
oddetall og at 3 er det første primtallet, og lar monitoren ta seg av 2, fordi | |||||
det gjør ting mye lettere.) | |||||
Legg også merke til at jeg gjenbruker trådene mine, fordi det hadde tatt en del | |||||
tid å lage 400 tråder, 4 for hver faktorisering. | |||||
Noe jeg kunne ha gjort, som ville ha gjort koden min mye raskere, | |||||
er å lage et array av primtall, istedenfor å bare gå igjennom oddetall og | |||||
spørre silen om det er et primtall. Dette kom jeg på for sent til å kunne | |||||
prøve det ut. | |||||
# Tider | |||||
CPU: quad core Intel Xeon E31225 @ 3.1 GHz (ingen hyperthreading) | |||||
2 000 000 000: | |||||
* Erastothenes' Sil sekvensielt: 9.82s | |||||
* Erastothenes' Sil parallelt: 6.77s (1.45x speedup) | |||||
* Faktorisering sekvensielt: 1176.19s | |||||
* Faktorisering parallelt: 356.50s (3.3x speedup) | |||||
200 000 000: | |||||
* Erastothenes' Sil sekvensielt: 829.98ms | |||||
* Erastothenes' Sil parallelt: 618.72ms (1.34x speedup) | |||||
* Faktorisering sekvensielt: 115.10s | |||||
* Faktorisering parallelt: 37.20s (3.09x speedup) | |||||
20 000 000: | |||||
* Erastothenes' Sil sekvensielt: 68.11ms | |||||
* Erastothenes' Sil parallelt: 41.41ms (1.64x speedup) | |||||
* Faktorisering sekvensielt: 13.82s | |||||
* Faktorisering parallelt: 4.31s (3.21x speedup) | |||||
2 000 000: | |||||
* Erastothenes' Sil sekvensielt: 15.67ms | |||||
* Erastothenes' Sil parallelt: 9.58ms (1.64x speedup) | |||||
* Faktorisering sekvensielt: 1.17s | |||||
* Faktorisering parallelt: 534.75s (2.18x speedup) |