diff --git a/simulation/Simulation.java b/simulation/Simulation.java index 0191f29..2c151c8 100644 --- a/simulation/Simulation.java +++ b/simulation/Simulation.java @@ -17,6 +17,7 @@ package simulation; import java.util.ArrayList; +import java.util.List; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -26,6 +27,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; + import starting_point.Starting_Point; import structure.Deck_Card; import structure.Evaluable; @@ -35,8 +38,7 @@ /** Purpose: final step which performs the actual simulation.
-Programmer: Gabriel Toban Harris
-Date: 2021-08-04/2021-8-[17-22] +Programmer: Gabriel Toban Harris, Alexander Oxorn
*/ @@ -251,20 +253,20 @@ public static AtomicInteger[] initialize_array(final int LENGTH) */ public static > ArrayList purify_forest(final C RAW_FOREST) { - ArrayList filtered_trees = new ArrayList(RAW_FOREST.parallelStream().filter(tree -> tree.DISPLAY).collect(Collectors.toList())); + ArrayList filtered_trees = RAW_FOREST.parallelStream().filter(tree -> tree.DISPLAY).collect(Collectors.toCollection(ArrayList::new)); filtered_trees.trimToSize(); return filtered_trees; } /** * Subroutine to draw a hand. Note, returns shallow. - * + * * @param the type of cards in the deck - * + * * @param HAND_SIZE to draw, should be >= DECK's size * @param DECK to draw from - * - * @return created hand, backed ({@link ArrayList#subList(int, int)}) by the original deck + * + * @return created hand, backed ({@link ArrayList#subList(int, int)}) by the original deck */ public static ArrayList draw_hand(final int HAND_SIZE, final ArrayList DECK) { @@ -272,6 +274,19 @@ public static ArrayList draw_hand(final int HAND_SIZE, return new ArrayList(DECK.subList(0, HAND_SIZE)); } + /** + * Subroutine to draw a deep copied hand synchronously. + * + * @param HAND_SIZE to draw, should be >= DECK's size + * @param DECK to draw from + * + * @return created hand copy, backed ({@link ArrayList#subList(int, int)}) by the original deck + */ + public synchronized static ArrayList draw_safe_hand_copy(final int HAND_SIZE, final ArrayList DECK) + { + return Deck_Card.deep_copy(draw_hand(HAND_SIZE, DECK)); + } + /** * Frees all cards in hand. {@link Reservable#release()} * @@ -301,7 +316,7 @@ public String simulate(final boolean OVERRIDE, final int HAND_SIZE, final int TE final int CORE_COUNT = Runtime.getRuntime().availableProcessors(); if (CORE_COUNT > 1) - return this.parallel_simulation(CORE_COUNT, HAND_SIZE, TEST_HAND_COUNT); + return this.parallel_simulation2(HAND_SIZE, TEST_HAND_COUNT); else if (CORE_COUNT == 1) return Simulation.sequential_simulation(HAND_SIZE, TEST_HAND_COUNT, this.DECK, this.FOREST); else if (CORE_COUNT < 1) @@ -311,6 +326,41 @@ else if (CORE_COUNT < 1) "\" which is not < 1, == 1, nor > 1."); } + protected String parallel_simulation2(final int HAND_SIZE, final int TEST_HAND_COUNT) { + class ScenarioCounter { + final Scenario equation; + final AtomicInteger count; + + ScenarioCounter(Scenario equation) { + this.equation = equation; + this.count = new AtomicInteger(); + } + + void run_hand(ArrayList hand) { + if (this.equation.evaluate(hand)) { + this.count.incrementAndGet(); + } + reset_hand(hand); + } + } + + final List forestCounters = FOREST.stream().map(ScenarioCounter::new).collect(Collectors.toList()); + final long START_TIME; //simulation start time in milliseconds + + synchronized (Simulation.PARALLEL_SIMULATION_LOCK) { + START_TIME = System.currentTimeMillis(); + + Stream.generate(() -> draw_safe_hand_copy(HAND_SIZE, DECK)) + .parallel() + .limit(TEST_HAND_COUNT) + .forEach(hand -> forestCounters.forEach(fc -> fc.run_hand(hand))); + + AtomicInteger[] HITS = forestCounters.stream().map(a -> a.count).toArray(AtomicInteger[]::new); + + return Simulation.assemble_results_subroutine(HAND_SIZE, TEST_HAND_COUNT, START_TIME, HITS, this.FOREST); + } + } + /** * Carries out parallel simulation. The trade off over {@link #sequential_simulation(int, int, ArrayList, ArrayList)} is resources for time. * @@ -330,12 +380,12 @@ class Hand_Tester implements Runnable /** * Counts number of times this test has passed. */ - private final AtomicInteger HIT_COUNTER; + private final AtomicInteger[] HIT_COUNTER; /** * How to test hand. */ - private final Scenario EQUATION; + private final ArrayList EQUATION; /** * Hand to be tested. @@ -349,7 +399,7 @@ class Hand_Tester implements Runnable * @param EQUATION to perform testing on {@link #TEST_HAND} * @param TEST_HAND to be tested by {@link #EQUATION} */ - public Hand_Tester(final AtomicInteger HIT_COUNTER, final Scenario EQUATION, final ArrayList TEST_HAND) + public Hand_Tester(final AtomicInteger[] HIT_COUNTER, final ArrayList EQUATION, final ArrayList TEST_HAND) { this.HIT_COUNTER = HIT_COUNTER; this.EQUATION = EQUATION; @@ -359,8 +409,12 @@ public Hand_Tester(final AtomicInteger HIT_COUNTER, final Scenario EQUATION, fin @Override public void run() { - if (this.EQUATION.evaluate(this.TEST_HAND)) - this.HIT_COUNTER.incrementAndGet(); + for (int i = 0; i < this.EQUATION.size(); ++i) { + Scenario branch = this.EQUATION.get(i); + if (branch.evaluate(this.TEST_HAND)) + this.HIT_COUNTER[i].incrementAndGet(); + reset_hand(this.TEST_HAND); + } } } @@ -492,6 +546,7 @@ protected void modulate_parallelization() //control partition size { final int BLOCKING_QUEUE_SIZE = this.TASK_OVERSEER.getQueue().size(); + //Magic numbers have been tested to be rather good, also feels right as well. final int UPPER_THRESHOLD = 125; final int LOWER_THRESHOLD = 25; @@ -522,15 +577,9 @@ protected void run_subroutine(final int START, final int END) for (int i = START; i < END; ++i) { - //synchronise transformation of shallow to deep - synchronized (this) - { - current_hand = Deck_Card.deep_copy(draw_hand(HAND_SIZE, this.DECK)); - } + current_hand = draw_safe_hand_copy(HAND_SIZE, this.DECK); - this.TASK_OVERSEER.execute(new Hand_Tester(this.HITS[0], this.FOREST.get(0), current_hand)); - for (int j = 1; j < this.FOREST.size(); ++j) - this.TASK_OVERSEER.execute(new Hand_Tester(this.HITS[j], this.FOREST.get(j), Deck_Card.deep_copy(current_hand))); + this.TASK_OVERSEER.execute(new Hand_Tester(this.HITS, this.FOREST, current_hand)); } } }