@@ -12,13 +12,12 @@ class Main { | |||
t.start(); | |||
s.solve(sieve); | |||
t.end(); | |||
System.out.println("Test "+i+": "+t.prettyTime()); | |||
} | |||
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]; | |||
for (int i = 0; i < nTests; ++i) { | |||
@@ -26,20 +25,17 @@ class Main { | |||
t.start(); | |||
long n = maxNum * maxNum; | |||
for (long j = n; j >= n - 100; --j) { | |||
Timer t2 = new Timer().start(); | |||
long[] factors = s.factor(sieve, j); | |||
t2.end(); | |||
if (j > (n - 5) || j < (n - 95)) { | |||
if (j == n - 95) | |||
if (j == n - 96) | |||
System.out.println("............."); | |||
System.out.println( | |||
t2.prettyTime()+" "+ | |||
Util.factors(j, factors)); | |||
} | |||
} | |||
t.end(); | |||
System.out.println("Test "+i+": "+t.prettyTime()); | |||
} | |||
return Timer.median(timers); | |||
@@ -54,7 +50,7 @@ class Main { | |||
Sequential sseq = new Sequential(); | |||
Parallel spar = new Parallel(); | |||
boolean findPrimes = false; | |||
boolean findPrimes = true; | |||
boolean factor = true; | |||
int maxNum = Integer.parseInt(args[0]); | |||
@@ -63,11 +59,11 @@ class Main { | |||
if (findPrimes) { | |||
System.out.println("\nFinding primes sequentially..."); | |||
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..."); | |||
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) { | |||
@@ -75,13 +71,13 @@ class Main { | |||
Sieve sieve = new Sieve(maxNum); | |||
spar.solve(sieve); | |||
System.out.println("\nFactorizing sequentially..."); | |||
System.out.println("\nFactoring sequentially..."); | |||
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); | |||
System.out.println("Parallel median: "+par.prettySpeedup(seq)); | |||
System.out.println("Parallel: "+par.prettySpeedup(seq)); | |||
} | |||
spar.stopThreads(); |
@@ -60,19 +60,36 @@ class Parallel implements Solver { | |||
FactorMonitor monitor; | |||
int step; | |||
int offset; | |||
int num; | |||
long num; | |||
long currentPrime; | |||
long biggestPossible; | |||
Sieve sieve; | |||
boolean ready = false; | |||
boolean done = false; | |||
// Offset should start at 0 | |||
synchronized public void start(FactorMonitor monitor, int step, int offset) { | |||
this.monitor = monitor; | |||
this.step = step; | |||
this.offset = offset; | |||
this.num = monitor.currentNum; | |||
this.biggestPossible = (long)Math.sqrt(num); | |||
this.sieve = monitor.sieve; | |||
ready = true; | |||
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() { | |||
@@ -81,24 +98,46 @@ class Parallel implements Solver { | |||
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() { | |||
while (!ready && !done) | |||
try { wait(); } catch (InterruptedException ex) {} | |||
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; | |||
@@ -110,30 +149,34 @@ class Parallel implements Solver { | |||
} | |||
class FactorMonitor { | |||
int currentNum; | |||
Sieve sieve; | |||
long original; | |||
long currentNum; | |||
int biggestLeft; | |||
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.original = 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) | |||
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, | |||
// just discard it and tell the thread to restart. | |||
if (num != currentNum) { | |||
if (num != currentNum) | |||
return false; | |||
} | |||
while ((currentNum % factor) == 0) { | |||
currentNum /= factor; | |||
Integer i = new Integer(factor); | |||
Long i = new Long(factor); | |||
arr.add(i); | |||
} | |||
notify(); | |||
@@ -141,15 +184,29 @@ class Parallel implements Solver { | |||
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() { | |||
if (done) { | |||
return true; | |||
} else if (sieve.isPrime(currentNum)) { | |||
} else if (isPrime(currentNum)) { | |||
done = true; | |||
if (currentNum != 1) | |||
arr.add(new Integer(currentNum)); | |||
else | |||
notify(); | |||
arr.add(new Long(currentNum)); | |||
notify(); | |||
return true; | |||
} else { | |||
return false; | |||
@@ -160,6 +217,10 @@ class Parallel implements Solver { | |||
while (!isDone()) | |||
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()]; | |||
for (int i = 0; i < a.length; ++i) { | |||
a[i] = arr.get(i); | |||
@@ -171,12 +232,11 @@ class Parallel implements Solver { | |||
FactorWorker[] factorWorkers = 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(); | |||
nThreads = Math.min(nThreads, 8); | |||
if (sieve.isPrime(num)) | |||
if (num <= sieve.maxNum && sieve.isPrime((int)num)) | |||
return new long[] { num }; | |||
// Initiate thread pool | |||
@@ -191,12 +251,12 @@ class Parallel implements Solver { | |||
} | |||
} | |||
FactorMonitor monitor = new FactorMonitor(sieve, num); | |||
FactorMonitor monitor = new FactorMonitor(sieve, num, nThreads); | |||
// Start workers | |||
for (int i = 0; i < nThreads; ++i) { | |||
FactorWorker w = factorWorkers[i]; | |||
w.start(monitor, nThreads, (i * 2) + 1); | |||
w.start(monitor, nThreads, i); | |||
} | |||
long[] arr = monitor.getFactors(); |
@@ -9,21 +9,30 @@ class Sequential implements Solver { | |||
if (num < sieve.maxNum && sieve.isPrime((int)num)) | |||
return new long[] { num }; | |||
int sqrt = (int)Math.sqrt(num); | |||
ArrayList<Long> arr = new ArrayList<>(); | |||
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; | |||
} | |||
} | |||
if (sieve.isPrime((int)num)) { | |||
if (prime > sqrt) { | |||
arr.add(new Long(num)); | |||
break; | |||
} | |||
if (num <= sqrt && sieve.isPrime((int)num)) { | |||
if (num != 1) | |||
arr.add(new Long(num)); | |||
break; | |||
} | |||
} | |||
long[] a = new long[arr.size()]; |
@@ -5,7 +5,7 @@ class Test { | |||
String desc; | |||
{ desc = "Sequential and Parallel solvers produce the same primes"; | |||
int maxNum = 100000; | |||
int maxNum = 200000; | |||
Sieve sieve1 = new Sieve(maxNum); | |||
Sieve sieve2 = new Sieve(maxNum); | |||
@@ -36,7 +36,7 @@ class Test { | |||
} | |||
{ desc = "Sequential and parallel produces the same factors"; | |||
int maxNum = 1000; | |||
int maxNum = 10000000; | |||
Sieve sieve = new Sieve(maxNum); | |||
Sequential s1 = new Sequential(); | |||
@@ -45,7 +45,8 @@ class Test { | |||
s2.solve(sieve); | |||
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[] arr2 = s2.factor(sieve, i); | |||
@@ -0,0 +1,37 @@ | |||
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) | |||
@@ -0,0 +1,76 @@ | |||
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) |