ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/TSPExchangerTest.java
Revision: 1.1
Committed: Sun Aug 7 19:25:55 2005 UTC (18 years, 9 months ago) by dl
Branch: MAIN
Log Message:
Add exchanger performance tests; update others

File Contents

# User Rev Content
1 dl 1.1 /*
2     * Written by Bill Scherer and Doug Lea with assistance from members
3     * of JCP JSR-166 Expert Group and released to the public domain. Use,
4     * modify, and redistribute this code in any way without
5     * acknowledgement.
6     */
7    
8     import java.util.concurrent.*;
9     import java.util.concurrent.atomic.*;
10     import java.util.concurrent.locks.*;
11    
12     public class TSPExchangerTest {
13     // Set SLS true to use as default the settings in Scherer, Lea, and
14     // Scott paper. Otherwise smaller values are used to speed up testing
15     static final boolean SLS = false;
16    
17     static final int DEFAULT_THREADS = SLS? 32: 8;
18     static final int DEFAULT_CITIES = SLS? 100: 50;
19     static final int DEFAULT_POPULATION = SLS? 1000: 500;
20     static final int DEFAULT_BREEDERS = SLS? 200: 100;
21     static final int DEFAULT_GENERATIONS = SLS? 20000: 10000;
22    
23    
24     public static void main(String[] args) throws Exception {
25     int maxThreads = DEFAULT_THREADS;
26     int nCities = DEFAULT_CITIES;
27     int pSize = DEFAULT_POPULATION;
28     int nBreeders = DEFAULT_BREEDERS;
29     int numGenerations = DEFAULT_GENERATIONS;
30    
31     // Parse and check args
32     int argc = 0;
33     try {
34     while (argc < args.length) {
35     String option = args[argc++];
36     if (option.equals("-b"))
37     nBreeders = Integer.parseInt(args[argc]);
38     else if (option.equals("-c"))
39     nCities = Integer.parseInt(args[argc]);
40     else if (option.equals("-g"))
41     numGenerations = Integer.parseInt(args[argc]);
42     else if (option.equals("-p"))
43     pSize = Integer.parseInt(args[argc]);
44     else
45     maxThreads = Integer.parseInt(option);
46     argc++;
47     }
48     }
49     catch (NumberFormatException e) {
50     reportUsageErrorAndDie();
51     System.exit(0);
52     }
53     catch (Exception e) {
54     reportUsageErrorAndDie();
55     }
56    
57     // Display runtime parameters
58     System.out.print("TSPExchangerTest -b " + nBreeders);
59     System.out.print(" -c " + nCities);
60     System.out.print(" -g " + numGenerations);
61     System.out.print(" -p " + pSize);
62     System.out.print(" max threads " + maxThreads);
63     System.out.println();
64    
65     // warmup
66     System.out.print("Threads: " + 2 + "\t");
67     oneRun(2,
68     nCities,
69     pSize,
70     nBreeders,
71     numGenerations);
72     Thread.sleep(100);
73    
74     int k = 4;
75     for (int i = 2; i <= maxThreads;) {
76     System.out.print("Threads: " + i + "\t");
77     oneRun(i,
78     nCities,
79     pSize,
80     nBreeders,
81     numGenerations);
82     Thread.sleep(100);
83     if (i == k) {
84     k = i << 1;
85     i = i + (i >>> 1);
86     }
87     else
88     i = k;
89     }
90     }
91    
92     private static void reportUsageErrorAndDie() {
93     System.out.print("usage: TSPExchangerTest [-b #breeders] [-c #cities]");
94     System.out.println(" [-g #generations]");
95     System.out.println(" [-p population size] [ #threads]");
96     System.exit(0);
97     }
98    
99     static void oneRun(int nThreads,
100     int nCities,
101     int pSize,
102     int nBreeders,
103     int numGenerations)
104     throws Exception {
105     CyclicBarrier runBarrier = new CyclicBarrier(nThreads + 1);
106     Population p = new Population(nCities, pSize, nBreeders, nThreads,
107     numGenerations, runBarrier);
108    
109     // Run the test
110     long startTime = System.currentTimeMillis();
111     runBarrier.await(); // start 'em off
112     runBarrier.await(); // wait 'til they're done
113     long stopTime = System.currentTimeMillis();
114     long elapsed = stopTime - startTime;
115     long rate = (numGenerations * 1000) / elapsed;
116     double secs = (double)elapsed / 1000.0;
117    
118     // Display results
119     System.out.print(LoopHelpers.rightJustify((int)p.bestFitness()) +
120     " fitness");
121     System.out.print(LoopHelpers.rightJustify(rate) + " gen/s \t");
122     System.out.print(secs + "s elapsed");
123     System.out.println();
124     }
125    
126     static final class Population {
127     final Chromosome[] individuals;
128     final Exchanger<Chromosome> x;
129     final CitySet cities;
130     final int[] dyers;
131     final int[] breeders;
132     final CyclicBarrier generationBarrier;
133     final Thread[] threads;
134     final boolean[] doneMating;
135     final ReentrantLock matingBarrierLock;
136     final Condition matingBarrier;
137     final LoopHelpers.SimpleRandom[] rngs;
138     final int nThreads;
139     volatile int matingBarrierCount;
140    
141     // action to run between each generation
142     class BarrierAction implements Runnable {
143     public void run() {
144     prepareToBreed();
145     resetMatingBarrier();
146     }
147     }
148    
149     Population(int nCities,
150     int pSize,
151     int nBreeders,
152     int nThreads,
153     int nGen,
154     CyclicBarrier runBarrier) {
155     this.nThreads = nThreads;
156     // rngs[nThreads] is for global actions; others are per-thread
157     this.rngs = new LoopHelpers.SimpleRandom[nThreads+1];
158     for (int i = 0; i < rngs.length; ++i)
159     rngs[i] = new LoopHelpers.SimpleRandom();
160     this.cities = new CitySet(nCities, rngs[nThreads]);
161     this.individuals = new Chromosome[pSize];
162     for (int i = 0; i < individuals.length; i++)
163     individuals[i] = new Chromosome(cities, nCities,
164     rngs[nThreads]);
165     this.doneMating = new boolean[nThreads];
166     this.dyers = new int[nBreeders];
167     this.breeders = new int[nBreeders];
168    
169     this.x = new Exchanger();
170    
171     this.matingBarrierLock = new ReentrantLock();
172     this.matingBarrier = matingBarrierLock.newCondition();
173    
174     BarrierAction ba = new BarrierAction();
175     this.generationBarrier = new CyclicBarrier(nThreads, ba);
176     ba.run(); // prepare for first generation
177    
178     this.threads = new Thread[nThreads];
179     for (int i = 0; i < nThreads; i++) {
180     Runner r = new Runner(i, this, runBarrier, nGen);
181     threads[i] = new Thread(r);
182     threads[i].start();
183     }
184     }
185    
186     double averageFitness() {
187     double total = 0;
188     for (int i = 0; i < individuals.length; i++)
189     total += individuals[i].fitness;
190     return total/(double)individuals.length;
191     }
192    
193     double bestFitness() {
194     double best = individuals[0].fitness;
195     for (int i = 0; i < individuals.length; i++)
196     if (individuals[i].fitness < best)
197     best = individuals[i].fitness;
198     return best;
199     }
200    
201     void resetMatingBarrier() {
202     matingBarrierCount = nThreads - 1;
203     }
204    
205     void awaitMatingBarrier(int tid) {
206     doneMating[tid] = true; // heuristically set before lock
207     matingBarrierLock.lock();
208     try {
209     int m = matingBarrierCount--;
210     if (m < 1) {
211     for (int i = 0; i < doneMating.length; ++i)
212     doneMating[i] = false;
213     Thread.interrupted(); // clear
214     matingBarrier.signalAll();
215     } else {
216     doneMating[tid] = true;
217     if (m == 1 && nThreads > 2) {
218     for (int j = 0; j < doneMating.length; ++j) {
219     if (!doneMating[j]) {
220     threads[j].interrupt();
221     break;
222     }
223     }
224     }
225     try {
226     do {
227     matingBarrier.await();
228     } while (matingBarrierCount >= 0);
229     } catch(InterruptedException ie) {}
230     }
231     } finally {
232     matingBarrierLock.unlock();
233     }
234     }
235    
236     void prepareToBreed() {
237    
238     // Calculate statistics
239     double totalFitness = 0;
240     double worstFitness = 0;
241     double bestFitness = individuals[0].fitness;
242    
243     for (int i = 0; i < individuals.length; i++) {
244     totalFitness += individuals[i].fitness;
245     if (individuals[i].fitness > worstFitness)
246     worstFitness = individuals[i].fitness;
247     if (individuals[i].fitness < bestFitness)
248     bestFitness = individuals[i].fitness;
249     }
250    
251     double[] lifeNorm = new double[individuals.length];
252     double lifeNormTotal = 0;
253     double[] deathNorm = new double[individuals.length];
254     double deathNormTotal = 0;
255     for (int i = 0; i < individuals.length; i++) {
256     deathNorm[i] = (individuals[i].fitness - bestFitness)
257     / (worstFitness - bestFitness + 1) + .05;
258     deathNorm[i] = (deathNorm[i] * deathNorm[i]);
259     lifeNorm[i] = 1.0 - deathNorm[i];
260     lifeNormTotal += lifeNorm[i];
261     deathNormTotal += deathNorm[i];
262     }
263    
264     double deathScale = deathNormTotal / (double)0x7FFFFFFF;
265     double lifeScale = lifeNormTotal / (double)0x7FFFFFFF;
266    
267     int nSub = breeders.length / nThreads;
268     LoopHelpers.SimpleRandom random = rngs[nThreads];
269    
270     // Select breeders (need to be distinct)
271     for (int i = 0; i < nSub; i++) {
272     boolean newBreeder;
273     int lucky;
274     do {
275     newBreeder = true;
276     double choice = lifeScale * (double)random.next();
277     for (lucky = 0; lucky < individuals.length; lucky++) {
278     choice -= lifeNorm[lucky];
279     if (choice <= 0)
280     break;
281     }
282     for (int j = 0; j < i; j++)
283     if (breeders[j] == lucky)
284     newBreeder = false;
285     } while (!newBreeder);
286     breeders[i] = lucky;
287     }
288    
289     // Select dead guys (need to be distinct)
290     for (int i = 0; i < nSub; i++) {
291     boolean newDead;
292     int victim;
293     do {
294     newDead = true;
295     double choice = deathScale * (double)random.next();
296     for (victim = 0; victim < individuals.length; victim++) {
297     choice -= deathNorm[victim];
298     if (choice <= 0)
299     break;
300     }
301     for (int j = 0; j < i; j++)
302     if (dyers[j] == victim)
303     newDead = false;
304     } while (!newDead);
305     dyers[i] = victim;
306     }
307    
308     }
309    
310    
311     void nextGeneration(int tid, int matings)
312     throws InterruptedException, BrokenBarrierException {
313    
314     int firstChild = ((individuals.length * tid) / nThreads);
315     int lastChild = ((individuals.length * (tid + 1)) / nThreads);
316     int nChildren = lastChild - firstChild;
317    
318     int firstSub = ((breeders.length * tid) / nThreads);
319     int lastSub = ((breeders.length * (tid + 1)) / nThreads);
320     int nSub = lastSub - firstSub;
321    
322     Chromosome[] children = new Chromosome[nChildren];
323    
324     LoopHelpers.SimpleRandom random = rngs[tid];
325    
326     for (int i = 0; i < nSub; i++) {
327     Chromosome parent = individuals[breeders[firstSub + i]];
328     Chromosome offspring = new Chromosome(parent);
329     int k = 0;
330     while (k < matings && matingBarrierCount > 0) {
331     try {
332     Chromosome other = x.exchange(offspring);
333     offspring = offspring.reproduceWith(other, random);
334     ++k;
335     } catch (InterruptedException to) {
336     break;
337     }
338     }
339     if (k != 0)
340     children[i] = offspring;
341     else {
342     // No peers, so we mate with ourselves
343     for ( ; i < nSub - 1; i += 2) {
344     int cur = firstSub + i;
345     Chromosome bro = individuals[breeders[cur]];
346     Chromosome sis = individuals[breeders[cur + 1]];
347    
348     children[i] = bro.breedWith(sis, matings, random);
349     children[i+1] = sis.breedWith(bro, matings, random);
350     }
351    
352     // Not even a sibling, so we go to asexual reproduction
353     if (i < nSub)
354     children[i] = individuals[breeders[firstSub + 1]];
355     break;
356     }
357    
358     }
359    
360     awaitMatingBarrier(tid);
361    
362     // Kill off dead guys
363     for (int i = 0; i < nSub; i++) {
364     individuals[dyers[firstSub + 1]] = children[i];
365     }
366    
367     generationBarrier.await();
368     }
369     }
370    
371     static final class Chromosome {
372     private final CitySet cities;
373     private final int[] alleles;
374     private final int length;
375     public double fitness; // immutable after publication
376    
377     // Basic constructor - gets randomized genetic code
378     Chromosome(CitySet cities, int length,
379     LoopHelpers.SimpleRandom random) {
380     this.length = length;
381     this.cities = cities;
382     // Initialize alleles to a random shuffle
383     alleles = new int[length];
384     for (int i = 0; i < length; i++)
385     alleles[i] = i;
386     for (int i = length - 1; i > 0; i--) {
387     int tmp = alleles[i];
388     int idx = random.next() % length;
389     alleles[i] = alleles[idx];
390     alleles[idx] = tmp;
391     }
392     recalcFitness();
393     }
394    
395     // Copy constructor - clones parent's genetic code
396     Chromosome(Chromosome clone) {
397     length = clone.length;
398     cities = clone.cities;
399     fitness = clone.fitness;
400     alleles = new int[length];
401     System.arraycopy(clone.alleles, 0, alleles, 0, length);
402     }
403    
404     int getAllele(int offset) {
405     return alleles[offset % length];
406     }
407     void setAllele(int offset, int v) {
408     alleles[offset % length] = v;
409     }
410    
411     void recalcFitness() {
412     fitness = cities.distanceBetween(alleles[0], alleles[length-1]);
413     for (int i = 1; i < length; i++) {
414     fitness += cities.distanceBetween(alleles[i-1], alleles[i]);
415     }
416     }
417    
418     Chromosome breedWith(Chromosome partner, int n,
419     LoopHelpers.SimpleRandom random) {
420     Chromosome offspring = new Chromosome(this);
421     for (int i = 0; i < n; i++)
422     offspring = offspring.reproduceWith(partner, random);
423     return offspring;
424     }
425    
426     Chromosome reproduceWith(Chromosome other,
427     LoopHelpers.SimpleRandom random) {
428     Chromosome child = new Chromosome(this);
429     int coStart = random.next() % length;
430     int coLen = 3;
431     while (1 == (random.next() & 1) && (coLen < length))
432     coLen++;
433     int cPos, pPos;
434    
435     int join = other.getAllele(coStart);
436     child.alleles[0] = join;
437    
438     for (pPos = 0; alleles[pPos] != join; pPos++)
439     ;
440    
441     for (cPos = 1; cPos < coLen; cPos++)
442     child.setAllele(cPos, other.getAllele(coStart + cPos));
443    
444     for (int i = 0; i < length; i++) {
445     boolean found = false;
446     int allele = getAllele(pPos++);
447     for (int j = 0; j < coLen; j++) {
448     if (found = (child.getAllele(j) == allele))
449     break;
450     }
451     if (!found)
452     child.setAllele(cPos++, allele);
453     }
454    
455     child.recalcFitness();
456     return child;
457     }
458    
459     }
460     /**
461     * A collection of (x,y) points that represent cities
462     */
463     static final class CitySet {
464     final int XMAX = 1000;
465     final int YMAX = 1000;
466     final int length;
467     final int xPts[];
468     final int yPts[];
469     final double distances[];
470    
471     CitySet(int n, LoopHelpers.SimpleRandom random) {
472     this.length = n;
473     xPts = new int[n];
474     yPts = new int [n];
475     for (int i = 0; i < n; i++) {
476     xPts[i] = random.next() % XMAX;
477     yPts[i] = random.next() % YMAX;
478     }
479     distances = new double[n * n];
480     for (int i = 0; i < n; i++) {
481     for (int j = 0; j < n; j++) {
482     double dX = (double)(xPts[i] - xPts[j]);
483     double dY = (double)(yPts[i] - yPts[j]);
484     distances[i + j * n] = Math.hypot(dX, dY);
485     }
486     }
487     }
488    
489     // Retrieve the cached distance between a pair of cities
490     double distanceBetween(int idx1, int idx2) {
491     return distances[idx1 + idx2 * length];
492     }
493     }
494    
495     static final class Runner implements Runnable {
496     final Population pop;
497     final CyclicBarrier b;
498     final int nGen;
499     final int tid;
500     static final boolean verbose = false;
501    
502     Runner(int tid, Population p, CyclicBarrier b, int n) {
503     this.tid = tid;
504     this.pop = p;
505     this.b = b;
506     this.nGen = n;
507     }
508    
509     public void run() {
510     try {
511     b.await();
512     for (int i = 0; i < nGen; i++) {
513     if (verbose && 0 == tid && 0 == i % 1000) {
514     System.out.print("Gen " + i + " fitness:");
515     System.out.print(" best=" + (int)pop.bestFitness());
516     System.out.println("; avg=" + (int)pop.averageFitness());
517     }
518     int matings = (((nGen - i) * 2) / (nGen)) + 1;
519     pop.nextGeneration(tid, matings);
520     }
521     b.await();
522     }
523     catch (InterruptedException e) {
524     e.printStackTrace(System.out);
525     System.exit(0);
526     }
527     catch (BrokenBarrierException e) {
528     e.printStackTrace(System.out);
529     System.exit(0);
530     }
531     }
532     }
533     }