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.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<Point> pathSoFar;
private ForkJoinPool pool;
private ArrayList<ConcurrentSolverTask> subtasks;
private int currentSector;
public ConcurrentSolverTask(PointAndDirection start, ArrayDeque<Point> 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<Point> 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()) {