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: определение достижимости параллелизма и реализация параллельных алгоритмов. Шаги выполнения: diff --git a/build.gradle.kts b/build.gradle.kts index 3341beb..5370e10 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ plugins { kotlin("jvm") version "1.9.20" + java application } @@ -12,6 +13,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 +27,20 @@ kotlin { application { mainClass.set("MainKt") -} \ No newline at end of file +} + +// JCStress runner task: runs JCStress tests located on the test runtime classpath +// Use: ./gradlew jcstress [-PjcstressArgs="-v -m quick"] +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/graphs/ParallelOnDifferentThreads.png b/graphs/ParallelOnDifferentThreads.png new file mode 100644 index 0000000..1c3f0fa Binary files /dev/null and b/graphs/ParallelOnDifferentThreads.png differ diff --git a/graphs/SerialAndParallel.png b/graphs/SerialAndParallel.png new file mode 100644 index 0000000..d274bb0 Binary files /dev/null and b/graphs/SerialAndParallel.png differ diff --git a/results/index.html b/results/index.html new file mode 100644 index 0000000..450c940 --- /dev/null +++ b/results/index.html @@ -0,0 +1,128 @@ + + + +Java Concurrency Stress test report + + + + + + + + +
+

+0% +

+ + + +
 
Overall pass rate: 0/1 
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
java.specification.nameJava Platform API Specification
java.specification.vendorOracle Corporation
java.specification.version17
java.vendorAmazon.com Inc.
java.version17.0.11
java.vm.nameOpenJDK 64-Bit Server VM
java.vm.vendorAmazon.com Inc.
java.vm.version17.0.11+9-LTS
os.archamd64
os.nameLinux
os.version6.8.0-79-generic
+
+
+

FAILED tests

+

Strong asserts were violated. Correct implementations should have no assert failures here.

+ + + + + + + +
   org.itmo.JSStressGraphTest104FAILED
+
+
+

ERROR tests

+

Tests break for some reason, other than failing the assert. Correct implementations should have none.

+ +
+None! +
+
+
+

INTERESTING tests

+

Some interesting behaviors observed. This is for the plain curiosity.

+ +
+None! +
+
+
+

All tests

+

+ + + + + + + +
   org.itmo.JSStressGraphTest104FAILED
+
+ + diff --git a/results/org.itmo.JSStressGraphTest.html b/results/org.itmo.JSStressGraphTest.html new file mode 100644 index 0000000..f4ca01f --- /dev/null +++ b/results/org.itmo.JSStressGraphTest.html @@ -0,0 +1,293 @@ + + + +Java Concurrency Stress test report + + + +

org.itmo.JSStressGraphTest

+

Description and references

+

null

+

Environment

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
java.specification.nameJava Platform API Specification
java.specification.vendorOracle Corporation
java.specification.version17
java.vendorAmazon.com Inc.
java.version17.0.11
java.vm.nameOpenJDK 64-Bit Server VM
java.vm.vendorAmazon.com Inc.
java.vm.version17.0.11+9-LTS
os.archamd64
os.nameLinux
os.version6.8.0-79-generic
+

Results

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Compilation ModeScheduling ClassJava OptionsStatusObserved States
-3
Forbidden
No default case provided, assume Forbidden
split
+    actor1: Interpreter
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking]
+
FAILED3815
split
+    actor1: Interpreter
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking]
+
FAILED3889
split
+    actor1: C1
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking]
+
FAILED3889
split
+    actor1: C1
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=1253187770]
+
FAILED3741
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=1947555701]
+
FAILED3741
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=1990019871]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=2109808913]
+
FAILED3815
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:+UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=349458943]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=143529006]
+
FAILED3815
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=1517871585]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=243224436]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=547705354]
+
FAILED3889
split
+    actor1: C2
+
    actor1: package group 0, core group 0
+
+
[-Dfile.encoding=UTF-8, -Duser.country=US, -Duser.language=en, -Duser.variant, -XX:-UseBiasedLocking, -XX:+StressLCM, -XX:+StressGCM, -XX:+StressIGVN, -XX:+StressCCP, -XX:StressSeed=570703936]
+
FAILED3815
+

Messages

+

VM Output Streams

+

VM Error Streams

+ + diff --git a/src/main/java/org/itmo/Graph.java b/src/main/java/org/itmo/Graph.java index 141a0b6..95b1eba 100644 --- a/src/main/java/org/itmo/Graph.java +++ b/src/main/java/org/itmo/Graph.java @@ -1,13 +1,27 @@ package org.itmo; -import java.util.*; -import java.util.concurrent.*; +import java.util.HashMap; +import java.util.List; +import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; class Graph { + + private static int THREAD_COUNT = 4; + + private static Queue[] batches; + private final int V; - private final ArrayList[] adjList; + private final List[] adjList; + + public Map visitedMap; Graph(int vertices) { this.V = vertices; @@ -23,7 +37,64 @@ void addEdge(int src, int dest) { } } - void parallelBFS(int startVertex) { + void parallelBFS(int startVertex, int threadCount) { + THREAD_COUNT = threadCount; + batches = new LinkedList[THREAD_COUNT]; + for (int i = 0; i < THREAD_COUNT; i++) { + batches[i] = new LinkedList<>(); + } + ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); + + AtomicBoolean[] visited = new AtomicBoolean[V]; + for (int i = 0; i < visited.length; i++) { + visited[i] = new AtomicBoolean(false); + } + + Queue queue = new ConcurrentLinkedQueue<>(); + + queue.add(startVertex); + visited[startVertex].set(true); + + while (!queue.isEmpty()) { + List> tasks = getCurrentLevelTasks(queue, visited); + + try { + if (!tasks.isEmpty()) executorService.invokeAll(tasks); + } catch (InterruptedException e) { + throw new RuntimeException("Parallel BFS failed...", e); + } + } + + executorService.shutdown(); + } + + private List> getCurrentLevelTasks(Queue queue, AtomicBoolean[] visited) { + int size = queue.size(); + int batchSize = size / THREAD_COUNT > 0 ? size / THREAD_COUNT : size; + for (int i = 0; i < size; i++) { + int index = i / batchSize >= batches.length ? batches.length - 1 : i / batchSize; + batches[index].add(queue.poll()); + } + return formTasks(queue, visited); + } + + private List> formTasks(Queue queue, AtomicBoolean[] visited) { + List> tasks = new ArrayList<>(); + for (Queue batch : batches) { + tasks.add(() -> { + int startVertex; + while (!batch.isEmpty()) { + startVertex = batch.poll(); + for (int n : adjList[startVertex]) { + if (visited[n].compareAndSet(false, true)) { + queue.add(n); + } + } + } + return null; + }); + } + return tasks; } //Generated by ChatGPT @@ -47,4 +118,71 @@ void bfs(int startVertex) { } } + void parallelBfsForTest(int startVertex, int threadCount) { + THREAD_COUNT = threadCount; + batches = new LinkedList[THREAD_COUNT]; + for (int i = 0; i < THREAD_COUNT; i++) { + batches[i] = new LinkedList<>(); + } + ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); + + visitedMap = new HashMap<>(V); + + Queue queue = new ConcurrentLinkedQueue<>(); + + queue.add(startVertex); + visitedMap.put(startVertex, 1); + + while (!queue.isEmpty()) { + List> tasks = getCurrentLevelTasksForTest(queue, visitedMap); + + try { + if (!tasks.isEmpty()) executorService.invokeAll(tasks); + } catch (InterruptedException e) { + throw new RuntimeException("Parallel BFS failed...", e); + } + } + + executorService.shutdown(); + } + + private List> getCurrentLevelTasksForTest(Queue queue, Map visitedMap) { + int size = queue.size(); + int batchSize = size / THREAD_COUNT > 0 ? size / THREAD_COUNT : size; + for (int i = 0; i < size; i++) { + int index = i / batchSize >= batches.length ? batches.length - 1 : i / batchSize; + batches[index].add(queue.poll()); + } + return formTasksForTest(queue, visitedMap); + } + + private List> formTasksForTest(Queue queue, Map visitedMap) { + List> tasks = new ArrayList<>(); + for (Queue batch : batches) { + tasks.add(() -> { + int startVertex; + while (!batch.isEmpty()) { + startVertex = batch.poll(); + + for (int n : adjList[startVertex]) { + // non-valid + if (!visitedMap.containsKey(n)) { + visitedMap.put(n, 0); + Integer counter = visitedMap.get(n); + counter++; + visitedMap.put(n, counter); + queue.add(n); + } else { + Integer counter = visitedMap.get(n); + counter++; + visitedMap.put(n, counter); + } + // non-valid + } + } + return null; + }); + } + return tasks; + } } diff --git a/src/main/java/org/itmo/UnsafeCounter.java b/src/main/java/org/itmo/UnsafeCounter.java new file mode 100644 index 0000000..1041a21 --- /dev/null +++ b/src/main/java/org/itmo/UnsafeCounter.java @@ -0,0 +1,13 @@ +package org.itmo; + +public class UnsafeCounter { + private int counter = 0; + + public void increment() { + counter++; // <-- гонка данных + } + + public int get() { + return counter; + } +} diff --git a/src/test/java/org/itmo/BFSTest.java b/src/test/java/org/itmo/BFSTest.java index 7bf9098..22ed23b 100644 --- a/src/test/java/org/itmo/BFSTest.java +++ b/src/test/java/org/itmo/BFSTest.java @@ -4,34 +4,108 @@ import java.io.FileWriter; import java.io.IOException; -import java.nio.Buffer; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; import java.util.Random; -import java.util.function.BiFunction; -import java.util.stream.IntStream; public class BFSTest { @Test - public void bfsTest() throws IOException { - int[] sizes = new int[]{10, 100, 1000, 10_000, 10_000, 50_000, 100_000, 1_000_000, 2_000_000}; - int[] connections = new int[]{50, 500, 5000, 50_000, 100_000, 1_000_000, 1_000_000, 10_000_000, 10_000_000}; - Random r = new Random(42); - try (FileWriter fw = new FileWriter("tmp/results.txt")) { - for (int i = 0; i < sizes.length; i++) { - System.out.println("--------------------------"); - System.out.println("Generating graph of size " + sizes[i] + " ...wait"); - Graph g = new RandomGraphGenerator().generateGraph(r, sizes[i], connections[i]); - System.out.println("Generation completed!\nStarting bfs"); - long serialTime = executeSerialBfsAndGetTime(g); - long parallelTime = executeParallelBfsAndGetTime(g); - fw.append("Times for " + sizes[i] + " vertices and " + connections[i] + " connections: "); - fw.append("\nSerial: " + serialTime); - fw.append("\nParallel: " + parallelTime); - fw.append("\n--------\n"); + public void bfsNonValidTest() throws IOException { + bfsNonValidTest(1, 4); + } + + @Test + public void bfsTestSoloTest() throws IOException { + bfsTestBase(1, 4); + } + + @Test + public void bfsTestSomeTests() throws IOException { + bfsTestBase(10, 4); + } + + @Test + public void bfsTestSomeTestsOnSeveralThreads() throws IOException { + int triesCount = 1; + int[] threadAmount = new int[]{1, 2, 4, 8, 16, 32, 64}; + for (int threadsToTest : threadAmount) { + bfsTestBase(triesCount, threadsToTest); + } + } + + private void bfsTestBase(int tries, int threads) throws IOException { + List> overAllSerialStats = new ArrayList<>(); + List> overAllParallelStats = new ArrayList<>(); + for (int j = 0; j < tries; j++) { + int[] sizes = new int[]{10, 100, 1000, 10_000, 10_000, 50_000, 100_000, 1_000_000, 2_000_000}; + int[] connections = new int[]{50, 500, 5000, 50_000, 100_000, 1_000_000, 1_000_000, 10_000_000, 10_000_000}; + Random r = new Random(42); + List currentSerialTimes = new ArrayList<>(); + List currentParallelTimes = new ArrayList<>(); + overAllSerialStats.add(currentSerialTimes); + overAllParallelStats.add(currentParallelTimes); + try (FileWriter fw = new FileWriter("tmp/results_" + threads + "_threads" + "_" + j + ".txt")) { + for (int i = 0; i < sizes.length; i++) { + System.out.println("--------------------------"); + System.out.println("Try number: " + j); + System.out.println("On threads: " + threads); + System.out.println("Generating graph of size " + sizes[i] + " ...wait"); + Graph g = new RandomGraphGenerator().generateGraph(r, sizes[i], connections[i]); + System.out.println("Generation completed!\nStarting bfs"); + long serialTime = executeSerialBfsAndGetTime(g); + currentSerialTimes.add(serialTime); + long parallelTime = executeParallelBfsAndGetTime(g, threads); + currentParallelTimes.add(parallelTime); + fw.append("Times for " + sizes[i] + " vertices and " + connections[i] + " connections: "); + fw.append("\nSerial: " + serialTime); + fw.append("\nParallel: " + parallelTime); + fw.append("\n--------\n"); + } + fw.append("Overall:\nSerial: " + currentSerialTimes + "\nParallel: " + currentParallelTimes); + fw.flush(); + } + } + System.out.println(overAllSerialStats); + System.out.println("---------------------"); + System.out.println(overAllParallelStats); + } + + private void bfsNonValidTest(int tries, int threads) throws IOException { + List> overAllSerialStats = new ArrayList<>(); + List> overAllParallelStats = new ArrayList<>(); + for (int j = 0; j < tries; j++) { + int[] sizes = new int[]{10, 100, 1000, 10_000, 10_000, 50_000, 100_000, 1_000_000}; + int[] connections = new int[]{50, 500, 5000, 50_000, 100_000, 1_000_000, 1_000_000, 10_000_000}; + Random r = new Random(42); + List currentSerialTimes = new ArrayList<>(); + List currentParallelTimes = new ArrayList<>(); + overAllSerialStats.add(currentSerialTimes); + overAllParallelStats.add(currentParallelTimes); + try (FileWriter fw = new FileWriter("tmp/results_" + threads + "_threads" + "_" + j + ".txt")) { + for (int i = 0; i < sizes.length; i++) { + System.out.println("--------------------------"); + System.out.println("Try number: " + j); + System.out.println("On threads: " + threads); + System.out.println("Generating graph of size " + sizes[i] + " ...wait"); + Graph g = new RandomGraphGenerator().generateGraph(r, sizes[i], connections[i]); + System.out.println("Generation completed!\nStarting bfs"); + long serialTime = executeSerialBfsAndGetTime(g); + currentSerialTimes.add(serialTime); + long parallelTime = executeParallelBfsAndGetTimeNonValid(g, threads); + currentParallelTimes.add(parallelTime); + fw.append("Times for " + sizes[i] + " vertices and " + connections[i] + " connections: "); + fw.append("\nSerial: " + serialTime); + fw.append("\nParallel: " + parallelTime); + fw.append("\n--------\n"); + } + fw.append("Overall:\nSerial: " + currentSerialTimes + "\nParallel: " + currentParallelTimes); + fw.flush(); } - fw.flush(); } + System.out.println(overAllSerialStats); + System.out.println("---------------------"); + System.out.println(overAllParallelStats); } @@ -42,11 +116,17 @@ private long executeSerialBfsAndGetTime(Graph g) { return endTime - startTime; } - private long executeParallelBfsAndGetTime(Graph g) { + private long executeParallelBfsAndGetTime(Graph g, int threadCount) { long startTime = System.currentTimeMillis(); - g.parallelBFS(0); + g.parallelBFS(0, threadCount); long endTime = System.currentTimeMillis(); return endTime - startTime; } + private long executeParallelBfsAndGetTimeNonValid(Graph g, int threadCount) { + long startTime = System.currentTimeMillis(); + g.parallelBfsForTest(0, threadCount); + long endTime = System.currentTimeMillis(); + return endTime - startTime; + } } diff --git a/src/test/java/org/itmo/JSStressGraphTest.java b/src/test/java/org/itmo/JSStressGraphTest.java new file mode 100644 index 0000000..7d1ca6e --- /dev/null +++ b/src/test/java/org/itmo/JSStressGraphTest.java @@ -0,0 +1,59 @@ +package org.itmo; + +import org.openjdk.jcstress.annotations.Actor; +import org.openjdk.jcstress.annotations.Arbiter; +import org.openjdk.jcstress.annotations.Expect; +import org.openjdk.jcstress.annotations.JCStressTest; +import org.openjdk.jcstress.annotations.Outcome; +import org.openjdk.jcstress.annotations.State; +import org.openjdk.jcstress.infra.results.I_Result; + +import java.util.Random; + +@JCStressTest +@Outcome(id = "0", expect = Expect.ACCEPTABLE, desc = "Каждое значение было валидно обработано только 1 раз") +@Outcome(id = "1", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Небольшое отклонение от нормы") +@Outcome(id = "2", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Немного больше небольшого отклонения от нормы") +@Outcome(id = "3", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Среднее отклонение от нормы") +@Outcome(id = "4", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Большое отклонение от нормы") +@State +public class JSStressGraphTest { + + private static final int actorsCount = 4; + private final Graph graph; + + private final int size = 10; + + { + Random random = new Random(2); + int connections = 10; + graph = new RandomGraphGenerator().generateGraph(random, size, connections); + } + + @Actor public void actor1() { graph.parallelBfsForTest(0, 1); } + @Actor public void actor2() { graph.parallelBfsForTest(0, 1); } + @Actor public void actor3() { graph.parallelBfsForTest(0, 1); } + @Actor public void actor4() { graph.parallelBfsForTest(0, 1); } + + @Arbiter + public void arbiter(I_Result r) { + int visitCounter = 0; + for (Integer i : graph.visitedMap.values()) { + visitCounter += actorsCount * i; + } + int abs = Math.abs(visitCounter - size); + if (abs == size) { + r.r1 = 0; + } else { + if (abs > size && abs < size * 1.5) { + r.r1 = 1; + } else if (abs < size * 2.5) { + r.r1 = 2; + } else if (abs < size * 3.5) { + r.r1 = 3; + } else { + r.r1 = 4; + } + } + } +} diff --git a/src/test/java/org/itmo/RandomGraphGenerator.java b/src/test/java/org/itmo/RandomGraphGenerator.java index fdb888c..1a57226 100644 --- a/src/test/java/org/itmo/RandomGraphGenerator.java +++ b/src/test/java/org/itmo/RandomGraphGenerator.java @@ -1,7 +1,9 @@ package org.itmo; import java.util.Arrays; +import java.util.HashSet; import java.util.Random; +import java.util.Set; import java.util.SplittableRandom; import java.util.concurrent.ForkJoinPool; import java.util.stream.IntStream; @@ -11,22 +13,27 @@ public class RandomGraphGenerator { private long pack(int u, int v) { return (((long) u) << 32) | (v & 0xffffffffL); } + private int unpackU(long key) { return (int) (key >>> 32); } + private int unpackV(long key) { return (int) (key & 0xffffffffL); } Graph generateGraph(Random r, int size, int numEdges) { + if (size < 1) throw new IllegalArgumentException("size must be >= 1"); 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"); - int[] perm = java.util.stream.IntStream.range(0, size).toArray(); - for (int i = size - 1; i > 1; i--) { - int j = 1 + r.nextInt(i); - int tmp = perm[i]; perm[i] = perm[j]; perm[j] = tmp; + int[] perm = IntStream.range(0, size).toArray(); + for (int i = size - 1; i > 0; i--) { + int j = r.nextInt(i + 1); + int tmp = perm[i]; + perm[i] = perm[j]; + perm[j] = tmp; } final int chainCount = size - 1; @@ -74,7 +81,7 @@ Graph generateGraph(Random r, int size, int numEdges) { while (unique < numEdges) { int missing = numEdges - unique; - int extra = Math.max(missing / 2, 10_000); // небольшой запас + int extra = Math.max(missing / 2, 10_000); int add = missing + extra; long[] more = new long[unique + add]; @@ -109,6 +116,31 @@ Graph generateGraph(Random r, int size, int numEdges) { keys = more; } + Set chainSet = new HashSet<>(chainCount * 2); + for (int i = 1; i < size; i++) { + chainSet.add(pack(perm[i - 1], perm[i])); + } + + int p = 0; + for (int i = 0; i < unique && p < chainCount; i++) { + long e = keys[i]; + if (chainSet.remove(e)) { + // swap keys[p] и keys[i] + long tmp = keys[p]; + keys[p] = keys[i]; + keys[i] = tmp; + p++; + } + } + + SplittableRandom shuf = base.split(); + for (int i = p; i < numEdges; i++) { + int j = i + shuf.nextInt(unique - i); + long tmp = keys[i]; + keys[i] = keys[j]; + keys[j] = tmp; + } + Graph g = new Graph(size); for (int i = 0; i < numEdges; i++) { long key = keys[i]; @@ -118,5 +150,4 @@ Graph generateGraph(Random r, int size, int numEdges) { } return g; } - -} +} \ No newline at end of file diff --git a/src/test/java/org/itmo/UnsafeCounterTest.java b/src/test/java/org/itmo/UnsafeCounterTest.java new file mode 100644 index 0000000..c0fa97d --- /dev/null +++ b/src/test/java/org/itmo/UnsafeCounterTest.java @@ -0,0 +1,27 @@ +package org.itmo; + +import org.openjdk.jcstress.annotations.*; +import org.openjdk.jcstress.infra.results.I_Result; + +//@JCStressTest +//@Outcome(id = "5", expect = Expect.ACCEPTABLE, desc = "Все 5 инкрементов выполнены корректно") +//@Outcome(id = "1", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Гонка данных: часть инкрементов потерялась") +//@Outcome(id = "2", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Гонка данных: часть инкрементов потерялась") +//@Outcome(id = "3", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Гонка данных: часть инкрементов потерялась") +//@Outcome(id = "4", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Гонка данных: часть инкрементов потерялась") +//@State +public class UnsafeCounterTest { + + private UnsafeCounter counter = new UnsafeCounter(); + +// @Actor public void actor1() { counter.increment(); } +// @Actor public void actor2() { counter.increment(); } +// @Actor public void actor3() { counter.increment(); } +// @Actor public void actor4() { counter.increment(); } +// @Actor public void actor5() { counter.increment(); } + +// @Arbiter + public void arbiter(I_Result r) { + r.r1 = counter.get(); + } +} diff --git a/tmp/results.txt b/tmp/results.txt index 027e7f9..e69de29 100644 --- a/tmp/results.txt +++ b/tmp/results.txt @@ -1,32 +0,0 @@ -Times for 10 vertices and 50 connections: -Serial: 0 -Parallel: 0 --------- -Times for 100 vertices and 500 connections: -Serial: 0 -Parallel: 0 --------- -Times for 1000 vertices and 5000 connections: -Serial: 1 -Parallel: 0 --------- -Times for 10000 vertices and 50000 connections: -Serial: 3 -Parallel: 0 --------- -Times for 10000 vertices and 100000 connections: -Serial: 2 -Parallel: 0 --------- -Times for 50000 vertices and 1000000 connections: -Serial: 30 -Parallel: 0 --------- -Times for 100000 vertices and 1000000 connections: -Serial: 18 -Parallel: 0 --------- -Times for 1000000 vertices and 10000000 connections: -Serial: 307 -Parallel: 0 --------- diff --git a/tmp/results_16_threads_0.txt b/tmp/results_16_threads_0.txt new file mode 100644 index 0000000..58ee756 --- /dev/null +++ b/tmp/results_16_threads_0.txt @@ -0,0 +1,39 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 3 +-------- +Times for 100 vertices and 500 connections: +Serial: 0 +Parallel: 2 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 0 +Parallel: 2 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 3 +Parallel: 4 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 5 +Parallel: 4 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 45 +Parallel: 18 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 31 +Parallel: 23 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 1452 +Parallel: 319 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 2732 +Parallel: 2444 +-------- +Overall: +Serial: [0, 0, 0, 3, 5, 45, 31, 1452, 2732] +Parallel: [3, 2, 2, 4, 4, 18, 23, 319, 2444] \ No newline at end of file diff --git a/tmp/results_1_threads_0.txt b/tmp/results_1_threads_0.txt new file mode 100644 index 0000000..b1bddcc --- /dev/null +++ b/tmp/results_1_threads_0.txt @@ -0,0 +1,39 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 2 +-------- +Times for 100 vertices and 500 connections: +Serial: 0 +Parallel: 1 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 2 +Parallel: 3 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 17 +Parallel: 18 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 14 +Parallel: 13 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 24 +Parallel: 33 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 83 +Parallel: 81 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 1480 +Parallel: 755 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 2730 +Parallel: 2786 +-------- +Overall: +Serial: [0, 0, 2, 17, 14, 24, 83, 1480, 2730] +Parallel: [2, 1, 3, 18, 13, 33, 81, 755, 2786] \ No newline at end of file diff --git a/tmp/results_2_threads_0.txt b/tmp/results_2_threads_0.txt new file mode 100644 index 0000000..ec02038 --- /dev/null +++ b/tmp/results_2_threads_0.txt @@ -0,0 +1,39 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 1 +-------- +Times for 100 vertices and 500 connections: +Serial: 0 +Parallel: 1 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 1 +Parallel: 2 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 3 +Parallel: 9 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 4 +Parallel: 8 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 25 +Parallel: 22 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 82 +Parallel: 63 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 1451 +Parallel: 833 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 2731 +Parallel: 2170 +-------- +Overall: +Serial: [0, 0, 1, 3, 4, 25, 82, 1451, 2731] +Parallel: [1, 1, 2, 9, 8, 22, 63, 833, 2170] \ No newline at end of file diff --git a/tmp/results_32_threads_0.txt b/tmp/results_32_threads_0.txt new file mode 100644 index 0000000..f40073b --- /dev/null +++ b/tmp/results_32_threads_0.txt @@ -0,0 +1,39 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 3 +-------- +Times for 100 vertices and 500 connections: +Serial: 0 +Parallel: 4 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 1 +Parallel: 4 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 2 +Parallel: 5 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 4 +Parallel: 5 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 58 +Parallel: 20 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 45 +Parallel: 25 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 939 +Parallel: 789 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 2703 +Parallel: 2483 +-------- +Overall: +Serial: [0, 0, 1, 2, 4, 58, 45, 939, 2703] +Parallel: [3, 4, 4, 5, 5, 20, 25, 789, 2483] \ No newline at end of file diff --git a/tmp/results_4_threads_0.txt b/tmp/results_4_threads_0.txt new file mode 100644 index 0000000..3ba8811 --- /dev/null +++ b/tmp/results_4_threads_0.txt @@ -0,0 +1,35 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 7 +-------- +Times for 100 vertices and 500 connections: +Serial: 1 +Parallel: 7 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 3 +Parallel: 8 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 23 +Parallel: 34 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 20 +Parallel: 30 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 34 +Parallel: 73 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 105 +Parallel: 132 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 2007 +Parallel: 4652 +-------- +Overall: +Serial: [0, 1, 3, 23, 20, 34, 105, 2007] +Parallel: [7, 7, 8, 34, 30, 73, 132, 4652] \ No newline at end of file diff --git a/tmp/results_64_threads_0.txt b/tmp/results_64_threads_0.txt new file mode 100644 index 0000000..02558af --- /dev/null +++ b/tmp/results_64_threads_0.txt @@ -0,0 +1,39 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 6 +-------- +Times for 100 vertices and 500 connections: +Serial: 0 +Parallel: 5 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 0 +Parallel: 33 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 3 +Parallel: 8 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 5 +Parallel: 8 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 19 +Parallel: 20 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 82 +Parallel: 46 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 1441 +Parallel: 297 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 3032 +Parallel: 2456 +-------- +Overall: +Serial: [0, 0, 0, 3, 5, 19, 82, 1441, 3032] +Parallel: [6, 5, 33, 8, 8, 20, 46, 297, 2456] \ No newline at end of file diff --git a/tmp/results_8_threads_0.txt b/tmp/results_8_threads_0.txt new file mode 100644 index 0000000..29ee81a --- /dev/null +++ b/tmp/results_8_threads_0.txt @@ -0,0 +1,39 @@ +Times for 10 vertices and 50 connections: +Serial: 0 +Parallel: 1 +-------- +Times for 100 vertices and 500 connections: +Serial: 0 +Parallel: 1 +-------- +Times for 1000 vertices and 5000 connections: +Serial: 0 +Parallel: 2 +-------- +Times for 10000 vertices and 50000 connections: +Serial: 3 +Parallel: 4 +-------- +Times for 10000 vertices and 100000 connections: +Serial: 4 +Parallel: 4 +-------- +Times for 50000 vertices and 1000000 connections: +Serial: 46 +Parallel: 17 +-------- +Times for 100000 vertices and 1000000 connections: +Serial: 30 +Parallel: 24 +-------- +Times for 1000000 vertices and 10000000 connections: +Serial: 1465 +Parallel: 331 +-------- +Times for 2000000 vertices and 10000000 connections: +Serial: 2714 +Parallel: 1896 +-------- +Overall: +Serial: [0, 0, 0, 3, 4, 46, 30, 1465, 2714] +Parallel: [1, 1, 2, 4, 4, 17, 24, 331, 1896] \ No newline at end of file