use sectors

This commit is contained in:
Luca Conte 2025-06-09 19:16:20 +02:00
parent 344aa4af18
commit e2acf6899e
1 changed files with 69 additions and 10 deletions

View File

@ -20,6 +20,7 @@ import javax.swing.JScrollPane;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
final public class Solver extends JPanel { 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? // For each cell in the labyrinth: Has solve() visited it yet?
private boolean[][] visited; // initialized in solve() private boolean[][] visited; // initialized in solve()
private AtomicBoolean[][] visitedAtomic; 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 // determined by trial and error
private static final int DISTANCE_PER_TASK = DEFAULT_WIDTH_IN_CELLS * DEFAULT_HEIGHT_IN_CELLS / 128; 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]); 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() { public Point[] solveConcurrently() {
// dummy origin direction for start // dummy origin direction for start
PointAndDirection start = new PointAndDirection(labyrinth.getStart(), null); PointAndDirection start = new PointAndDirection(labyrinth.getStart(), null);
visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()]; // visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()];
for (int i = 0; i < visitedAtomic.length; i++) { // for (int i = 0; i < visitedAtomic.length; i++) {
for (int j = 0; j < visitedAtomic[i].length; j++) { // for (int j = 0; j < visitedAtomic[i].length; j++) {
visitedAtomic[i][j] = new AtomicBoolean(false); // 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(); ForkJoinPool pool = ForkJoinPool.commonPool();
@ -140,6 +162,9 @@ final public class Solver extends JPanel {
ConcurrentSolverTask t = new ConcurrentSolverTask(start, pathSoFar, pool); ConcurrentSolverTask t = new ConcurrentSolverTask(start, pathSoFar, pool);
Point[] result = pool.invoke(t); 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; return result;
} }
@ -149,6 +174,7 @@ final public class Solver extends JPanel {
private ArrayDeque<Point> pathSoFar; private ArrayDeque<Point> pathSoFar;
private ForkJoinPool pool; private ForkJoinPool pool;
private ArrayList<ConcurrentSolverTask> subtasks; private ArrayList<ConcurrentSolverTask> subtasks;
private int currentSector;
public ConcurrentSolverTask(PointAndDirection start, ArrayDeque<Point> pathSoFar, ForkJoinPool pool) { public ConcurrentSolverTask(PointAndDirection start, ArrayDeque<Point> pathSoFar, ForkJoinPool pool) {
this.start = start; this.start = start;
this.pathSoFar = pathSoFar; this.pathSoFar = pathSoFar;
@ -156,9 +182,33 @@ final public class Solver extends JPanel {
this.backtrackStack = new ArrayDeque<>(); this.backtrackStack = new ArrayDeque<>();
this.subtasks = new ArrayList<>(); 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() { public Point[] compute() {
numTasks.incrementAndGet();
int currentLength = 0; int currentLength = 0;
Point current = this.start.getPoint(); Point current = this.start.getPoint();
@ -197,11 +247,19 @@ final public class Solver extends JPanel {
// check if that cell has been visited // check if that cell has been visited
// set visitedAtomic to true if it has not been visited // set visitedAtomic to true if it has not been visited
if (!visitedAtomic[neighbour.x][neighbour.y].compareAndSet(false, true)) { // if (!visitedAtomic[neighbour.x][neighbour.y].compareAndSet(false, true)) {
continue; // continue;
} // }
acquireSector(neighbour);
if (visited[neighbour.x][neighbour.y]) continue;
visited[neighbour.x][neighbour.y] = true;
releaseSector();
// System.out.println("Found unvisited neighbour: " + neighbour); // System.out.println("Found unvisited neighbour: " + neighbour);
if (currentLength >= DISTANCE_PER_TASK) { if (currentLength >= DISTANCE_PER_TASK) {
releaseSector();
// if we have reached the distance limit, create a subtask // if we have reached the distance limit, create a subtask
ArrayDeque<Point> subPath = this.pathSoFar.clone(); ArrayDeque<Point> subPath = this.pathSoFar.clone();
subPath.addLast(current); subPath.addLast(current);
@ -210,8 +268,9 @@ final public class Solver extends JPanel {
subPath, subPath,
this.pool this.pool
); );
subtask.fork();
subtasks.add(subtask); subtasks.add(subtask);
subtask.fork();
} else { } else {
if (next == null) { if (next == null) {
// visit first unvisited neighbour next // visit first unvisited neighbour next
@ -224,7 +283,6 @@ final public class Solver extends JPanel {
} }
} }
} }
// at least one unvisited neighbour found // at least one unvisited neighbour found
if (next != null) { if (next != null) {
pathSoFar.addLast(current); pathSoFar.addLast(current);
@ -232,6 +290,7 @@ final public class Solver extends JPanel {
currentLength++; currentLength++;
} }
if (next == null) { if (next == null) {
releaseSector();
// no unvisited neighbour found // no unvisited neighbour found
// backtrack if possible // backtrack if possible
if (this.backtrackStack.isEmpty()) { if (this.backtrackStack.isEmpty()) {