Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use nix
13 changes: 8 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ build/
!**/src/main/**/build/
!**/src/test/**/build/

.direnv/

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
.idea
*.iws
*.iml
*.ipr
Expand Down Expand Up @@ -39,4 +38,8 @@ bin/
.vscode/

### Mac OS ###
.DS_Store
.DS_Store

# JCStress
jcstress-results-*.bin.gz
results/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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: определение достижимости параллелизма и реализация параллельных алгоритмов.

Шаги выполнения:
Expand Down
16 changes: 15 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -24,4 +26,16 @@ kotlin {

application {
mainClass.set("MainKt")
}
}

tasks.register<JavaExec>("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())
}
}
22 changes: 22 additions & 0 deletions plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python3

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()
18 changes: 18 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
pkgs ? import <nixpkgs> { },
}:
pkgs.mkShellNoCC {
preferLocalBuild = true;
allowSubstitutes = false;

name = "par-prog-lab1";
packages = with pkgs; [
jdk8
(python3.withPackages (
p: with p; [
matplotlib
numpy
]
))
];
}
38 changes: 38 additions & 0 deletions src/main/java/MainKt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 < 5; i++) {
System.err.println("Warmup " + i);
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) {
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;
}
if (numEdges > 50_000_000) {
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);
}
}
}
}
24 changes: 24 additions & 0 deletions src/main/java/org/itmo/AtomicBooleanArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.itmo;

import java.util.Arrays;
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;
}

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;
}
}
71 changes: 65 additions & 6 deletions src/main/java/org/itmo/Graph.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
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;

class Graph {
public class Graph {
private final int V;
private final ArrayList<Integer>[] adjList;

Expand All @@ -23,11 +24,57 @@ void addEdge(int src, int dest) {
}
}

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);
ArrayList<Integer> queue = new ArrayList<>();
queue.add(startVertex);
try {
while (!queue.isEmpty()) {
// Can't use runnable because of the `pool.invokeAll` signature.
final List<Callable<List<Integer>>> 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<Integer> 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;
});
}
final List<Future<List<Integer>>> results = pool.invokeAll(tasks);
queue.clear();
for (Future<List<Integer>> result : results) {
assert result.isDone();
queue.addAll(result.get());
}
}
} 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<Integer> queue = new LinkedList<>();
Expand All @@ -45,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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
51 changes: 51 additions & 0 deletions src/test/java/org/itmo/BFSJCStressTest.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
30 changes: 26 additions & 4 deletions src/test/java/org/itmo/BFSTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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();
}
}
}
}
Loading