diff --git a/projekt/Solver.java b/projekt/Solver.java index 089b5c7..a21b8f3 100644 --- a/projekt/Solver.java +++ b/projekt/Solver.java @@ -20,6 +20,7 @@ import javax.swing.JScrollPane; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; final public class Solver extends JPanel { @@ -37,6 +38,10 @@ final public class Solver extends JPanel { // For each cell in the labyrinth: Has solve() visited it yet? private boolean[][] visited; // initialized in solve() private AtomicBoolean[][] visitedAtomic; + private AtomicInteger numTasks = new AtomicInteger(0); + + private Semaphore[] sectors; + private static final int SECTOR_SIZE = (int) Math.sqrt(DEFAULT_WIDTH_IN_CELLS * DEFAULT_HEIGHT_IN_CELLS) / 50; // determined by trial and error private static final int DISTANCE_PER_TASK = DEFAULT_WIDTH_IN_CELLS * DEFAULT_HEIGHT_IN_CELLS / 128; @@ -123,15 +128,32 @@ final public class Solver extends JPanel { return pathSoFar.toArray(new Point[0]); } + private int getSectorsX() { + return (int)Math.ceil((double)labyrinth.getWidth() / SECTOR_SIZE); + } + private int getSectorsY() { + return (int)Math.ceil((double)labyrinth.getHeight() / SECTOR_SIZE); + } + private int getSectorId(Point p) { + return (p.getX() / SECTOR_SIZE) + (p.getY() / SECTOR_SIZE) * getSectorsX(); + } + public Point[] solveConcurrently() { // dummy origin direction for start PointAndDirection start = new PointAndDirection(labyrinth.getStart(), null); - visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()]; - for (int i = 0; i < visitedAtomic.length; i++) { - for (int j = 0; j < visitedAtomic[i].length; j++) { - visitedAtomic[i][j] = new AtomicBoolean(false); - } + // visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()]; + // for (int i = 0; i < visitedAtomic.length; i++) { + // for (int j = 0; j < visitedAtomic[i].length; j++) { + // visitedAtomic[i][j] = new AtomicBoolean(false); + // } + // } + + visited = new boolean[labyrinth.getWidth()][labyrinth.getHeight()]; + + sectors = new Semaphore[getSectorsX() * getSectorsY()]; + for (int i = 0; i < sectors.length; i++) { + sectors[i] = new Semaphore(1); } ForkJoinPool pool = ForkJoinPool.commonPool(); @@ -140,6 +162,9 @@ final public class Solver extends JPanel { ConcurrentSolverTask t = new ConcurrentSolverTask(start, pathSoFar, pool); Point[] result = pool.invoke(t); + System.out.println("Used tasks: " + numTasks.get()); + System.out.println("Task to Solution Length Ratio: " + (double)result.length / numTasks.get()); + return result; } @@ -149,6 +174,7 @@ final public class Solver extends JPanel { private ArrayDeque pathSoFar; private ForkJoinPool pool; private ArrayList subtasks; + private int currentSector; public ConcurrentSolverTask(PointAndDirection start, ArrayDeque pathSoFar, ForkJoinPool pool) { this.start = start; this.pathSoFar = pathSoFar; @@ -156,9 +182,33 @@ final public class Solver extends JPanel { this.backtrackStack = new ArrayDeque<>(); this.subtasks = new ArrayList<>(); + this.currentSector = -1; + } + + private void acquireSector(Point p) { + if (getSectorId(p) == this.currentSector) return; + if (this.currentSector >= 0) { + // release previous sector + sectors[this.currentSector].release(); + } + this.currentSector = getSectorId(p); + try { + // acquire new sector + sectors[this.currentSector].acquire(); + } catch (InterruptedException e) { + System.out.println(e); + return; + } + } + + private void releaseSector() { + if (this.currentSector >= 0) { + sectors[this.currentSector].release(); + } } public Point[] compute() { + numTasks.incrementAndGet(); int currentLength = 0; Point current = this.start.getPoint(); @@ -197,11 +247,19 @@ final public class Solver extends JPanel { // check if that cell has been visited // set visitedAtomic to true if it has not been visited - if (!visitedAtomic[neighbour.x][neighbour.y].compareAndSet(false, true)) { - continue; - } + // if (!visitedAtomic[neighbour.x][neighbour.y].compareAndSet(false, true)) { + // continue; + // } + + acquireSector(neighbour); + if (visited[neighbour.x][neighbour.y]) continue; + visited[neighbour.x][neighbour.y] = true; + releaseSector(); + + // System.out.println("Found unvisited neighbour: " + neighbour); if (currentLength >= DISTANCE_PER_TASK) { + releaseSector(); // if we have reached the distance limit, create a subtask ArrayDeque subPath = this.pathSoFar.clone(); subPath.addLast(current); @@ -210,8 +268,9 @@ final public class Solver extends JPanel { subPath, this.pool ); - subtask.fork(); subtasks.add(subtask); + + subtask.fork(); } else { if (next == null) { // visit first unvisited neighbour next @@ -224,7 +283,6 @@ final public class Solver extends JPanel { } } } - // at least one unvisited neighbour found if (next != null) { pathSoFar.addLast(current); @@ -232,6 +290,7 @@ final public class Solver extends JPanel { currentLength++; } if (next == null) { + releaseSector(); // no unvisited neighbour found // backtrack if possible if (this.backtrackStack.isEmpty()) {