View Javadoc

1   /*
2    * Copyright 2006-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sourceforge.domian.test.benchmark;
17  
18  
19  import java.text.SimpleDateFormat;
20  import java.util.Date;
21  import java.util.Iterator;
22  
23  import net.sourceforge.domian.repository.PartitionRepository;
24  import net.sourceforge.domian.repository.PersistentRepository;
25  import net.sourceforge.domian.repository.Repository;
26  import net.sourceforge.domian.util.StopWatch;
27  import net.sourceforge.domian.util.concurrent.task.ConcurrentRepositoryTaskExecutor;
28  import net.sourceforge.domian.util.concurrent.task.RepositoryTask;
29  
30  import static net.sourceforge.domian.specification.SpecificationFactory.all;
31  import static net.sourceforge.domian.test.benchmark.QueenPuzzleUtils.conditionalLogging;
32  import static org.apache.commons.lang.math.RandomUtils.nextInt;
33  
34  
35  /**
36   * An amusing little computing task involving (possible) vast numbers of repository inserts, updates, and search.
37   * <p/>
38   * In detail, it consists of:
39   * <ol>
40   * <li/>Generating random chess piece constellations consisting of 8 pieces, disguised as entity objects; adding them to a repository
41   * <li/>A batch processing task, iterating through all constellations, doing a little check, with a conditional update
42   * <li/>A search task for all constellations solving the queen puzzle
43   * <li/>A completion control search task for all constellations not processed (should be none)
44   * </ol>
45   * <p/>
46   * The batch processing task tries to solve the infamous "Queen Puzzle" which seeks to find all possible chess queen piece constellations
47   * where none of the queens are threatened by one of the other queens.
48   * A standard 8 x 8 chess board and 8 queen pieces are hard-coded.
49   * Possible sequences of queens are staggering 64^8 = 281.474.976.710.656 (over 280 trillions!)
50   */
51  class ConcurrentQueenPuzzle extends AbstractQueenPuzzle {
52  
53      ConcurrentQueenPuzzle(final SequentialQueenPuzzle.RepositoryType repositoryType,
54                            final long numberOfConstellations,
55                            final int numberOfWorkers,
56                            final long logInterval) {
57          super(repositoryType, numberOfConstellations, numberOfWorkers, logInterval);
58      }
59  
60  
61      void solvePuzzle() {
62          System.out.println();
63          System.out.println("Solving the \"Queen Puzzle\" concurrently... [start time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS").format(new Date()) + "]");
64          System.out.println("Processors      : " + Runtime.getRuntime().availableProcessors());
65          System.out.println("Worker threads  : " + this.numberOfWorkers);
66          System.out.println("Constellations  : " + this.numberOfConstellations + " + 1 correct one for completion control");
67          System.out.println("Repository type : " + this.repositoryType);
68          System.out.println();
69  
70          /****** The repository to torment ******/
71          final Repository<QueenPuzzleConstellation> repo = createRepository(this.repositoryType);
72          if (repo instanceof PersistentRepository) {
73              PersistentRepository persistentRepository = (PersistentRepository) repo;
74              repo.remove(all(QueenPuzzleConstellation.class));
75              if (persistentRepository.getPersistenceDefinition().supportsAsynchronousPersistence()) {
76                  persistentRepository.persist();
77              }
78          }
79  
80          final StopWatch stopWatch = new StopWatch().start();
81  
82          /* Add partition if applicable */
83          if (repo instanceof PartitionRepository) {
84              //((PartitionedRepository) repo).addPartitionFor(queenPuzzleConstellationsThatInherentlySolvesTheQueenPuzzle); // This would of course be the fastest and smartest thing to do - but kind of violates the purpose of this execution...
85              ((PartitionRepository<QueenPuzzleConstellation>) repo).addPartitionFor(queenPuzzleConstellationsThatIsProcessedAndMarkedAsToSolveTheQueenPuzzle, "approved-queenpuzzle-constellations");
86              ((PartitionRepository<QueenPuzzleConstellation>) repo).addPartitionFor(unProcessedQueenPuzzleConstellations, "unprocessed-queenpuzzle-constellations");
87          }
88  
89          /****** Adding correct constellation just to if everything seems to work... ******/
90          addOneWellKnownSuccessfulQueenPuzzleConstellationInto(repo);
91  
92          /****** The data generator(s) ******/
93          final ConcurrentRepositoryTaskExecutor concurrentTaskExecutor = new ConcurrentRepositoryTaskExecutor(repo);
94          for (int producerNumber = 0; producerNumber < this.numberOfWorkers; ++producerNumber) {
95              concurrentTaskExecutor.addTask(new RandomPermutationQueenPuzzleConstellationProducer(this.numberOfConstellations / this.numberOfWorkers,
96                                                                                                   this.logInterval));
97          }
98          System.out.println("Starting concurrent execution of " + this.numberOfWorkers + " worker threads...");
99          try {
100             concurrentTaskExecutor.execute();
101         } catch (Exception e) {
102             e.printStackTrace();
103             throw new RuntimeException(e);
104         }
105         System.out.println("Concurrent execution of worker threads terminated!");
106 
107         doPrintProcessStatistics(stopWatch.getElapsedTime(), this.numberOfConstellations);
108 
109         /****** Repartition if applicable ******/
110         /* ConcurrentPartitionedHashSetRepository has implicit repartioning when using iterators */
111         /*if (repo instanceof PartitionedHashSetRepository) {
112             doRepartition((PartitionedRepository) repo);
113         }*/
114 
115         /****** Persist if applicable ******/
116         if (repo instanceof PersistentRepository) {
117             doPersist((PersistentRepository) repo);
118         }
119 
120         /****** The result search task ******/
121         doResultSearch(repo);
122 
123         /****** The completion control task ******/
124         doCompletionControl(repo);
125 
126         System.out.println();
127         System.out.println("Overall time: " + stopWatch);
128     }
129 
130 
131     private static class RandomPermutationQueenPuzzleConstellationProducer extends RepositoryTask<Repository<QueenPuzzleConstellation>> {
132 
133         final long numberOfConstellationsToProduce;
134         final long logInterval;
135 
136         RandomPermutationQueenPuzzleConstellationProducer(final long numberOfConstellationsToProduce, final long logInterval) {
137             super();
138             this.numberOfConstellationsToProduce = numberOfConstellationsToProduce;
139             this.logInterval = logInterval;
140         }
141 
142         /** Permutation constraints; no equal elements. */
143         private boolean constellationConstraintOk(final int[] queenPlacingNumbers, final int queenPlacingIndex, final int queenPlacingNumber) {
144             for (int index = 0; index < queenPlacingIndex; ++index) {
145                 if (queenPlacingNumber == queenPlacingNumbers[index]) {
146                     return false;
147                 }
148             }
149             return true;
150         }
151 
152         public void doConcurrentTask() {
153             long processingCounter = 0;
154             long redundantProcessingCounter = 0;
155 
156             final StopWatch stopWatch = new StopWatch().start();
157 
158             /****** Generate constellations and put them in repo ******/
159             for (long workerInternalConstellationNumber = 1;
160                  workerInternalConstellationNumber <= this.numberOfConstellationsToProduce;
161                  ++workerInternalConstellationNumber) {
162                 final int[] constellation = new int[8];
163                 int index = 0;
164                 while (index < 8) {
165                     int number = nextInt(64);
166                     while (!constellationConstraintOk(constellation, index, number)) {
167                         number = nextInt(64);
168                     }
169                     constellation[index++] = number;
170                 }
171                 final QueenPuzzleConstellation queenPuzzleConstellation = new QueenPuzzleConstellation(new ChessPiecePlacing(constellation[0]),
172                                                                                                        new ChessPiecePlacing(constellation[1]),
173                                                                                                        new ChessPiecePlacing(constellation[2]),
174                                                                                                        new ChessPiecePlacing(constellation[3]),
175                                                                                                        new ChessPiecePlacing(constellation[4]),
176                                                                                                        new ChessPiecePlacing(constellation[5]),
177                                                                                                        new ChessPiecePlacing(constellation[6]),
178                                                                                                        new ChessPiecePlacing(constellation[7]));
179                 this.repository.put(queenPuzzleConstellation);
180                 conditionalLogging((long) this.threadNumber, this.logInterval, workerInternalConstellationNumber, stopWatch, queenPuzzleConstellation, "generated and added to repo so far...");
181             }
182 
183             /****** Check constellations if they solve the Queen Puzzle constraint ******/
184             final Iterator<? extends QueenPuzzleConstellation> allUnprocessedConstellations = this.repository.iterateAll(unProcessedQueenPuzzleConstellations);
185             long numberOfProcessedConstellations = 0;
186             while (allUnprocessedConstellations.hasNext()) {
187                 QueenPuzzleConstellation unProcessedQueenPuzzleConstellation = allUnprocessedConstellations.next();
188 
189                 /* Should synchronize this as the non-thread-safe QueenPuzzleConstellation instance may well be processed by another worker thread... */
190                 synchronized (unProcessedQueenPuzzleConstellation) {
191                     if (unProcessedQueenPuzzleConstellation.isProcessed()) {
192                         ++redundantProcessingCounter;
193                     } else {
194                         unProcessedQueenPuzzleConstellation.setSolvesQueenPuzzle(queenPuzzleConstellationsThatInherentlySolvesTheQueenPuzzle.isSatisfiedBy(unProcessedQueenPuzzleConstellation));
195                         this.repository.update(unProcessedQueenPuzzleConstellation);
196                         ++processingCounter;
197                     }
198                     conditionalLogging((long) this.threadNumber, logInterval, ++numberOfProcessedConstellations, stopWatch, unProcessedQueenPuzzleConstellation, "processed so far...");
199                 }
200 
201                 /* But as this is an idempotent operation, one could skip (expensive) thread synchronization and allow some redundant work to be done - it seems to be more efficient... */
202                 //unProcessedQueenPuzzleConstellation.setSolvesQueenPuzzle(queenPuzzleConstellationsThatInherentlySolvesTheQueenPuzzle.isSatisfiedBy(unProcessedQueenPuzzleConstellation));
203                 //conditionalLogging((long) this.threadNumber, logInterval, ++numberOfProcessedConstellations, stopWatch, unProcessedQueenPuzzleConstellation, "processed so far...");
204             }
205             /****** Report worker thread statistics ******/
206             System.out.println("Worker thread #" + this.threadNumber + " finished. " + processingCounter + " puzzle constellation processed by this worker. " + redundantProcessingCounter + " puzzle constellation was already processed.");
207             //System.out.println("Worker thread #" + this.threadNumber + " finished. " + numberOfProcessedConstellations + " puzzle constellation processed by this worker.");
208         }
209     }
210 }