From f8a9882358d1d73d37fa32e593f528b83e76bed0 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sun, 5 Oct 2025 14:27:26 +0000 Subject: [PATCH 01/14] Setting up GitHub Classroom Feedback From a45923b57eab815c939de612fb4b72c77937cc35 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sun, 5 Oct 2025 14:27:28 +0000 Subject: [PATCH 02/14] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6495c30..0eb4312 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/AwTYhPar) # Лабораторная работа № 1: определение достижимости параллелизма и реализация параллельных алгоритмов. Шаги выполнения: From fc47e4c4c9c28703fe1f955acb15be56f3cf4fdd Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 12 Oct 2025 20:15:20 +0300 Subject: [PATCH 03/14] Fix .gitignore --- .gitignore | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index b63da45..bef95b8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,7 @@ build/ !**/src/test/**/build/ ### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ +.idea *.iws *.iml *.ipr @@ -39,4 +36,4 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store From 36204c9cbed9b769f4eca301365db0546012861a Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 03:01:57 +0300 Subject: [PATCH 04/14] Primitive parallel version --- .../java/org/itmo/AtomicBooleanArray.java | 15 ++++++++ src/main/java/org/itmo/Graph.java | 35 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/main/java/org/itmo/AtomicBooleanArray.java diff --git a/src/main/java/org/itmo/AtomicBooleanArray.java b/src/main/java/org/itmo/AtomicBooleanArray.java new file mode 100644 index 0000000..a3655a3 --- /dev/null +++ b/src/main/java/org/itmo/AtomicBooleanArray.java @@ -0,0 +1,15 @@ +package org.itmo; + +import java.util.concurrent.atomic.AtomicIntegerArray; + +public final class AtomicBooleanArray { + private final AtomicIntegerArray inner; + + public AtomicBooleanArray(int size) { + inner = new AtomicIntegerArray(size); + } + + public boolean getAndSet(int i, boolean value) { + return inner.getAndSet(i, value ? 1 : 0) != 0; + } +} diff --git a/src/main/java/org/itmo/Graph.java b/src/main/java/org/itmo/Graph.java index 141a0b6..a4a66c8 100644 --- a/src/main/java/org/itmo/Graph.java +++ b/src/main/java/org/itmo/Graph.java @@ -4,6 +4,7 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; class Graph { private final int V; @@ -24,6 +25,40 @@ void addEdge(int src, int dest) { } void parallelBFS(int startVertex) { + final int parallelism = Runtime.getRuntime().availableProcessors(); + final ExecutorService pool = Executors.newFixedThreadPool(parallelism); + final AtomicBooleanArray visited = new AtomicBooleanArray(V); + visited.getAndSet(startVertex, true); + + ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + queue.add(startVertex); + + // Can't use runnable because of the `pool.invokeAll` signature. + final List> tasks = new ArrayList<>(parallelism); + final Callable task = () -> { + while (!queue.isEmpty()) { + Integer v = queue.poll(); + if (v != null) { + for (int n : adjList[v]) { + if (!visited.getAndSet(n, true)) { + queue.add(n); + } + } + } + } + return null; + }; + for (int i = 0; i < parallelism; i++) { + tasks.add(task); + } + + try { + pool.invokeAll(tasks); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + pool.shutdown(); + } } //Generated by ChatGPT From c2736ac4717a9fdf00161319dcc3c62d80ac13bf Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 03:15:27 +0300 Subject: [PATCH 05/14] ./gradlew run --- src/main/java/MainKt.java | 15 +++++++++++++++ src/main/java/org/itmo/Graph.java | 4 ++-- .../java/org/itmo/RandomGraphGenerator.java | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/main/java/MainKt.java rename src/{test => main}/java/org/itmo/RandomGraphGenerator.java (98%) diff --git a/src/main/java/MainKt.java b/src/main/java/MainKt.java new file mode 100644 index 0000000..4f0fcec --- /dev/null +++ b/src/main/java/MainKt.java @@ -0,0 +1,15 @@ +import org.itmo.Graph; +import org.itmo.RandomGraphGenerator; + +import java.util.Random; + +public class MainKt { + public static void main(String[] args) { + final RandomGraphGenerator generator = new RandomGraphGenerator(); + final Random random = new Random(4); + for (int i = 0; i < 1000; i++) { + final Graph graph = generator.generateGraph(random, 1_000_000, 1_000_000); + graph.parallelBFS(0); + } + } +} diff --git a/src/main/java/org/itmo/Graph.java b/src/main/java/org/itmo/Graph.java index a4a66c8..1684b69 100644 --- a/src/main/java/org/itmo/Graph.java +++ b/src/main/java/org/itmo/Graph.java @@ -6,7 +6,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerArray; -class Graph { +public class Graph { private final int V; private final ArrayList[] adjList; @@ -24,7 +24,7 @@ void addEdge(int src, int dest) { } } - void parallelBFS(int startVertex) { + public void parallelBFS(int startVertex) { final int parallelism = Runtime.getRuntime().availableProcessors(); final ExecutorService pool = Executors.newFixedThreadPool(parallelism); final AtomicBooleanArray visited = new AtomicBooleanArray(V); diff --git a/src/test/java/org/itmo/RandomGraphGenerator.java b/src/main/java/org/itmo/RandomGraphGenerator.java similarity index 98% rename from src/test/java/org/itmo/RandomGraphGenerator.java rename to src/main/java/org/itmo/RandomGraphGenerator.java index fdb888c..9f60b27 100644 --- a/src/test/java/org/itmo/RandomGraphGenerator.java +++ b/src/main/java/org/itmo/RandomGraphGenerator.java @@ -18,7 +18,7 @@ private int unpackV(long key) { return (int) (key & 0xffffffffL); } - Graph generateGraph(Random r, int size, int numEdges) { + public Graph generateGraph(Random r, int size, int numEdges) { if (numEdges < size - 1) throw new IllegalArgumentException("We need min size-1 edges"); long maxDirected = (long) size * (size - 1); if (numEdges > maxDirected) throw new IllegalArgumentException("Too many edges for directed graph without self-loops"); From 91fa83a30dc5b35101bc93aad70a52347d2015b8 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 03:31:42 +0300 Subject: [PATCH 06/14] Deadlock --- src/main/java/org/itmo/Graph.java | 17 +- tmp/dump.txt | 427 ++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 tmp/dump.txt diff --git a/src/main/java/org/itmo/Graph.java b/src/main/java/org/itmo/Graph.java index 1684b69..c90f1bc 100644 --- a/src/main/java/org/itmo/Graph.java +++ b/src/main/java/org/itmo/Graph.java @@ -5,6 +5,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; public class Graph { private final int V; @@ -34,14 +36,23 @@ public void parallelBFS(int startVertex) { queue.add(startVertex); // Can't use runnable because of the `pool.invokeAll` signature. + final Lock[] locks = new Lock[parallelism]; + for (int i = 0; i < locks.length; i++) { + locks[i] = new ReentrantLock(); + } final List> tasks = new ArrayList<>(parallelism); final Callable task = () -> { while (!queue.isEmpty()) { Integer v = queue.poll(); - if (v != null) { + if (v == null) { + continue; + } + synchronized (locks[v % locks.length]) { for (int n : adjList[v]) { - if (!visited.getAndSet(n, true)) { - queue.add(n); + synchronized (locks[n % locks.length]) { + if (!visited.getAndSet(n, true)) { + queue.add(n); + } } } } diff --git a/tmp/dump.txt b/tmp/dump.txt new file mode 100644 index 0000000..b6e6bff --- /dev/null +++ b/tmp/dump.txt @@ -0,0 +1,427 @@ +2025-10-14 03:31:25 +Full thread dump OpenJDK 64-Bit Server VM (25.462-b08 mixed mode): + +"Attach Listener" #201 daemon prio=9 os_prio=0 tid=0x00007f0774001000 nid=0x19eab waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"pool-15-thread-12" #200 prio=5 os_prio=0 tid=0x00007f07e823d000 nid=0x19ded waiting on condition [0x00007f07ceef9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b2f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - None + +"pool-15-thread-11" #199 prio=5 os_prio=0 tid=0x00007f07e823c800 nid=0x19dec waiting on condition [0x00007f07ce8f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b2f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - None + +"pool-15-thread-10" #198 prio=5 os_prio=0 tid=0x00007f07e8227000 nid=0x19deb waiting on condition [0x00007f07cebf9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b2f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - None + +"pool-15-thread-9" #197 prio=5 os_prio=0 tid=0x00007f07e8233800 nid=0x19dea waiting on condition [0x00007f07ce7f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b2f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - None + +"pool-15-thread-8" #196 prio=5 os_prio=0 tid=0x00007f07e8230000 nid=0x19de9 waiting on condition [0x00007f07ceaf9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b2f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - None + +"pool-15-thread-7" #195 prio=5 os_prio=0 tid=0x00007f07e8231800 nid=0x19de8 waiting on condition [0x00007f07ceff9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b2f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - None + +"pool-15-thread-6" #194 prio=5 os_prio=0 tid=0x00007f07e822c800 nid=0x19de7 waiting for monitor entry [0x00007f07ce9f9000] + java.lang.Thread.State: BLOCKED (on object monitor) + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:51) + - waiting to lock <0x000000077b22b650> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - <0x000000077b22c718> (a java.util.concurrent.ThreadPoolExecutor$Worker) + +"pool-15-thread-5" #193 prio=5 os_prio=0 tid=0x00007f07e8228800 nid=0x19de6 waiting for monitor entry [0x00007f07ce6f9000] + java.lang.Thread.State: BLOCKED (on object monitor) + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b770> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b650> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - <0x000000077b22c450> (a java.util.concurrent.ThreadPoolExecutor$Worker) + +"pool-15-thread-4" #192 prio=5 os_prio=0 tid=0x00007f07e823a800 nid=0x19de5 waiting for monitor entry [0x00007f07cedf9000] + java.lang.Thread.State: BLOCKED (on object monitor) + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b7a0> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b710> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - <0x000000077b22c188> (a java.util.concurrent.ThreadPoolExecutor$Worker) + +"pool-15-thread-3" #191 prio=5 os_prio=0 tid=0x00007f07e822e000 nid=0x19de4 waiting for monitor entry [0x00007f07ce4f9000] + java.lang.Thread.State: BLOCKED (on object monitor) + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b770> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b7a0> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - <0x000000077b22bec0> (a java.util.concurrent.ThreadPoolExecutor$Worker) + +"pool-15-thread-2" #190 prio=5 os_prio=0 tid=0x00007f07e8238800 nid=0x19de3 waiting for monitor entry [0x00007f07cecf9000] + java.lang.Thread.State: BLOCKED (on object monitor) + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b680> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b770> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - <0x000000077b22bbf8> (a java.util.concurrent.ThreadPoolExecutor$Worker) + +"pool-15-thread-1" #189 prio=5 os_prio=0 tid=0x00007f07e8235000 nid=0x19de2 waiting for monitor entry [0x00007f07ce5f9000] + java.lang.Thread.State: BLOCKED (on object monitor) + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b7a0> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b680> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + + Locked ownable synchronizers: + - <0x000000077b22b8e0> (a java.util.concurrent.ThreadPoolExecutor$Worker) + +"ForkJoinPool.commonPool-worker-10" #19 daemon prio=5 os_prio=0 tid=0x00007f0760005000 nid=0x19d36 waiting on condition [0x00007f07cf0f9000] + java.lang.Thread.State: TIMED_WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"ForkJoinPool.commonPool-worker-3" #20 daemon prio=5 os_prio=0 tid=0x00007f074c001000 nid=0x19d35 waiting on condition [0x00007f07cf1f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"ForkJoinPool.commonPool-worker-1" #18 daemon prio=5 os_prio=0 tid=0x00007f0750001000 nid=0x19d34 waiting on condition [0x00007f07cf2f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"ForkJoinPool.commonPool-worker-15" #16 daemon prio=5 os_prio=0 tid=0x00007f0760003000 nid=0x19d33 waiting on condition [0x00007f07cf3f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"ForkJoinPool.commonPool-worker-13" #14 daemon prio=5 os_prio=0 tid=0x00007f0754003000 nid=0x19d30 waiting on condition [0x00007f07cf6f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"ForkJoinPool.commonPool-worker-2" #11 daemon prio=5 os_prio=0 tid=0x00007f07e821e800 nid=0x19d2d waiting on condition [0x00007f07cf9f9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"ForkJoinPool.commonPool-worker-9" #10 daemon prio=5 os_prio=0 tid=0x00007f07e821d000 nid=0x19d2c waiting on condition [0x00007f07cfaf9000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x00000006d0001d70> (a java.util.concurrent.ForkJoinPool) + at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) + at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) + at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) + + Locked ownable synchronizers: + - None + +"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007f07e80e7000 nid=0x19d2a runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f07e80d2000 nid=0x19d29 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f07e80cf800 nid=0x19d28 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f07e80ce000 nid=0x19d27 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f07e80cb000 nid=0x19d26 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f07e80c8000 nid=0x19d25 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + + Locked ownable synchronizers: + - None + +"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f07e8094800 nid=0x19d24 in Object.wait() [0x00007f07d4686000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x00000006d0065e80> (a java.lang.ref.ReferenceQueue$Lock) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) + - locked <0x00000006d0065e80> (a java.lang.ref.ReferenceQueue$Lock) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) + at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:188) + + Locked ownable synchronizers: + - None + +"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f07e8090000 nid=0x19d23 in Object.wait() [0x00007f07d4786000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x00000006d0065ec0> (a java.lang.ref.Reference$Lock) + at java.lang.Object.wait(Object.java:502) + at java.lang.ref.Reference.tryHandlePending(Reference.java:191) + - locked <0x00000006d0065ec0> (a java.lang.ref.Reference$Lock) + at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) + + Locked ownable synchronizers: + - None + +"main" #1 prio=5 os_prio=0 tid=0x00007f07e800a800 nid=0x19d17 waiting on condition [0x00007f07ecbfe000] + java.lang.Thread.State: WAITING (parking) + at sun.misc.Unsafe.park(Native Method) + - parking to wait for <0x000000077b22b8c0> (a java.util.concurrent.FutureTask) + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) + at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429) + at java.util.concurrent.FutureTask.get(FutureTask.java:191) + at java.util.concurrent.AbstractExecutorService.invokeAll(AbstractExecutorService.java:244) + at org.itmo.Graph.parallelBFS(Graph.java:67) + at MainKt.main(MainKt.java:12) + + Locked ownable synchronizers: + - None + +"VM Thread" os_prio=0 tid=0x00007f07e8086800 nid=0x19d22 runnable + +"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f07e8020800 nid=0x19d18 runnable + +"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f07e8022800 nid=0x19d19 runnable + +"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f07e8024000 nid=0x19d1a runnable + +"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f07e8026000 nid=0x19d1b runnable + +"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f07e8028000 nid=0x19d1c runnable + +"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f07e8029800 nid=0x19d1d runnable + +"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f07e802b800 nid=0x19d1e runnable + +"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007f07e802d800 nid=0x19d1f runnable + +"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x00007f07e802f000 nid=0x19d20 runnable + +"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x00007f07e8031000 nid=0x19d21 runnable + +"VM Periodic Task Thread" os_prio=0 tid=0x00007f07e80ea000 nid=0x19d2b waiting on condition + +JNI global references: 217 + + +Found one Java-level deadlock: +============================= +"pool-15-thread-6": + waiting to lock monitor 0x00007f072c005aa8 (object 0x000000077b22b650, a java.util.concurrent.locks.ReentrantLock), + which is held by "pool-15-thread-5" +"pool-15-thread-5": + waiting to lock monitor 0x00007f077c004068 (object 0x000000077b22b770, a java.util.concurrent.locks.ReentrantLock), + which is held by "pool-15-thread-2" +"pool-15-thread-2": + waiting to lock monitor 0x00007f077c003fb8 (object 0x000000077b22b680, a java.util.concurrent.locks.ReentrantLock), + which is held by "pool-15-thread-1" +"pool-15-thread-1": + waiting to lock monitor 0x00007f077c004c18 (object 0x000000077b22b7a0, a java.util.concurrent.locks.ReentrantLock), + which is held by "pool-15-thread-3" +"pool-15-thread-3": + waiting to lock monitor 0x00007f077c004068 (object 0x000000077b22b770, a java.util.concurrent.locks.ReentrantLock), + which is held by "pool-15-thread-2" + +Java stack information for the threads listed above: +=================================================== +"pool-15-thread-6": + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:51) + - waiting to lock <0x000000077b22b650> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) +"pool-15-thread-5": + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b770> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b650> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) +"pool-15-thread-2": + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b680> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b770> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) +"pool-15-thread-1": + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b7a0> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b680> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) +"pool-15-thread-3": + at org.itmo.Graph.lambda$parallelBFS$0(Graph.java:53) + - waiting to lock <0x000000077b22b770> (a java.util.concurrent.locks.ReentrantLock) + - locked <0x000000077b22b7a0> (a java.util.concurrent.locks.ReentrantLock) + at org.itmo.Graph$$Lambda$3/1915910607.call(Unknown Source) + at java.util.concurrent.FutureTask.run(FutureTask.java:266) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) + at java.lang.Thread.run(Thread.java:750) + +Found 1 deadlock. + From 042c599c889a72cafd76a05d2047f28fb5696d0d Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 03:33:46 +0300 Subject: [PATCH 07/14] Remove deadlock --- src/main/java/org/itmo/Graph.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/itmo/Graph.java b/src/main/java/org/itmo/Graph.java index c90f1bc..66ef32e 100644 --- a/src/main/java/org/itmo/Graph.java +++ b/src/main/java/org/itmo/Graph.java @@ -36,10 +36,6 @@ public void parallelBFS(int startVertex) { queue.add(startVertex); // Can't use runnable because of the `pool.invokeAll` signature. - final Lock[] locks = new Lock[parallelism]; - for (int i = 0; i < locks.length; i++) { - locks[i] = new ReentrantLock(); - } final List> tasks = new ArrayList<>(parallelism); final Callable task = () -> { while (!queue.isEmpty()) { @@ -47,13 +43,9 @@ public void parallelBFS(int startVertex) { if (v == null) { continue; } - synchronized (locks[v % locks.length]) { - for (int n : adjList[v]) { - synchronized (locks[n % locks.length]) { - if (!visited.getAndSet(n, true)) { - queue.add(n); - } - } + for (int n : adjList[v]) { + if (!visited.getAndSet(n, true)) { + queue.add(n); } } } From a2e4053b2dc1a3b980aaddc2246ab4d747398849 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 12:17:17 +0300 Subject: [PATCH 08/14] Benchmark --- src/main/java/MainKt.java | 26 +++++++++++++++++++++++--- tmp/times.txt | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tmp/times.txt diff --git a/src/main/java/MainKt.java b/src/main/java/MainKt.java index 4f0fcec..ff9c36b 100644 --- a/src/main/java/MainKt.java +++ b/src/main/java/MainKt.java @@ -7,9 +7,29 @@ public class MainKt { public static void main(String[] args) { final RandomGraphGenerator generator = new RandomGraphGenerator(); final Random random = new Random(4); - for (int i = 0; i < 1000; i++) { - final Graph graph = generator.generateGraph(random, 1_000_000, 1_000_000); - graph.parallelBFS(0); + for (int i = 0; i < 100; i++) { + System.err.println("Warmup " + i); + generator.generateGraph(random, 100_000, 1_000_000); + } + final int[] sizes = {10, 100, 1000, 10_000, 10_000, 50_000, 100_000}; + for (final int size : sizes) { + for (int i = 0; i <= size / 5; i += Math.max(1, size / 50)) { + final int numEdges = i * i; + if (numEdges < size) { + continue; + } + if (numEdges > size * (size - 1) / 2) { + break; + } + System.out.print(size + " " + numEdges + " "); + System.out.flush(); + final Graph graph = generator.generateGraph(random, size, numEdges); + final long start = System.nanoTime(); + graph.parallelBFS(0); + final long end = System.nanoTime(); + final long duration = end - start; + System.out.println(duration); + } } } } diff --git a/tmp/times.txt b/tmp/times.txt new file mode 100644 index 0000000..ddbbe83 --- /dev/null +++ b/tmp/times.txt @@ -0,0 +1,39 @@ +100 100 7534869 +100 144 1342229 +100 196 1267499 +100 256 1283842 +100 324 1248781 +100 400 1566563 +1000 1600 1258768 +1000 3600 1835527 +1000 6400 1508385 +1000 10000 1760515 +1000 14400 3742221 +1000 19600 4875553 +1000 25600 6183211 +1000 32400 7815846 +1000 40000 7081104 +10000 40000 6816541 +10000 160000 5398532 +10000 360000 5002387 +10000 640000 6658068 +10000 1000000 7580476 +10000 1440000 9171554 +10000 1960000 13154034 +10000 2560000 11434026 +10000 3240000 15047320 +10000 4000000 15362518 +10000 40000 3307104 +10000 160000 4390149 +10000 360000 5189006 +10000 640000 6002389 +10000 1000000 7174134 +10000 1440000 11227783 +10000 1960000 12763336 +10000 2560000 12633359 +10000 3240000 12702503 +10000 4000000 18393884 +100000 4000000 42025315 +100000 16000000 77106138 +100000 36000000 118415988 +100000 64000000 187235473 \ No newline at end of file From c3a96fa7eff86c3fa2554fd5e09f066609eb0f07 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 12:27:44 +0300 Subject: [PATCH 09/14] Graph --- plot.py | 7 +++++++ tmp/times.svg | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 plot.py create mode 100644 tmp/times.svg diff --git a/plot.py b/plot.py new file mode 100644 index 0000000..f888247 --- /dev/null +++ b/plot.py @@ -0,0 +1,7 @@ +with open("tmp/times.txt") as f: + data = f.read() +data = [list(map(int, line.split())) for line in data.splitlines()] + +for v, e, time in data: + load = 100 * e / (v * (v - 1) // 2) + print(f"{{{v}, {load}, {time}}},") \ No newline at end of file diff --git a/tmp/times.svg b/tmp/times.svg new file mode 100644 index 0000000..e4bcd94 --- /dev/null +++ b/tmp/times.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From 33ddfcfb778149cb5b79aa014500982d755bee8c Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Oct 2025 12:56:27 +0300 Subject: [PATCH 10/14] Add results --- tmp/results.txt | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tmp/results.txt b/tmp/results.txt index 027e7f9..cb9d5b7 100644 --- a/tmp/results.txt +++ b/tmp/results.txt @@ -1,32 +1,36 @@ Times for 10 vertices and 50 connections: Serial: 0 -Parallel: 0 +Parallel: 5 -------- Times for 100 vertices and 500 connections: Serial: 0 -Parallel: 0 +Parallel: 1 -------- Times for 1000 vertices and 5000 connections: -Serial: 1 -Parallel: 0 +Serial: 2 +Parallel: 3 -------- Times for 10000 vertices and 50000 connections: -Serial: 3 -Parallel: 0 +Serial: 11 +Parallel: 19 -------- Times for 10000 vertices and 100000 connections: -Serial: 2 -Parallel: 0 +Serial: 4 +Parallel: 11 -------- Times for 50000 vertices and 1000000 connections: Serial: 30 -Parallel: 0 +Parallel: 103 -------- Times for 100000 vertices and 1000000 connections: -Serial: 18 -Parallel: 0 +Serial: 43 +Parallel: 78 -------- Times for 1000000 vertices and 10000000 connections: -Serial: 307 -Parallel: 0 +Serial: 1300 +Parallel: 678 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 1849 +Parallel: 1596 -------- From 8446f3a5253dbed2f8890e97015734654d1740b4 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 28 Dec 2025 22:16:08 +0300 Subject: [PATCH 11/14] Fronts --- .../java/org/itmo/AtomicBooleanArray.java | 9 +++ src/main/java/org/itmo/Graph.java | 81 ++++++++++++------- src/test/java/org/itmo/BFSTest.java | 30 ++++++- tmp/results.txt | 28 +++---- 4 files changed, 100 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/itmo/AtomicBooleanArray.java b/src/main/java/org/itmo/AtomicBooleanArray.java index a3655a3..df2bfc1 100644 --- a/src/main/java/org/itmo/AtomicBooleanArray.java +++ b/src/main/java/org/itmo/AtomicBooleanArray.java @@ -1,5 +1,6 @@ package org.itmo; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicIntegerArray; public final class AtomicBooleanArray { @@ -12,4 +13,12 @@ public AtomicBooleanArray(int size) { public boolean getAndSet(int i, boolean value) { return inner.getAndSet(i, value ? 1 : 0) != 0; } + + public boolean[] toArray() { + final boolean[] result = new boolean[inner.length()]; + for (int i = 0; i < result.length; i++) { + result[i] = inner.get(i) != 0; + } + return result; + } } diff --git a/src/main/java/org/itmo/Graph.java b/src/main/java/org/itmo/Graph.java index 66ef32e..03430d9 100644 --- a/src/main/java/org/itmo/Graph.java +++ b/src/main/java/org/itmo/Graph.java @@ -1,12 +1,10 @@ package org.itmo; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicIntegerArray; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; public class Graph { private final int V; @@ -26,46 +24,57 @@ void addEdge(int src, int dest) { } } - public void parallelBFS(int startVertex) { + public AtomicBooleanArray parallelBFS(int startVertex) { final int parallelism = Runtime.getRuntime().availableProcessors(); final ExecutorService pool = Executors.newFixedThreadPool(parallelism); final AtomicBooleanArray visited = new AtomicBooleanArray(V); visited.getAndSet(startVertex, true); - - ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + ArrayList queue = new ArrayList<>(); queue.add(startVertex); - - // Can't use runnable because of the `pool.invokeAll` signature. - final List> tasks = new ArrayList<>(parallelism); - final Callable task = () -> { + try { while (!queue.isEmpty()) { - Integer v = queue.poll(); - if (v == null) { - continue; + // Can't use runnable because of the `pool.invokeAll` signature. + final List>> tasks = new ArrayList<>(parallelism); + + final int frontSize = queue.size(); + final int perTask = (frontSize - 1 + parallelism) / parallelism; + + for (int i_ = 0; i_ < parallelism; i_++) { + final int i = i_; + tasks.add(() -> { + final List result = new LinkedList<>(); + for (int j = 0; j < perTask; ++j) { + final int index = i * perTask + j; + if (index >= frontSize) { + break; + } + final int v = queue.get(index); + for (int u : adjList[v]) { + if (!visited.getAndSet(u, true)) { + result.add(u); + } + } + } + return result; + }); } - for (int n : adjList[v]) { - if (!visited.getAndSet(n, true)) { - queue.add(n); - } + final List>> results = pool.invokeAll(tasks); + queue.clear(); + for (Future> result : results) { + assert result.isDone(); + queue.addAll(result.get()); } } - return null; - }; - for (int i = 0; i < parallelism; i++) { - tasks.add(task); - } - - try { - pool.invokeAll(tasks); - } catch (InterruptedException e) { + } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } finally { pool.shutdown(); } + return visited; } //Generated by ChatGPT - void bfs(int startVertex) { + boolean[] bfs(int startVertex) { boolean[] visited = new boolean[V]; LinkedList queue = new LinkedList<>(); @@ -83,6 +92,18 @@ void bfs(int startVertex) { } } } + return visited; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append(V).append(":"); + for (int v = 0; v < V; v++) { + for (int u : adjList[v]) { + sb.append("\n\t").append(v).append(" -> ").append(u); + } + } + return sb.toString(); + } } diff --git a/src/test/java/org/itmo/BFSTest.java b/src/test/java/org/itmo/BFSTest.java index 7bf9098..d959a6a 100644 --- a/src/test/java/org/itmo/BFSTest.java +++ b/src/test/java/org/itmo/BFSTest.java @@ -4,11 +4,8 @@ import java.io.FileWriter; import java.io.IOException; -import java.nio.Buffer; -import java.util.HashSet; +import java.util.Arrays; import java.util.Random; -import java.util.function.BiFunction; -import java.util.stream.IntStream; public class BFSTest { @@ -49,4 +46,29 @@ private long executeParallelBfsAndGetTime(Graph g) { return endTime - startTime; } + @Test + public void fuzz() { + Random r = new Random(42); + for (int iter_ = 0; iter_ < 1000; iter_++) { + final int size = 100 + r.nextInt(1000); + final int connections = size - 1 + r.nextInt(Math.max(size * size / 2 - 2 * size, 0)); + System.out.println("--------------------------"); + System.out.println("Generating graph of size " + size + " ...wait"); + Graph g = new RandomGraphGenerator().generateGraph(r, size, connections); + System.out.println("Generation completed!\nStarting bfs"); + final int startVertex = 0; + final boolean[] expected = g.bfs(startVertex); + final boolean[] actual = g.parallelBFS(startVertex).toArray(); + if (!Arrays.equals(expected, actual)) { + System.err.println(g); + System.err.println(Arrays.toString(expected)); + for (int i = 0; i < size; i++) { + if (expected[i] != actual[i]) { + System.err.println(i + ": " + expected[i] + " -> " + actual[i]); + } + } + throw new RuntimeException(); + } + } + } } diff --git a/tmp/results.txt b/tmp/results.txt index cb9d5b7..3689cd6 100644 --- a/tmp/results.txt +++ b/tmp/results.txt @@ -4,33 +4,33 @@ Parallel: 5 -------- Times for 100 vertices and 500 connections: Serial: 0 -Parallel: 1 +Parallel: 2 -------- Times for 1000 vertices and 5000 connections: -Serial: 2 +Serial: 0 Parallel: 3 -------- Times for 10000 vertices and 50000 connections: -Serial: 11 -Parallel: 19 +Serial: 7 +Parallel: 13 -------- Times for 10000 vertices and 100000 connections: -Serial: 4 -Parallel: 11 +Serial: 3 +Parallel: 19 -------- Times for 50000 vertices and 1000000 connections: -Serial: 30 -Parallel: 103 +Serial: 27 +Parallel: 11 -------- Times for 100000 vertices and 1000000 connections: -Serial: 43 -Parallel: 78 +Serial: 35 +Parallel: 32 -------- Times for 1000000 vertices and 10000000 connections: -Serial: 1300 -Parallel: 678 +Serial: 1043 +Parallel: 146 -------- Times for 2000000 vertices and 10000000 connections: -Serial: 1849 -Parallel: 1596 +Serial: 1996 +Parallel: 1220 -------- From 348092b151180d226ae3a3c590c54801be435557 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 28 Dec 2025 23:04:59 +0300 Subject: [PATCH 12/14] direnv --- .envrc | 1 + .gitignore | 2 ++ shell.nix | 12 ++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 .envrc create mode 100644 shell.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore index bef95b8..0030fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ build/ !**/src/main/**/build/ !**/src/test/**/build/ +.direnv/ + ### IntelliJ IDEA ### .idea *.iws diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..94f3d91 --- /dev/null +++ b/shell.nix @@ -0,0 +1,12 @@ +{ + pkgs ? import { }, +}: +pkgs.mkShellNoCC { + preferLocalBuild = true; + allowSubstitutes = false; + + name = "par-prog-lab1"; + packages = with pkgs; [ + jdk8 + ]; +} From c2ae5992cb032eaa48b3efeffa27870b46bb7745 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 28 Dec 2025 23:05:48 +0300 Subject: [PATCH 13/14] JCStress --- .gitignore | 4 ++ build.gradle.kts | 16 ++++++- src/test/java/org/itmo/BFSJCStressTest.java | 51 +++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/itmo/BFSJCStressTest.java diff --git a/.gitignore b/.gitignore index 0030fdf..e841327 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ bin/ ### Mac OS ### .DS_Store + +# JCStress +jcstress-results-*.bin.gz +results/ diff --git a/build.gradle.kts b/build.gradle.kts index 3341beb..9919064 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,8 @@ repositories { dependencies { testImplementation(kotlin("test")) + testImplementation("org.openjdk.jcstress:jcstress-core:0.16") + testAnnotationProcessor("org.openjdk.jcstress:jcstress-core:0.16") } tasks.test { @@ -24,4 +26,16 @@ kotlin { application { mainClass.set("MainKt") -} \ No newline at end of file +} + +tasks.register("jcstress") { + group = "verification" + description = "Run JCStress stress tests" + mainClass.set("org.openjdk.jcstress.Main") + classpath = sourceSets.test.get().runtimeClasspath + dependsOn("testClasses") + val argsProp = project.findProperty("jcstressArgs") as String? + if (!argsProp.isNullOrBlank()) { + args = argsProp.split("\\s+".toRegex()) + } +} diff --git a/src/test/java/org/itmo/BFSJCStressTest.java b/src/test/java/org/itmo/BFSJCStressTest.java new file mode 100644 index 0000000..756212f --- /dev/null +++ b/src/test/java/org/itmo/BFSJCStressTest.java @@ -0,0 +1,51 @@ + +package org.itmo; + +import org.openjdk.jcstress.annotations.*; +import org.openjdk.jcstress.infra.results.I_Result; + +import java.util.Random; + +@JCStressTest +@State +@Outcome(id = "0", expect = Expect.ACCEPTABLE, desc = "Everything is ok") +@Outcome(id = "-2", expect = Expect.FORBIDDEN, desc = "Exception thrown") +@Outcome(id = "-1", expect = Expect.FORBIDDEN, desc = "Test not run") +public class BFSJCStressTest { + private int result = -1; + + @Actor + public void actor() { + this.result = run(); + } + + private static int run() { + final Random r = new Random(42); + final int startVertex = 0; + final int vert = 1000; + final int edges = 10_000; + final Graph graph = new RandomGraphGenerator().generateGraph(r, vert, edges); + final boolean[] expected = graph.bfs(startVertex); + final boolean[] actual; + try { + actual = graph.parallelBFS(startVertex).toArray(); + } catch (Exception e) { + return -2; + } + if (actual.length != expected.length) { + return -3; + } + int result = 0; + for (int i = 0; i < actual.length; i++) { + if (actual[i] != expected[i]) { + result++; + } + } + return result; + } + + @Arbiter + public void arbiter(I_Result result) { + result.r1 = this.result; + } +} \ No newline at end of file From a6fbc5a4aef38543b433ad8f87f3fcc6cf4e710d Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 28 Dec 2025 23:48:22 +0300 Subject: [PATCH 14/14] Graphs --- plot.py | 27 +++++++++++--- shell.nix | 6 +++ src/main/java/MainKt.java | 7 +++- tmp/times.txt | 77 +++++++++++++++++++-------------------- 4 files changed, 70 insertions(+), 47 deletions(-) mode change 100644 => 100755 plot.py diff --git a/plot.py b/plot.py old mode 100644 new mode 100755 index f888247..649637e --- a/plot.py +++ b/plot.py @@ -1,7 +1,22 @@ -with open("tmp/times.txt") as f: - data = f.read() -data = [list(map(int, line.split())) for line in data.splitlines()] +#!/usr/bin/env python3 -for v, e, time in data: - load = 100 * e / (v * (v - 1) // 2) - print(f"{{{v}, {load}, {time}}},") \ No newline at end of file +import fileinput + +import matplotlib.pyplot as plt +import numpy as np + + +def get_data(): + for line in fileinput.input(encoding="utf-8"): + v, e, time = map(int, line.strip().split()) + load = 100 * e / (v * (v - 1) // 2) + yield [np.log10(v), load, np.log10(time)] + + +DATA = np.array(list(get_data())) +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.scatter(*DATA.T) +ax.set_xlabel("Vertices (log10)") +ax.set_ylabel("Edge Load") +ax.set_zlabel("Time (log10)") +plt.show() diff --git a/shell.nix b/shell.nix index 94f3d91..3b84f0c 100644 --- a/shell.nix +++ b/shell.nix @@ -8,5 +8,11 @@ pkgs.mkShellNoCC { name = "par-prog-lab1"; packages = with pkgs; [ jdk8 + (python3.withPackages ( + p: with p; [ + matplotlib + numpy + ] + )) ]; } diff --git a/src/main/java/MainKt.java b/src/main/java/MainKt.java index ff9c36b..fbcfa97 100644 --- a/src/main/java/MainKt.java +++ b/src/main/java/MainKt.java @@ -7,9 +7,9 @@ public class MainKt { public static void main(String[] args) { final RandomGraphGenerator generator = new RandomGraphGenerator(); final Random random = new Random(4); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 5; i++) { System.err.println("Warmup " + i); - generator.generateGraph(random, 100_000, 1_000_000); + generator.generateGraph(random, 100_000, 10_000_000); } final int[] sizes = {10, 100, 1000, 10_000, 10_000, 50_000, 100_000}; for (final int size : sizes) { @@ -21,6 +21,9 @@ public static void main(String[] args) { if (numEdges > size * (size - 1) / 2) { break; } + if (numEdges > 50_000_000) { + break; + } System.out.print(size + " " + numEdges + " "); System.out.flush(); final Graph graph = generator.generateGraph(random, size, numEdges); diff --git a/tmp/times.txt b/tmp/times.txt index ddbbe83..c5d63ea 100644 --- a/tmp/times.txt +++ b/tmp/times.txt @@ -1,39 +1,38 @@ -100 100 7534869 -100 144 1342229 -100 196 1267499 -100 256 1283842 -100 324 1248781 -100 400 1566563 -1000 1600 1258768 -1000 3600 1835527 -1000 6400 1508385 -1000 10000 1760515 -1000 14400 3742221 -1000 19600 4875553 -1000 25600 6183211 -1000 32400 7815846 -1000 40000 7081104 -10000 40000 6816541 -10000 160000 5398532 -10000 360000 5002387 -10000 640000 6658068 -10000 1000000 7580476 -10000 1440000 9171554 -10000 1960000 13154034 -10000 2560000 11434026 -10000 3240000 15047320 -10000 4000000 15362518 -10000 40000 3307104 -10000 160000 4390149 -10000 360000 5189006 -10000 640000 6002389 -10000 1000000 7174134 -10000 1440000 11227783 -10000 1960000 12763336 -10000 2560000 12633359 -10000 3240000 12702503 -10000 4000000 18393884 -100000 4000000 42025315 -100000 16000000 77106138 -100000 36000000 118415988 -100000 64000000 187235473 \ No newline at end of file +100 100 6856322 +100 144 2018553 +100 196 1334527 +100 256 1190585 +100 324 1235842 +100 400 1785633 +1000 1600 1841157 +1000 3600 2257341 +1000 6400 2673595 +1000 10000 3382136 +1000 14400 2922859 +1000 19600 4962784 +1000 25600 5613775 +1000 32400 19526585 +1000 40000 1604325 +10000 40000 2309024 +10000 160000 2951564 +10000 360000 4126015 +10000 640000 4889242 +10000 1000000 6431268 +10000 1440000 18963525 +10000 1960000 10440579 +10000 2560000 13975598 +10000 3240000 13654956 +10000 4000000 15500303 +10000 40000 1785983 +10000 160000 2564154 +10000 360000 3416009 +10000 640000 5878264 +10000 1000000 6279153 +10000 1440000 8551860 +10000 1960000 10137957 +10000 2560000 11910459 +10000 3240000 12635761 +10000 4000000 13142321 +100000 4000000 26687625 +100000 16000000 63877639 +100000 36000000 123488881