merge upstream
This commit is contained in:
137
Karakozov_Andrey_lab_1/MatrixSumOfProducts.java
Normal file
137
Karakozov_Andrey_lab_1/MatrixSumOfProducts.java
Normal file
@@ -0,0 +1,137 @@
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class MatrixSumOfProducts {
|
||||
|
||||
// Генерация матрицы
|
||||
public static int[][] generateMatrix(int rows, int cols, int minValue, int maxValue) {
|
||||
int[][] matrix = new int[rows][cols];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[i][j] = minValue + random.nextInt(maxValue - minValue + 1);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Однопоточный алгоритм
|
||||
public static BigInteger singleThreadedSum(int[][] matrix) {
|
||||
BigInteger totalSum = BigInteger.ZERO;
|
||||
for (int[] row : matrix) {
|
||||
BigInteger rowProduct = BigInteger.ONE;
|
||||
for (int value : row) {
|
||||
rowProduct = rowProduct.multiply(BigInteger.valueOf(value));
|
||||
}
|
||||
totalSum = totalSum.add(rowProduct);
|
||||
}
|
||||
return totalSum;
|
||||
}
|
||||
|
||||
// Параллельный алгоритм с ThreadPoolExecutor
|
||||
public static BigInteger multiThreadedSumWithThreadPool(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
AtomicReference<BigInteger> totalSum = new AtomicReference<>(BigInteger.ZERO);
|
||||
|
||||
int rowsPerThread = matrix.length / numThreads;
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
int startRow = i * rowsPerThread;
|
||||
int endRow = (i == numThreads - 1) ? matrix.length : startRow + rowsPerThread;
|
||||
|
||||
executor.submit(() -> {
|
||||
BigInteger localSum = BigInteger.ZERO;
|
||||
for (int row = startRow; row < endRow; row++) {
|
||||
BigInteger rowProduct = BigInteger.ONE;
|
||||
for (int value : matrix[row]) {
|
||||
rowProduct = rowProduct.multiply(BigInteger.valueOf(value));
|
||||
}
|
||||
localSum = localSum.add(rowProduct);
|
||||
}
|
||||
// Используем локальную копию переменной localSum
|
||||
BigInteger finalLocalSum = localSum;
|
||||
totalSum.updateAndGet(current -> current.add(finalLocalSum));
|
||||
});
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.MINUTES);
|
||||
return totalSum.get();
|
||||
}
|
||||
|
||||
// Параллельный алгоритм с ForkJoinPool
|
||||
public static BigInteger multiThreadedSumWithForkJoin(int[][] matrix) {
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
return forkJoinPool.invoke(new MatrixTask(matrix, 0, matrix.length));
|
||||
}
|
||||
|
||||
private static class MatrixTask extends RecursiveTask<BigInteger> {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
private static final int THRESHOLD = 100; // Порог для разделения задачи
|
||||
|
||||
public MatrixTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BigInteger compute() {
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
BigInteger localSum = BigInteger.ZERO;
|
||||
for (int row = startRow; row < endRow; row++) {
|
||||
BigInteger rowProduct = BigInteger.ONE;
|
||||
for (int value : matrix[row]) {
|
||||
rowProduct = rowProduct.multiply(BigInteger.valueOf(value));
|
||||
}
|
||||
localSum = localSum.add(rowProduct);
|
||||
}
|
||||
return localSum;
|
||||
} else {
|
||||
int mid = (startRow + endRow) / 2;
|
||||
MatrixTask leftTask = new MatrixTask(matrix, startRow, mid);
|
||||
MatrixTask rightTask = new MatrixTask(matrix, mid, endRow);
|
||||
leftTask.fork();
|
||||
BigInteger rightResult = rightTask.compute();
|
||||
BigInteger leftResult = leftTask.join();
|
||||
return leftResult.add(rightResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Основной метод
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException {
|
||||
int rows = 100;
|
||||
int cols = 100;
|
||||
int minValue = 1;
|
||||
int maxValue = 100;
|
||||
int numThreads = Runtime.getRuntime().availableProcessors(); // Используем все доступные ядра
|
||||
|
||||
// Генерация матрицы
|
||||
int[][] matrix = generateMatrix(rows, cols, minValue, maxValue);
|
||||
|
||||
// Однопоточный алгоритм
|
||||
long startTime = System.nanoTime();
|
||||
BigInteger singleThreadedResult = singleThreadedSum(matrix);
|
||||
long endTime = System.nanoTime();
|
||||
System.out.println("Single-threaded result: " + singleThreadedResult);
|
||||
System.out.println("Execution time (single-threaded): " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
// Параллельный алгоритм с ThreadPoolExecutor
|
||||
startTime = System.nanoTime();
|
||||
BigInteger multiThreadedResultThreadPool = multiThreadedSumWithThreadPool(matrix, numThreads);
|
||||
endTime = System.nanoTime();
|
||||
System.out.println("Multi-threaded result (ThreadPoolExecutor): " + multiThreadedResultThreadPool);
|
||||
System.out.println("Execution time (ThreadPoolExecutor): " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
// Параллельный алгоритм с ForkJoinPool
|
||||
startTime = System.nanoTime();
|
||||
BigInteger multiThreadedResultForkJoin = multiThreadedSumWithForkJoin(matrix);
|
||||
endTime = System.nanoTime();
|
||||
System.out.println("Multi-threaded result (ForkJoinPool): " + multiThreadedResultForkJoin);
|
||||
System.out.println("Execution time (ForkJoinPool): " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
}
|
||||
}
|
||||
62
Karakozov_Andrey_lab_1/readme.md
Normal file
62
Karakozov_Andrey_lab_1/readme.md
Normal file
@@ -0,0 +1,62 @@
|
||||
## Лабораторная работа №1.
|
||||
|
||||
|
||||
Разработка многопоточного приложения с использованием Java Concurrency
|
||||
согласно варианту задания.
|
||||
Необходимо:
|
||||
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2. Разработать параллельный вариант алгоритма с использованием
|
||||
ThreadPoolExecutor и замерить время его работы
|
||||
3. Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
|
||||
и замерить время его работы.
|
||||
ВАЖНО: Массив генерируется до работы всех вариантов алгоритмов. Все три
|
||||
алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
**Вариант 13**
|
||||
Определить сумму из произведений элементов каждой строки матрицы.
|
||||
|
||||
**Запуск программы**
|
||||
javac MatrixSumOfProducts.java
|
||||
java MatrixSumOfProducts
|
||||
|
||||
**Используемые технологии**
|
||||
*java.util.concurrent — библиотека для разработки многопоточных программ.
|
||||
*BigInteger — для работы с большими числами, чтобы избежать переполнения при умножении.
|
||||
|
||||
**Что делает программа**
|
||||
1. Генерирует матрицу заданного размера.
|
||||
|
||||
2. Вычисляет сумму произведений элементов каждой строки матрицы:
|
||||
|
||||
2.1 Однопоточным алгоритмом.
|
||||
|
||||
2.2 Параллельным алгоритмом с использованием ThreadPoolExecutor.
|
||||
|
||||
2.3 Параллельным алгоритмом с использованием ForkJoinPool.
|
||||
|
||||
3. Замеряет время выполнения каждого из алгоритмов.
|
||||
|
||||
**Тесты**
|
||||
```
|
||||
Размерность матрицы 100x100
|
||||
|
||||
Single-threaded time: 17 ms
|
||||
ThreadPoolExecutor time: 73 ms
|
||||
ForkJoinPool time: 36 ms
|
||||
```
|
||||
|
||||
```
|
||||
Размерность матрицы 1000x1000
|
||||
|
||||
Single-threaded time: 1639 ms
|
||||
ThreadPoolExecutor time: 1440 ms
|
||||
ForkJoinPool time: 464 ms
|
||||
```
|
||||
|
||||
### Вывод
|
||||
Для небольших задач реализация многопоточности может приводить к более худшему результату, так как ее реализация требует дополнительных затрат на создание и управление потоками. Однако при увеличении размера задачи многопоточные алгоритмы начинают показывать свою эффективность за счет параллельной обработки данных.
|
||||
|
||||
**Наблюдения:**
|
||||
1. **Однопоточный алгоритм** работает быстрее на небольших матрицах (например, 100x100), так как отсутствуют накладные расходы на создание и синхронизацию потоков.
|
||||
2. **ThreadPoolExecutor** показывает хорошие результаты на больших матрицах (например, 1000x1000), но его производительность может быть ниже, чем у `ForkJoinPool`, из-за менее эффективного распределения задач между потоками.
|
||||
3. **ForkJoinPool** демонстрирует наилучшую производительность на больших задачах, так как он оптимизирован для рекурсивного разделения задач и эффективного использования ресурсов процессора.
|
||||
@@ -0,0 +1,53 @@
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
|
||||
public class ForkJoinMatrixProcessor {
|
||||
private static class MatrixTask extends RecursiveAction {
|
||||
private final double[][] matrix;
|
||||
private final int startRow, endRow;
|
||||
private final double average;
|
||||
|
||||
MatrixTask(double[][] matrix, int startRow, int endRow, double average) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
this.average = average;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (endRow - startRow <= 10) { // Базовый случай: обрабатываем небольшой блок
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < matrix[i].length; j++) {
|
||||
matrix[i][j] /= average;
|
||||
}
|
||||
}
|
||||
} else { // Рекурсивный случай: разбиваем задачу на две части
|
||||
int mid = (startRow + endRow) / 2;
|
||||
invokeAll(
|
||||
new MatrixTask(matrix, startRow, mid, average),
|
||||
new MatrixTask(matrix, mid, endRow, average)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void process(double[][] matrix) {
|
||||
int rows = matrix.length;
|
||||
int cols = matrix[0].length;
|
||||
|
||||
// Вычисляем среднее арифметическое
|
||||
double sum = 0;
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
sum += matrix[i][j];
|
||||
}
|
||||
}
|
||||
double average = sum / (rows * cols);
|
||||
|
||||
// Создаем и запускаем задачу в ForkJoinPool
|
||||
ForkJoinPool pool = new ForkJoinPool();
|
||||
pool.invoke(new MatrixTask(matrix, 0, rows, average));
|
||||
pool.shutdown();
|
||||
}
|
||||
}
|
||||
38
bartasova_ksenia_lab_1/Lab_1_sspr/src/Main.java
Normal file
38
bartasova_ksenia_lab_1/Lab_1_sspr/src/Main.java
Normal file
@@ -0,0 +1,38 @@
|
||||
public class Main {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
int rows = 1000;
|
||||
int cols = 1000;
|
||||
double[][] matrix = MatrixGenerator.generateMatrix(rows, cols);
|
||||
|
||||
// Однопоточный вариант
|
||||
double[][] singleThreadedMatrix = copyMatrix(matrix);
|
||||
long startTime = System.nanoTime();
|
||||
SingleThreadedMatrixProcessor.process(singleThreadedMatrix);
|
||||
long endTime = System.nanoTime();
|
||||
System.out.println("Single-threaded: " + (endTime - startTime) / 1e9 + " сек");
|
||||
|
||||
// Параллельный вариант с ThreadPoolExecutor
|
||||
double[][] threadPoolMatrix = copyMatrix(matrix);
|
||||
startTime = System.nanoTime();
|
||||
ThreadPoolMatrixProcessor.process(threadPoolMatrix, 4); // 4 потока
|
||||
endTime = System.nanoTime();
|
||||
System.out.println("ThreadPoolExecutor: " + (endTime - startTime) / 1e9 + " сек");
|
||||
|
||||
// Параллельный вариант с ForkJoinPool
|
||||
double[][] forkJoinMatrix = copyMatrix(matrix);
|
||||
startTime = System.nanoTime();
|
||||
ForkJoinMatrixProcessor.process(forkJoinMatrix);
|
||||
endTime = System.nanoTime();
|
||||
System.out.println("ForkJoinPool: " + (endTime - startTime) / 1e9 + " сек");
|
||||
}
|
||||
|
||||
private static double[][] copyMatrix(double[][] matrix) {
|
||||
int rows = matrix.length;
|
||||
int cols = matrix[0].length;
|
||||
double[][] copy = new double[rows][cols];
|
||||
for (int i = 0; i < rows; i++) {
|
||||
System.arraycopy(matrix[i], 0, copy[i], 0, cols);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
14
bartasova_ksenia_lab_1/Lab_1_sspr/src/MatrixGenerator.java
Normal file
14
bartasova_ksenia_lab_1/Lab_1_sspr/src/MatrixGenerator.java
Normal file
@@ -0,0 +1,14 @@
|
||||
import java.util.Random;
|
||||
|
||||
public class MatrixGenerator {
|
||||
public static double[][] generateMatrix(int rows, int cols) {
|
||||
double[][] matrix = new double[rows][cols];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[i][j] = random.nextDouble() * 100; // Заполняем случайными числами
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
public class SingleThreadedMatrixProcessor {
|
||||
public static void process(double[][] matrix) {
|
||||
int rows = matrix.length;
|
||||
int cols = matrix[0].length;
|
||||
|
||||
// Вычисляем среднее арифметическое
|
||||
double sum = 0;
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
sum += matrix[i][j];
|
||||
}
|
||||
}
|
||||
double average = sum / (rows * cols);
|
||||
|
||||
// Делим каждый элемент на среднее
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[i][j] /= average;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ThreadPoolMatrixProcessor {
|
||||
public static void process(double[][] matrix, int threadPoolSize) throws InterruptedException {
|
||||
int rows = matrix.length;
|
||||
int cols = matrix[0].length;
|
||||
|
||||
// Вычисляем среднее арифметическое
|
||||
double sum = 0;
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
sum += matrix[i][j];
|
||||
}
|
||||
}
|
||||
double average = sum / (rows * cols);
|
||||
|
||||
// Создаем пул потоков
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
|
||||
|
||||
// Обрабатываем каждую строку в отдельном потоке
|
||||
for (int i = 0; i < rows; i++) {
|
||||
final int row = i;
|
||||
executor.submit(() -> {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[row][j] /= average;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Завершаем работу пула
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
39
bartasova_ksenia_lab_1/readme.md
Normal file
39
bartasova_ksenia_lab_1/readme.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Лабораторная работа №1
|
||||
|
||||
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
|
||||
Необходимо:
|
||||
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2. Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
|
||||
3. Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
|
||||
Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
## Вариант 3
|
||||
Разделить элементы матрицы на среднее арифметическое всех ее элементов.
|
||||
|
||||
### Как запустить лабораторную работу:
|
||||
javac Main.java компилирует исходный код
|
||||
java Main команда запускает скомпилированный класс
|
||||
|
||||
### Какие технологии использовали:
|
||||
java.util.concurrent используется для работы с многопоточностью и параллельными алгоритмами.
|
||||
- ExecutorService и Executors используются для управления пулами потоков и выполнения задач.
|
||||
- TimeUnit помогает работать с временными интервалами.
|
||||
- ForkJoinPool, RecursiveAction и RecursiveTask используются для параллельного выполнения задач, которые можно разбить на более мелкие подзадачи.
|
||||
|
||||
### Как работает программа:
|
||||
1. Генерируем матрицу размером 1000*1000
|
||||
2. Создаем три копии исходной матрицы
|
||||
3. Класс SingleThreadedMatrixProcessor обрабатывает матрицу в одном потоке
|
||||
4. Класс ThreadPoolMatrixProcessor обрабатывает матрицу с использованием пула потоков: создается пул потоков, каждая строка матрицы обрабатывается в отдельном потоке
|
||||
5. Класс ForkJoinMatrixProcessor обрабатывает матрицу с использованием ForkJoinPool и рекурсивного разделения задачи: если блок большой, он разделяется на две части, и каждая часть обрабатывается рекурсивно
|
||||
6. Выводятся результаты времени выполнения для каждого из трех подходов.
|
||||
### Тесты:
|
||||
Single-threaded: 0.015592711 сек
|
||||
ThreadPoolExecutor: 0.024355768 сек
|
||||
ForkJoinPool: 0.01131473 сек
|
||||
### Вывод:
|
||||
ForkJoinPool — самый быстрый. Однопоточный алгоритм — быстрее, чем ThreadPoolExecutor, но медленнее, чем ForkJoinPool. ThreadPoolExecutor — самый медленный в данном случае.
|
||||
|
||||
|
||||
|
||||
|
||||
113
buslaev_roman_lab_1/Main.java
Normal file
113
buslaev_roman_lab_1/Main.java
Normal file
@@ -0,0 +1,113 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
|
||||
public class Main {
|
||||
private static final int ROWS = 10000;
|
||||
private static final int COLS = 10000;
|
||||
private static final int THREADS = 4;
|
||||
|
||||
public static final long MaxValue = 1000000;
|
||||
|
||||
public static void main(String[] args) {
|
||||
long[][] matrix = generateMatrix(ROWS, COLS);
|
||||
|
||||
long start = System.nanoTime();
|
||||
long minSingle = findMinSingleThread(matrix);
|
||||
long end = System.nanoTime();
|
||||
System.out.println("Single: Min number - " + minSingle + ", Time: " + (end - start) / 1000000 + " мс");
|
||||
|
||||
start = System.nanoTime();
|
||||
long minThreadPool = findMinThreadPool(matrix);
|
||||
end = System.nanoTime();
|
||||
System.out.println("ThreadPoolExecutor: Min number - " + minThreadPool + ", Time: " + (end - start) / 1000000 + " мс");
|
||||
|
||||
start = System.nanoTime();
|
||||
long minForkJoin = findMinForkJoin(matrix);
|
||||
end = System.nanoTime();
|
||||
System.out.println("ForkJoinPool: Min number - " + minForkJoin + ", Time: " + (end - start) / 1000000 + " мс");
|
||||
}
|
||||
|
||||
private static long[][] generateMatrix(int rows, int cols) {
|
||||
Random rand = new Random();
|
||||
long[][] matrix = new long[rows][cols];
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[i][j] = rand.nextLong(MaxValue);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private static long findMinSingleThread(long[][] matrix) {
|
||||
long my_min = matrix[0][0];
|
||||
for(int i = 0; i < matrix.length; i++){
|
||||
my_min = min(my_min, findMinInRaw(matrix[i]));
|
||||
}
|
||||
return my_min;
|
||||
}
|
||||
|
||||
private static long findMinInRaw(long[] arr){
|
||||
long my_min = arr[0];
|
||||
for(int i = 1; i < arr.length; i++){
|
||||
my_min = min(my_min, arr[i]);
|
||||
}
|
||||
return my_min;
|
||||
}
|
||||
|
||||
private static long findMinThreadPool(long[][] matrix) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
|
||||
List<Future<Long>> results = new ArrayList<>();
|
||||
|
||||
for (long[] row : matrix) {
|
||||
results.add(executor.submit(() -> findMinInRaw(row)));
|
||||
}
|
||||
|
||||
long min = MaxValue;
|
||||
for (Future<Long> future : results) {
|
||||
try {
|
||||
min = min(min, future.get());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
executor.shutdown();
|
||||
return min;
|
||||
}
|
||||
|
||||
private static long findMinForkJoin(long[][] matrix) {
|
||||
ForkJoinPool pool = new ForkJoinPool();
|
||||
return pool.invoke(new MinTask(matrix, 0, matrix.length));
|
||||
}
|
||||
|
||||
static class MinTask extends RecursiveTask<Long> {
|
||||
private static final int MAX_ROWS = 1000;
|
||||
private final long[][] matrix;
|
||||
private final int startRow, endRow;
|
||||
|
||||
MinTask(long[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (endRow - startRow <= MAX_ROWS) {
|
||||
long my_min = Long.MAX_VALUE;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
my_min = min(my_min, findMinInRaw(matrix[i]));
|
||||
}
|
||||
return my_min;
|
||||
}
|
||||
int mid = (startRow + endRow) / 2;
|
||||
MinTask leftTask = new MinTask(matrix, startRow, mid);
|
||||
MinTask rightTask = new MinTask(matrix, mid, endRow);
|
||||
invokeAll(leftTask, rightTask);
|
||||
return min(leftTask.join(), rightTask.join());
|
||||
}
|
||||
}
|
||||
}
|
||||
77
buslaev_roman_lab_1/README.md
Normal file
77
buslaev_roman_lab_1/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Лабораторная работа №1
|
||||
|
||||
Разработка многопоточного приложения с использованием Java Concurrency.
|
||||
Мой вариант -> 4) Определить минимальный элемент матрицы.
|
||||
|
||||
## Цель
|
||||
|
||||
Сравнить время выполнения трех реализаций алгоритма поиска минимального элемента в большой матрице:
|
||||
1. Однопоточный алгоритм.
|
||||
2. Многопоточный алгоритм с использованием `ThreadPoolExecutor`.
|
||||
3. Многопоточный алгоритм с использованием `ForkJoinPool`.
|
||||
|
||||
Все три алгоритма обрабатывают один и тот же массив, который генерируется один раз перед запуском.
|
||||
|
||||
---
|
||||
|
||||
## Описание программы
|
||||
|
||||
1. **Генерация матрицы**
|
||||
Метод `generateMatrix(...)` создает матрицу размером `ROWS x COLS`, заполняя ее случайными числами, используя `Random.nextLong(MaxValue)`.
|
||||
|
||||
2. **Однопоточный поиск минимума**
|
||||
- `findMinSingleThread(...)` обходит все строки и для каждой строки вызывает `findMinInRaw(...)` (локальный поиск минимума в одномерном массиве).
|
||||
- Возвращается общий минимальный элемент по всей матрице.
|
||||
|
||||
3. **Поиск минимума с `ThreadPoolExecutor`**
|
||||
- `findMinThreadPool(...)` создает пул потоков из `THREADS`.
|
||||
- Для каждой строки матрицы отправляет задачу (`Callable<Long>`), которая в свою очередь вызывает `findMinInRaw(...)` для поиска минимума в одной строке.
|
||||
- Собираются результаты всех `Future<Long>`, ищется минимальный элемент из их значений.
|
||||
|
||||
4. **Поиск минимума с `ForkJoinPool`**
|
||||
- `findMinForkJoin(...)` создает `ForkJoinPool` и запускает рекурсивную задачу `MinTask`.
|
||||
- `MinTask` (наследует `RecursiveTask<Long>`) делит промежуток строк на две части, пока не достигнет порога `MAX_ROWS`. Когда участок достаточно мал, последовательно ищет минимум по строкам.
|
||||
- Объединяет (берет минимум) из двух подзадач.
|
||||
|
||||
5. **Замер времени**
|
||||
- В `main(...)` для каждого из трех алгоритмов замеряется время до и после выполнения (через `System.nanoTime()`).
|
||||
- Результаты выводятся в консоль.
|
||||
|
||||
---
|
||||
|
||||
## Как запустить
|
||||
|
||||
1. Установить JDK (Java 8 или выше).
|
||||
2. Скомпилировать:
|
||||
```bash
|
||||
javac Main.java
|
||||
```
|
||||
3. Запустить:
|
||||
```bash
|
||||
java Main
|
||||
```
|
||||
4. По завершении вы увидите три строки с минимальным элементом и временем выполнения (в мс) для:
|
||||
- Однопоточного алгоритма
|
||||
- Алгоритма с `ThreadPoolExecutor`
|
||||
- Алгоритма с `ForkJoinPool`
|
||||
|
||||
---
|
||||
|
||||
## Пример вывода
|
||||
|
||||
```
|
||||
Single: Min number - 0, Time: 107 мс
|
||||
ThreadPoolExecutor: Min number - 0, Time: 102 мс
|
||||
ForkJoinPool: Min number - 0, Time: 63 мс
|
||||
```
|
||||
|
||||
Числа зависят от размера матрицы и мощности процессора.
|
||||
Чтобы было более наглядно видно разницу по времени между всеми реализации алгоритма, нужно более большую матрицу.
|
||||
|
||||
---
|
||||
|
||||
## Вывод
|
||||
1. **Однопоточный** вариант прост в реализации, но при больших размерах матрицы работает долго.
|
||||
2. **ThreadPoolExecutor** позволяет распараллелить обработку строк матрицы и значительно ускорить поиск.
|
||||
3. **ForkJoinPool** часто дает еще более высокое ускорение за счет механизма рекурсивного разбиения задач, но реальная производительность зависит от конфигурации процессоров и параметров разбиения.
|
||||
|
||||
77
buslaev_roman_lab_2/MPIMinimumMatrix.java
Normal file
77
buslaev_roman_lab_2/MPIMinimumMatrix.java
Normal file
@@ -0,0 +1,77 @@
|
||||
import mpi.MPI;
|
||||
import java.util.Random;
|
||||
|
||||
public class MPIMinimumMatrix {
|
||||
private static final int SIZE = 3000;
|
||||
public static final int MaxValue = 10000;
|
||||
|
||||
public static void main(String[] args) {
|
||||
long startTime = System.nanoTime();
|
||||
|
||||
MPI.Init(args);
|
||||
int rank = MPI.COMM_WORLD.Rank();
|
||||
int MPIsize = MPI.COMM_WORLD.Size();
|
||||
|
||||
int[][] matrix2D = null;
|
||||
int[] flatMatrix = null;
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
|
||||
if (rank == 0) {
|
||||
matrix2D = generateMatrix(SIZE);
|
||||
flatMatrix = new int[SIZE * SIZE];
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
flatMatrix[i * SIZE + j] = matrix2D[i][j];
|
||||
}
|
||||
}
|
||||
System.out.println("[Container 1] Matrix generated. Sending data to container 2...");
|
||||
}
|
||||
|
||||
int rowsPerProcess = SIZE / MPIsize;
|
||||
int[] localRows = new int[rowsPerProcess * SIZE];
|
||||
|
||||
MPI.COMM_WORLD.Scatter(
|
||||
flatMatrix, 0, rowsPerProcess * SIZE, MPI.INT,
|
||||
localRows, 0, rowsPerProcess * SIZE, MPI.INT,
|
||||
0
|
||||
);
|
||||
|
||||
System.out.println("[Container " + rank + "] Received matrix size: " + rowsPerProcess + "x" + SIZE);
|
||||
|
||||
localMin = findMin(localRows);
|
||||
System.out.println("[Container " + rank + "] Local minimum: " + localMin);
|
||||
|
||||
int[] globalMin = new int[1];
|
||||
MPI.COMM_WORLD.Reduce(new int[]{localMin}, 0, globalMin, 0, 1, MPI.INT, MPI.MIN, 0);
|
||||
|
||||
if (rank == 0) {
|
||||
System.out.println("[Container 1] All minimums received. Global minimum: " + globalMin[0]);
|
||||
}
|
||||
|
||||
long endTime = System.nanoTime();
|
||||
if (rank == 0) {
|
||||
System.out.println("[Container 1] Time last: " + (endTime - startTime) / 1000000 + " ms");
|
||||
}
|
||||
|
||||
MPI.Finalize();
|
||||
}
|
||||
|
||||
private static int[][] generateMatrix(int size) {
|
||||
Random rand = new Random();
|
||||
int[][] matrix = new int[size][size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = rand.nextInt(MaxValue);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private static int findMin(int[] arr) {
|
||||
int my_min = arr[0];
|
||||
for(int i = 1; i < arr.length; i++){
|
||||
my_min = min(my_min, arr[i]);
|
||||
}
|
||||
return my_min;
|
||||
}
|
||||
}
|
||||
100
buslaev_roman_lab_2/README.md
Normal file
100
buslaev_roman_lab_2/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Лабораторная работа №2
|
||||
Разработка параллельного приложения с использованием MPI (Message Passing Interface) на языке Java.
|
||||
|
||||
## Вариант задания
|
||||
Определить минимальный элемент матрицы.
|
||||
|
||||
---
|
||||
|
||||
## Как запустить лабораторную работу
|
||||
1. **Установить Java** на каждом контейнере (например, OpenJDK 8+ или 17).
|
||||
2. **Установить библиотеку MPI** для Java. Используется MPJ Express:
|
||||
- Скачайте MPJ Express на все контейнеры.
|
||||
- Добавьте переменные окружения в `~/.bashrc`:
|
||||
```bash
|
||||
export MPJ_HOME=~/mpj
|
||||
export PATH=$PATH:$MPJ_HOME/bin
|
||||
export CLASSPATH=.:$MPJ_HOME/lib/mpj.jar
|
||||
```
|
||||
3. **Настройте SSH без пароля** между контейнерами, чтобы mpjrun или mpjdaemon могли подключаться друг к другу.
|
||||
4. **Отредактируйте** файл `machines`, вписав IP-адреса контейнеров (по одному в каждой строке):
|
||||
```
|
||||
192.168.4.104
|
||||
192.168.4.105
|
||||
```
|
||||
5. **Скомпилируйте код** на контейнере, откуда будете запускать:
|
||||
```bash
|
||||
javac -cp $MPJ_HOME/lib/mpj.jar:. MPIMinimumMatrix.java
|
||||
```
|
||||
6. **Скопируйте** файл `MPIMinimumMatrix.class` на остальные контейнеры в ту же директорию, чтобы все узлы имели доступ к коду.
|
||||
7. **Запустите демонов** (в кластере) на всех контейнерах:
|
||||
```bash
|
||||
mpjdaemon -boot 192.168.4.104 192.168.4.105
|
||||
```
|
||||
|
||||
8. **Запустите программу**:
|
||||
```bash
|
||||
./mpjrun.sh -np 2 -dev niodev -machinesfile ~/machines -cp . MPIMinimumMatrix
|
||||
```
|
||||
Здесь:
|
||||
- `-np 2` означает число процессов (должно совпадать с числом контейнеров или быть меньше/больше — тогда распределение строк меняется).
|
||||
- `-dev niodev` включает сетевой (кластерный) режим.
|
||||
- `-machinesfile ./machines` — указывает IP контейнеров.
|
||||
|
||||
---
|
||||
|
||||
## Какие технологии использовались
|
||||
- **Java** (JDK 8 или выше) для написания кода.
|
||||
- **MPJ Express** (v0_44) для реализации MPI-функций (`MPI.Init`, `MPI.COMM_WORLD.Scatter`, `MPI.COMM_WORLD.Reduce` и т.д.).
|
||||
|
||||
---
|
||||
|
||||
## Что делает программа
|
||||
1. Процесс с рангом 0 (**rank=0**) генерирует квадратную матрицу размера `SIZE x SIZE` (по умолчанию 3000 x 3000) в двумерном массиве `matrix2D`.
|
||||
2. Преобразует матрицу в одномерный массив `flatMatrix`.
|
||||
3. **Scatter**: Делит `flatMatrix` на фрагменты по строкам и рассылает их другим процессам (включая себя).
|
||||
4. Каждый узел получает часть строк, ищет минимум в своих элементах.
|
||||
5. **Reduce**: Узлы передают локальный минимум процессу 0, который находит глобальный минимум и выводит результат.
|
||||
6. Замеряет и выводит **время выполнения** (в миллисекундах).
|
||||
|
||||
---
|
||||
|
||||
## Пример тестов
|
||||
Вычисление минимального элемента для матрицы `3000 x 3000`.
|
||||
|
||||
### Распределение
|
||||
- Узел `rank=0` получает первые 1500 строки.
|
||||
- Узел `rank=1` получает последние 1500 строки.
|
||||
|
||||
### Локальный поиск минимума
|
||||
- На `rank=0`: минимум = 1
|
||||
- На `rank=1`: минимум = 0
|
||||
|
||||
### Глобальный минимум
|
||||
- `Reduce(MIN)` = 0
|
||||
|
||||
### Вывод
|
||||
```
|
||||
Starting process <0> on <CT104>
|
||||
Starting process <1> on <second-container>
|
||||
[Container 1] Matrix generated. Sending data to container 2...
|
||||
[Container 0] Received matrix size: 1500x3000
|
||||
[Container 1] Received matrix size: 1500x3000
|
||||
[Container 0] Local minimum: 1
|
||||
[Container 1] Local minimum: 0
|
||||
[Container 1] All minimums received. Global minimum: 0
|
||||
[Container 1] Time last: 3895 ms
|
||||
Stopping Process <0> on <CT104>
|
||||
Stopping Process <1> on <second-container>
|
||||
```
|
||||
|
||||
## Проблемы, с которыми столкнулись
|
||||
- **SSH-пароли**: Без настройки SSH-ключей mpjrun/daemon будет «виснуть», ожидая пароль.
|
||||
- **Разные подсети**: Если контейнеры в разных сетях (192.168.4.x и 192.168.5.x), нужно либо объединить их в одну сеть, либо настроить маршруты.
|
||||
- **Переменная окружения `MPJ_HOME`** не видна при запуске неинтерактивной SSH-сессии. Решение: прописать `MPJ_HOME` и `PATH` в `/etc/environment`.
|
||||
|
||||
---
|
||||
|
||||
## Вывод
|
||||
Используя MPI (MPJ Express) и несколько контейнеров, удалось распараллелить задачу поиска минимума по строкам матрицы. Каждый контейнер обрабатывает свой блок данных, а затем результат собирается через операцию `Reduce`. Подход хорошо масштабируется и демонстрирует, как распределённые процессы могут совместно решать одну задачу.
|
||||
|
||||
29
gorskov_evgeniu_lab_1/LabWork01/.gitignore
vendored
Normal file
29
gorskov_evgeniu_lab_1/LabWork01/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
### IntelliJ IDEA ###
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
0
gorskov_evgeniu_lab_1/LabWork01/matrix.txt
Normal file
0
gorskov_evgeniu_lab_1/LabWork01/matrix.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
package sspr1;
|
||||
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
|
||||
public class ForkJoinMaxFinder {
|
||||
private static class MaxFinderTask extends RecursiveTask<Integer> {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
public MaxFinderTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer compute() {
|
||||
if (endRow - startRow <= 1) {
|
||||
int max = Integer.MIN_VALUE;
|
||||
for (int j = startRow + 1; j < matrix.length; j++) {
|
||||
if (matrix[startRow][j] > max) {
|
||||
max = matrix[startRow][j];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
} else {
|
||||
int mid = (startRow + endRow) / 2;
|
||||
MaxFinderTask leftTask = new MaxFinderTask(matrix, startRow, mid);
|
||||
MaxFinderTask rightTask = new MaxFinderTask(matrix, mid, endRow);
|
||||
leftTask.fork();
|
||||
int rightResult = rightTask.compute();
|
||||
int leftResult = leftTask.join();
|
||||
return Math.max(leftResult, rightResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int findMax(int[][] matrix) {
|
||||
ForkJoinPool pool = new ForkJoinPool();
|
||||
return pool.invoke(new MaxFinderTask(matrix, 0, matrix.length));
|
||||
}
|
||||
}
|
||||
38
gorskov_evgeniu_lab_1/LabWork01/src/sspr1/Main.java
Normal file
38
gorskov_evgeniu_lab_1/LabWork01/src/sspr1/Main.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package sspr1;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException, IOException {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
System.out.print("Введите размер матрицы: ");
|
||||
int size = scanner.nextInt();
|
||||
scanner.close();
|
||||
|
||||
int[][] matrix = MatrixGeneration.generateMatrix(size);
|
||||
|
||||
// Запись матрицы в файл
|
||||
MatrixGeneration.writeMatrixToFile(matrix, "matrix.txt");
|
||||
System.out.println("Матрица записана в файл matrix.txt");
|
||||
|
||||
// Однопоточный алгоритм
|
||||
long startTime = System.currentTimeMillis();
|
||||
int singleThreadedMax = SingleThreadedMaxFinder.findMax(matrix);
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("Single-threaded max: " + singleThreadedMax + ", Time: " + (endTime - startTime) + " ms");
|
||||
|
||||
// Многопоточный алгоритм с ThreadPoolExecutor
|
||||
startTime = System.currentTimeMillis();
|
||||
int threadPoolMax = ThreadPoolMaxFinder.findMax(matrix, Runtime.getRuntime().availableProcessors());
|
||||
endTime = System.currentTimeMillis();
|
||||
System.out.println("ThreadPool max: " + threadPoolMax + ", Time: " + (endTime - startTime) + " ms");
|
||||
|
||||
// Многопоточный алгоритм с ForkJoinPool
|
||||
startTime = System.currentTimeMillis();
|
||||
int forkJoinMax = ForkJoinMaxFinder.findMax(matrix);
|
||||
endTime = System.currentTimeMillis();
|
||||
System.out.println("ForkJoin max: " + forkJoinMax + ", Time: " + (endTime - startTime) + " ms");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package sspr1;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
public class MatrixGeneration {
|
||||
public static int[][] generateMatrix(int size) {
|
||||
int[][] matrix = new int[size][size];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = random.nextInt(1000);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static void writeMatrixToFile(int[][] matrix, String filename) throws IOException {
|
||||
try (FileWriter writer = new FileWriter(filename)) {
|
||||
for (int[] row : matrix) {
|
||||
for (int value : row) {
|
||||
writer.write(value + " ");
|
||||
}
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package sspr1;
|
||||
|
||||
public class SingleThreadedMaxFinder {
|
||||
public static int findMax(int[][] matrix) {
|
||||
int max = -999999999;
|
||||
int size = matrix.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
if (matrix[i][j] > max) {
|
||||
max = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package sspr1;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class ThreadPoolMaxFinder {
|
||||
public static int findMax(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
|
||||
int size = matrix.length;
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
Future<Integer>[] futures = new Future[size];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int row = i;
|
||||
futures[i] = executor.submit(() -> {
|
||||
int max = Integer.MIN_VALUE;
|
||||
for (int j = row + 1; j < size; j++) {
|
||||
if (matrix[row][j] > max) {
|
||||
max = matrix[row][j];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
});
|
||||
}
|
||||
|
||||
int globalMax = Integer.MIN_VALUE;
|
||||
for (Future<Integer> future : futures) {
|
||||
int localMax = future.get();
|
||||
if (localMax > globalMax) {
|
||||
globalMax = localMax;
|
||||
}
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
return globalMax;
|
||||
}
|
||||
}
|
||||
82
gorskov_evgeniu_lab_1/readme.md
Normal file
82
gorskov_evgeniu_lab_1/readme.md
Normal file
@@ -0,0 +1,82 @@
|
||||
Лабораторная работа №1.
|
||||
|
||||
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
|
||||
|
||||
Необходимо выполнить следующие задачи:
|
||||
|
||||
Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
|
||||
Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
|
||||
|
||||
Вариант задания
|
||||
8)Найти максимальный элемент выше главной диагонали.
|
||||
|
||||
Как запустить программу
|
||||
Необходиом запустить башфайл, где уже прописаны необходимые настройки для запуска программы
|
||||
/root/sspr1.sh
|
||||
|
||||
Инструменты
|
||||
|
||||
Язык программирования: Java
|
||||
Пакеты:
|
||||
java.util.concurrent — используется для реализации многопоточности через ThreadPoolExecutor и ForkJoinPool
|
||||
Среда разработки: IntelliJ IDEA
|
||||
Версия JDK: 21
|
||||
|
||||
Описание работы программы
|
||||
|
||||
Генерация матрицы
|
||||
Программа генерирует случайную матрицу заданного размера (количество строк и столбцов передается как аргументы командной строки).
|
||||
Матрица заполняется случайными целыми числами.
|
||||
|
||||
Однопоточный алгоритм
|
||||
|
||||
Алгоритм проходит по всем элементам матрицы, находящимся выше главной диагонали.
|
||||
Для каждого элемента проверяется, больше ли он текущего максимума.
|
||||
Время выполнения зависит от размера матрицы и выполняется последовательно.
|
||||
|
||||
Многопоточный алгоритм с использованием ThreadPoolExecutor
|
||||
|
||||
Каждая строка матрицы обрабатывается в отдельном потоке.
|
||||
Используется пул потоков, где количество потоков равно количеству доступных ядер процессора.
|
||||
Каждый поток находит максимум в своей строке выше главной диагонали.
|
||||
После завершения всех потоков находится глобальный максимум.
|
||||
|
||||
Многопоточный алгоритм с использованием ForkJoinPool
|
||||
|
||||
Используется рекурсивное разделение задачи на подзадачи.
|
||||
Матрица делится на две части, и каждая часть обрабатывается отдельно.
|
||||
После завершения обработки всех частей находится глобальный максимум.
|
||||
Подходит для больших матриц, так как эффективно использует многопоточность.
|
||||
|
||||
Замер времени
|
||||
|
||||
Для каждого алгоритма замеряется время выполнения.
|
||||
Результаты выводятся на экран.
|
||||
|
||||
Результаты работы на матрице 10000 × 10000
|
||||
|
||||
Тест Однопоточный алгоритм ThreadPoolExecutor ForkJoinPool
|
||||
Тест №1 1615 мс 740 мс 1040 мс
|
||||
Тест №2 1789 мс 743 мс 1020 мс
|
||||
Тест №3 1613 мс 696 мс 1068 мс
|
||||
|
||||
Результаты работы на матрице 5000 × 5000
|
||||
|
||||
Тест Однопоточный алгоритм ThreadPoolExecutor ForkJoinPool
|
||||
Тест №1 420 мс 210 мс 280 мс
|
||||
Тест №2 435 мс 205 мс 275 мс
|
||||
Тест №3 410 мс 215 мс 290 мс
|
||||
|
||||
Результаты работы на матрице 2000 × 2000
|
||||
|
||||
Тест Однопоточный алгоритм ThreadPoolExecutor ForkJoinPool
|
||||
Тест №1 65 мс 35 мс 45 мс
|
||||
Тест №2 68 мс 34 мс 46 мс
|
||||
Тест №3 67 мс 36 мс 44 мс
|
||||
|
||||
Вывод
|
||||
|
||||
Для небольших матриц (например, 100 × 100) разница во времени выполнения может быть незначительной.
|
||||
Для больших матриц (например, 10000 × 10000) многопоточные подходы (ThreadPoolExecutor и ForkJoinPool) значительно ускоряют выполнение.
|
||||
Выбор между ThreadPoolExecutor и ForkJoinPool зависит от специфики задачи. Если задача легко делится на независимые части, лучше использовать ThreadPoolExecutor. Если задача требует рекурсивного разделения, подойдет ForkJoinPool.
|
||||
82
gprskov_evgeniu_lab_2/readme.md
Normal file
82
gprskov_evgeniu_lab_2/readme.md
Normal file
@@ -0,0 +1,82 @@
|
||||
Лабораторная работа №2
|
||||
|
||||
Разработка параллельного MPI приложения на языке Java.
|
||||
|
||||
Необходимо разработать параллельный вариант алгоритма с применением MPI и замерить время его работы. В рамках работы программы должно быть две копии приложения, которые соединяются друг с другом по сети. Сообщения о статусе соединения (например, что соединение установлено) должны выводиться в консоль. Запуск каждого экземпляра MPI происходит в своём LXC контейнере. Такие сервисы, как Docker, использовать нельзя.
|
||||
|
||||
Необходимо выполнить следующие задачи:
|
||||
|
||||
Разработать параллельный вариант алгоритма с использованием MPI.
|
||||
Замерить время его работы.
|
||||
Обеспечить взаимодействие двух копий приложения через сеть.
|
||||
Выводить сообщения о статусе соединения в консоль.
|
||||
Запускать каждый экземпляр MPI в отдельном LXC контейнере.
|
||||
|
||||
Вариант задания
|
||||
|
||||
8)Найти максимальный элемент выше главной диагонали
|
||||
|
||||
Как запустить программу
|
||||
|
||||
Необходимо запустить bash-файл, где уже прописаны необходимые настройки для запуска программы:
|
||||
/root/sspr2.sh
|
||||
|
||||
Инструменты
|
||||
|
||||
Язык программирования: Java
|
||||
Пакеты:
|
||||
mpi — используется для реализации параллельных вычислений с использованием MPI.
|
||||
Среда разработки: IntelliJ IDEA
|
||||
Версия JDK: 21
|
||||
|
||||
Описание работы программы
|
||||
|
||||
Генерация матрицы
|
||||
Программа генерирует случайную матрицу заданного размера (количество строк и столбцов передается как аргументы командной строки)
|
||||
|
||||
Матрица заполняется случайными целыми числами
|
||||
|
||||
Параллельный алгоритм с использованием MPI
|
||||
|
||||
Матрица разделяется на две части
|
||||
Каждая часть обрабатывается в отдельном LXC контейнере
|
||||
Каждый контейнер находит максимальный элемент выше главной диагонали в своей части матрицы
|
||||
Результаты передаются между контейнерами, и определяется глобальный максимум
|
||||
|
||||
Взаимодействие между контейнерами
|
||||
|
||||
Используется MPI для передачи данных между контейнерами
|
||||
Сообщения о статусе соединения (например, "Соединение установлено") выводятся в консоль
|
||||
|
||||
Замер времени
|
||||
|
||||
Для параллельного алгоритма замеряется время выполнения
|
||||
Результаты выводятся на экран
|
||||
|
||||
Результаты работы на матрице 10000 × 10000
|
||||
|
||||
Тест Время выполнения
|
||||
Тест №1 520 мс
|
||||
Тест №2 530 мс
|
||||
Тест №3 510 мс
|
||||
|
||||
Результаты работы на матрице 5000 × 5000
|
||||
|
||||
Тест Время выполнения
|
||||
Тест №1 130 мс
|
||||
Тест №2 135 мс
|
||||
Тест №3 128 мс
|
||||
|
||||
Результаты работы на матрице 2000 × 2000
|
||||
|
||||
Тест Время выполнения
|
||||
Тест №1 22 мс
|
||||
Тест №2 24 мс
|
||||
Тест №3 21 мс
|
||||
|
||||
Вывод
|
||||
|
||||
Параллельный алгоритм с использованием MPI показывает значительное ускорение по сравнению с однопоточным и многопоточными подходами (например, ThreadPoolExecutor и ForkJoinPool).
|
||||
Взаимодействие между контейнерами через MPI позволяет эффективно распределить нагрузку и ускорить выполнение задачи.
|
||||
Для больших матриц (например, 10000 × 10000) MPI демонстрирует высокую производительность благодаря параллельной обработке данных.
|
||||
Для небольших матриц (например, 2000 × 2000) время выполнения значительно меньше, но MPI все равно остается эффективным.
|
||||
83
gprskov_evgeniu_lab_2/sspr2/CTFirst.java
Normal file
83
gprskov_evgeniu_lab_2/sspr2/CTFirst.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package sspr2;
|
||||
|
||||
import mpi.MPI;
|
||||
import mpi.MPIException;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CTFirst {
|
||||
public static void main(String[] args) throws MPIException, IOException {
|
||||
try {
|
||||
MPI.Init(args);
|
||||
String name = MPI.Get_processor_name();
|
||||
int row = Integer.parseInt(args[args.length - 4]);
|
||||
int col = Integer.parseInt(args[args.length - 5]);
|
||||
if (row % 2 != 0 || col % 2 != 0) {
|
||||
System.out.println("Row or col not even");
|
||||
MPI.Finalize();
|
||||
return;
|
||||
}
|
||||
System.out.println("Row = " + row);
|
||||
System.out.println("Column = " + col);
|
||||
int[][] matrix = generateMatrix(row, col);
|
||||
String filenameMatrixStart = "/root/sspr2/res/matrixStart.txt";
|
||||
int[][] secondPartMatrix = new int[row / 2][col];
|
||||
int iterator = 0;
|
||||
for (int i = row / 2; i < row; i++) {
|
||||
System.arraycopy(matrix[i], 0, secondPartMatrix[iterator], 0, col);
|
||||
iterator++;
|
||||
}
|
||||
System.out.println("Matrix write to file with name '" + filenameMatrixStart + "'");
|
||||
writeMatrixByFile(filenameMatrixStart, matrix, row, col);
|
||||
int[] responseArray = ParserMPI.convertMatrixToArray(matrix, row / 2, col);
|
||||
int[] responseIndexColumn = SortMatrix.getIndexColumn(matrix, col);
|
||||
System.out.println(responseIndexColumn.length);
|
||||
int[] sizeMatrix = {row / 2, col};
|
||||
MPI.COMM_WORLD.Send(sizeMatrix, 0, 2, MPI.INT, 1, 0);
|
||||
System.out.println("Началась отправка массива с размерами с " + name + "процессора");
|
||||
MPI.COMM_WORLD.Send(responseArray, 0, responseArray.length, MPI.INT, 1, 0);
|
||||
System.out.println("Началась асинхронная отправка половины массива с " + name + " контейнера");
|
||||
int ourPartMax = SortMatrix.searchMaxAboveMainDiagonal(secondPartMatrix, 0, row / 2, col, name);
|
||||
int[] requestMax = new int[1];
|
||||
MPI.COMM_WORLD.Recv(requestMax, 0, 1, MPI.INT, 1, 0);
|
||||
int secondMax = requestMax[0];
|
||||
if(ourPartMax < secondMax) {
|
||||
System.out.println("Max element " + secondMax + " is greater than " + ourPartMax);
|
||||
}
|
||||
else {
|
||||
System.out.println("Max element " + ourPartMax + " is greater than " + secondMax);
|
||||
}
|
||||
MPI.Finalize();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static int[][] generateMatrix(int row, int col) {
|
||||
int[][] matrix = new int[row][col];
|
||||
for (int i = 0; i < row; i++) {
|
||||
for (int j = 0; j < col; j++) {
|
||||
matrix[i][j] = (int) (Math.random() * 100);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private static void writeMatrixByFile(String filename, int[][] matrix, int row, int col) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
FileWriter fw = new FileWriter(filename);
|
||||
fw.write(row + "\n");
|
||||
fw.write(col + "\n");
|
||||
for (int i = 0; i < row; i++) {
|
||||
for (int j = 0; j < col; j++) {
|
||||
String s = Math.round(matrix[i][j] * 100.0) / 100.0 + "";
|
||||
sb.append(s).append(" ");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
fw.write(sb.toString());
|
||||
fw.close();
|
||||
}
|
||||
|
||||
}
|
||||
24
gprskov_evgeniu_lab_2/sspr2/ParserMPI.java
Normal file
24
gprskov_evgeniu_lab_2/sspr2/ParserMPI.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package sspr2;
|
||||
|
||||
public class ParserMPI {
|
||||
|
||||
public static int[][] parseMatrix(int[] requestMatrix, int row, int col) {
|
||||
int iterator = 0;
|
||||
int[][] matrix = new int[row][col];
|
||||
for (int i = 0; i < row; i++) {
|
||||
System.arraycopy(requestMatrix, iterator, matrix[i], 0, col);
|
||||
iterator += col;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static int[] convertMatrixToArray(int[][] matrix, int row, int col) {
|
||||
int iterator = 0;
|
||||
int[] response = new int[row * col];
|
||||
for (int i = 0; i < row; i++) {
|
||||
System.arraycopy(matrix[i], 0, response, iterator, col);
|
||||
iterator += col;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
55
gprskov_evgeniu_lab_2/sspr2/SortMatrix.java
Normal file
55
gprskov_evgeniu_lab_2/sspr2/SortMatrix.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package sspr2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class SortMatrix {
|
||||
|
||||
public static int[][] sortMatrix(int[][] matrix, int startRow, int endRow, int column, int[] indexColumn, String name) {
|
||||
int[][] tempMatrix = new int[endRow][column];
|
||||
int iterator = 1;
|
||||
for (int i = 0; i < column; i++) {
|
||||
for (int j = startRow; j < endRow; j++) {
|
||||
tempMatrix[j][i] = matrix[j][indexColumn[i]];
|
||||
}
|
||||
iterator++;
|
||||
// System.out.println(name + "закончил обрабатывать " + iterator + " колонку");
|
||||
}
|
||||
System.out.println(name + "закончил обрабатывать целиком свою половину");
|
||||
return tempMatrix;
|
||||
}
|
||||
|
||||
public static int searchMaxAboveMainDiagonal(int[][] matrix, int startRow, int endRow, int column, String name) {
|
||||
int max = 0;
|
||||
System.out.println(name + "начал обрабатывать свою половину");
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = i; j < column; j++) {
|
||||
if (matrix[i][j] > max) {
|
||||
max = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(name + "закончил обрабатывать целиком свою половину");
|
||||
return max;
|
||||
}
|
||||
|
||||
public static int[] getIndexColumn(int[][] matrix, int column) {
|
||||
int[] maxElements = new int[column];
|
||||
for (int i = 0; i < column; i++) {
|
||||
for (int[] ints : matrix) {
|
||||
if (ints[i] > maxElements[i]) {
|
||||
maxElements[i] = ints[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
Integer[] indexColumn = new Integer[column];
|
||||
for (int i = 0; i < column; i++) {
|
||||
indexColumn[i] = i;
|
||||
}
|
||||
Arrays.sort(indexColumn, Comparator.comparingInt((Integer j) ->
|
||||
Arrays.stream(matrix).mapToInt(rows -> rows[j]).max().orElse(Integer.MIN_VALUE)).reversed()
|
||||
);
|
||||
return Arrays.stream(indexColumn).mapToInt(integer -> integer).toArray();
|
||||
}
|
||||
|
||||
}
|
||||
2
gprskov_evgeniu_lab_2/sspr2/hosts
Normal file
2
gprskov_evgeniu_lab_2/sspr2/hosts
Normal file
@@ -0,0 +1,2 @@
|
||||
192.168.28.250
|
||||
192.168.28.129
|
||||
12
gprskov_evgeniu_lab_2/sspr2/res/matrixStart.txt
Normal file
12
gprskov_evgeniu_lab_2/sspr2/res/matrixStart.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
10
|
||||
10
|
||||
27.0 30.0 99.0 59.0 17.0 41.0 80.0 53.0 68.0 68.0
|
||||
53.0 77.0 10.0 22.0 83.0 72.0 56.0 31.0 23.0 17.0
|
||||
28.0 23.0 88.0 31.0 60.0 82.0 49.0 87.0 18.0 67.0
|
||||
12.0 64.0 95.0 55.0 22.0 0.0 47.0 54.0 18.0 86.0
|
||||
65.0 36.0 45.0 93.0 92.0 79.0 22.0 71.0 25.0 82.0
|
||||
40.0 96.0 71.0 6.0 83.0 1.0 58.0 11.0 28.0 31.0
|
||||
30.0 40.0 7.0 19.0 4.0 28.0 6.0 71.0 13.0 84.0
|
||||
81.0 13.0 70.0 83.0 75.0 3.0 94.0 80.0 43.0 80.0
|
||||
20.0 78.0 6.0 81.0 42.0 57.0 49.0 74.0 26.0 55.0
|
||||
48.0 40.0 47.0 55.0 74.0 35.0 24.0 39.0 9.0 17.0
|
||||
227
khasyanov_aidar_lab_1/MinDivisionTask.java
Normal file
227
khasyanov_aidar_lab_1/MinDivisionTask.java
Normal file
@@ -0,0 +1,227 @@
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class MinDivisionTask {
|
||||
static double[][] mainArray = new double[4000][4000];
|
||||
static double[][] bufferArray = new double[mainArray.length][mainArray.length];
|
||||
static final int FRONTIER = 5000; // Порог разбиения
|
||||
static final int THREADS = 2; // Кол-во потоков
|
||||
static int countOftests = 20; // Кол-во тестов
|
||||
|
||||
public static void main(String[] args) {
|
||||
DoTests();
|
||||
}
|
||||
public static void DoTests() {
|
||||
long SingleThreadedTimeSum = 0;
|
||||
long ThreadPoolExecutorTimeSum = 0;
|
||||
long ForkJoinPoolTimeSum = 0;
|
||||
int count = 0;
|
||||
Random random = new Random();
|
||||
while (count != countOftests) {
|
||||
count += 1;
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) {
|
||||
mainArray[i][j] = random.nextInt(-100,100);
|
||||
bufferArray[i][j] = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
//PrintLogMatrix(mainArray); System.out.println();
|
||||
|
||||
// Однопоточный алгоритм
|
||||
long SingleThreadedTime = ComputeAndPrintDuration("SingleThreadedAlgorithm");
|
||||
SingleThreadedTimeSum += SingleThreadedTime;
|
||||
// Многопоточный алгоритм
|
||||
long ThreadPoolExecutorTime = ComputeAndPrintDuration("ThreadPoolExecutorAlgorithm");
|
||||
ThreadPoolExecutorTimeSum += ThreadPoolExecutorTime;
|
||||
// ForkJoinPool-алгоритм
|
||||
long ForkJoinPoolTime = ComputeAndPrintDuration("ForkJoinPoolAlgorithm");
|
||||
ForkJoinPoolTimeSum += ForkJoinPoolTime;
|
||||
|
||||
System.out.println("{Тест "+ count + "} " + "SingleThreadedTime: " + SingleThreadedTime +
|
||||
" ThreadPoolExecutorTime: " + ThreadPoolExecutorTime + " ForkJoinPoolTime: " + ForkJoinPoolTime);
|
||||
}
|
||||
System.out.println("SingleThreadedTime: " + SingleThreadedTimeSum/countOftests);
|
||||
System.out.println("ThreadPoolExecutorTime: " + ThreadPoolExecutorTimeSum/countOftests);
|
||||
System.out.println("ForkJoinPoolTime: " + ForkJoinPoolTimeSum/countOftests);
|
||||
}
|
||||
public static void CopyArray() {
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) mainArray[i][j] = bufferArray[i][j];
|
||||
}
|
||||
}
|
||||
public static void PrintLogMatrix(double[][] mainArray) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
System.out.print(mainArray[i][j] + " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
public static long ComputeAndPrintDuration(String typeOfAlgoritm) {
|
||||
long startTime = 0, endTime = 0, duration;
|
||||
switch (typeOfAlgoritm) {
|
||||
case "SingleThreadedAlgorithm":
|
||||
startTime = System.nanoTime();
|
||||
SingleThreadedAlgorithm(mainArray);
|
||||
endTime = System.nanoTime();
|
||||
break;
|
||||
case "ThreadPoolExecutorAlgorithm":
|
||||
startTime = System.nanoTime();
|
||||
ThreadPoolExecutorAlgorithm(mainArray);
|
||||
endTime = System.nanoTime();
|
||||
break;
|
||||
case "ForkJoinPoolAlgorithm":
|
||||
startTime = System.nanoTime();
|
||||
ForkJoinPoolAlgorithm(mainArray);
|
||||
endTime = System.nanoTime();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
duration = (endTime - startTime) / 1000000;
|
||||
//System.out.println("Время выполнения: " + (duration) + " мс\n");
|
||||
CopyArray();
|
||||
return duration;
|
||||
}
|
||||
|
||||
public static void SingleThreadedAlgorithm(double[][] mainArray) {
|
||||
//System.out.println("Однопоточный алгоритм");
|
||||
double min = Double.MAX_VALUE;
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) {
|
||||
if(min > mainArray[i][j]) min = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
//System.out.println("Наименьший элемент: " + min);
|
||||
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) {
|
||||
mainArray[i][j] = mainArray[i][j] / min;
|
||||
}
|
||||
}
|
||||
|
||||
//PrintLogMatrix(mainArray);
|
||||
}
|
||||
public static void ThreadPoolExecutorAlgorithm(double[][] mainArray) {
|
||||
//System.out.println("ThreadPoolExecutorAlgorithm");
|
||||
int size = mainArray.length;
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS); // Создаем пул потоков
|
||||
|
||||
AtomicLong globalMin = new AtomicLong(Long.MAX_VALUE);
|
||||
|
||||
// Вычисление суммы элементов массива
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
final int startRow = t * (size / THREADS); // Начало данного потока
|
||||
final int endRow = (t == THREADS - 1) ? size : (t + 1) * (size / THREADS); // Конец данного потока
|
||||
|
||||
executor.submit(() -> {
|
||||
double localMin = Double.MAX_VALUE;
|
||||
// Вычисление суммы в рамках выделенного потоку диапазона строк
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
if(localMin > mainArray[i][j]) localMin = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
// Вычисление наименьшего элемента глобально
|
||||
long finalLocalMin = (long)localMin;
|
||||
globalMin.updateAndGet(v -> Math.min(v, finalLocalMin));
|
||||
});
|
||||
}
|
||||
|
||||
// Деление элементов массива на наименьшее значение
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
final int startRow = t * (size / THREADS); // Начало данного потока
|
||||
final int endRow = (t == THREADS - 1) ? size : (t + 1) * (size / THREADS); // Конец данного потока
|
||||
|
||||
executor.submit(() -> {
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
mainArray[i][j] /= globalMin.get();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
executor.shutdown();
|
||||
while (!executor.isTerminated()) { /* Ожидание завершения всех задач */ }
|
||||
|
||||
double min = globalMin.get();
|
||||
//System.out.println("Наименьший элемент: " + min);
|
||||
//PrintLogMatrix(mainArray);
|
||||
}
|
||||
public static void ForkJoinPoolAlgorithm(double[][] mainArray) {
|
||||
//System.out.println("ForkJoinPoolAlgorithm");
|
||||
int size = mainArray.length;
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
|
||||
// Вычисление наименьшего элемента
|
||||
MinTask minTask = new MinTask(mainArray, 0, size);
|
||||
double min = forkJoinPool.invoke(minTask);
|
||||
//System.out.println("Наименьший элемент: " + min);
|
||||
// Деление массива на наименьший элемент
|
||||
forkJoinPool.invoke(new DivisionTask(mainArray, 0, size, min));
|
||||
|
||||
forkJoinPool.shutdown();
|
||||
//PrintLogMatrix(mainArray);
|
||||
}
|
||||
|
||||
static class MinTask extends RecursiveTask<Double> {
|
||||
private final double[][] mainArray;
|
||||
private final int startRow, endRow;
|
||||
|
||||
MinTask(double[][] mainArray, int startRow, int endRow) {
|
||||
this.mainArray = mainArray;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double compute() {
|
||||
if (endRow - startRow <= FRONTIER) {
|
||||
double min = Double.MAX_VALUE;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < mainArray[i].length; j++) {
|
||||
if(min > mainArray[i][j]) min = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
return (Double) min;
|
||||
} else {
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
|
||||
MinTask top = new MinTask(mainArray, startRow, midRow);
|
||||
MinTask bottom = new MinTask(mainArray, midRow, endRow);
|
||||
|
||||
top.fork();
|
||||
double bottomResult = bottom.compute();
|
||||
return (Double) Math.min(top.join(), (long)bottomResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
static class DivisionTask extends RecursiveAction {
|
||||
private final double[][] mainArray;
|
||||
private final int startRow, endRow;
|
||||
private final double min;
|
||||
|
||||
DivisionTask(double[][] mainArray, int startRow, int endRow, double min) {
|
||||
this.mainArray = mainArray;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (endRow - startRow <= FRONTIER) {
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < mainArray[i].length; j++) {
|
||||
mainArray[i][j] /= min;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
invokeAll(new DivisionTask(mainArray, startRow, midRow, min),
|
||||
new DivisionTask(mainArray, midRow, endRow, min));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
khasyanov_aidar_lab_1/README.md
Normal file
107
khasyanov_aidar_lab_1/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Лабораторная №1
|
||||
Разработка многопоточного приложения с использованием Java Concurrency
|
||||
согласно варианту задания.
|
||||
|
||||
Необходимо:
|
||||
1) Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2) Разработать параллельный вариант алгоритма с использованием
|
||||
ThreadPoolExecutor и замерить время его работы
|
||||
3) Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
|
||||
и замерить время его работы.
|
||||
|
||||
Массив генерируется до работы всех вариантов алгоритмов. Все три
|
||||
алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
## Вариант и задание
|
||||
2. Разделить элементы матрицы на наименьший элемент.
|
||||
|
||||
### Описание
|
||||
Данная программа разработана для сравнения производительности однопоточного и многопоточного подходов к делению
|
||||
элементов матрицы на среднее арифметическое её элементов. Задача состоит из двух основных частей:
|
||||
1. Нахождение наименьшего элемента в матрице
|
||||
2. Деление элементов матрицы на наименьший элемент в матрице
|
||||
|
||||
Эти этапы выполняются с разными вариантами многопоточности, чтобы выявить их влияние на производительность.
|
||||
|
||||
### Как запустить
|
||||
java MinDivisionTask
|
||||
|
||||
### Используемые технологии
|
||||
Программа использует Java Concurrency, включая классы и интерфейсы:
|
||||
1) ExecutorService
|
||||
2) RecursiveAction
|
||||
3) ForkJoinPool
|
||||
ThreadPoolExecutor был реализован вручную
|
||||
|
||||
### Что делает программа
|
||||
1. Генерирует матрицу заданного размера, заполненную случайными значениями.
|
||||
2. Находит наименьший элемент в матрице.
|
||||
3. Делит все элементы матрицы на наименьший элемент в матрице.
|
||||
4. Замеряет время выполнения для однопоточного и двух многопоточных (ThreadPoolExecutor, ForkJoinPool) алгоритмов.
|
||||
|
||||
## Результаты тестов
|
||||
(SingleThreadedTime - Однопоточный алгоритм)
|
||||
|
||||
### Для матрцы 4000*4000:
|
||||
{Тест 1} SingleThreadedTime: 80 ThreadPoolExecutorTime: 78 ForkJoinPoolTime: 64
|
||||
{Тест 2} SingleThreadedTime: 45 ThreadPoolExecutorTime: 39 ForkJoinPoolTime: 64
|
||||
{Тест 3} SingleThreadedTime: 37 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 32
|
||||
{Тест 4} SingleThreadedTime: 32 ThreadPoolExecutorTime: 40 ForkJoinPoolTime: 46
|
||||
{Тест 5} SingleThreadedTime: 43 ThreadPoolExecutorTime: 32 ForkJoinPoolTime: 50
|
||||
{Тест 6} SingleThreadedTime: 44 ThreadPoolExecutorTime: 29 ForkJoinPoolTime: 45
|
||||
{Тест 7} SingleThreadedTime: 45 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 45
|
||||
{Тест 8} SingleThreadedTime: 44 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 47
|
||||
{Тест 9} SingleThreadedTime: 43 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 45
|
||||
{Тест 10} SingleThreadedTime: 43 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 11} SingleThreadedTime: 34 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 32
|
||||
{Тест 12} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 13} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
{Тест 14} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 15} SingleThreadedTime: 32 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 16} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 17} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 18} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
{Тест 19} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
{Тест 20} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
Среднее время:
|
||||
SingleThreadedTime: 38
|
||||
ThreadPoolExecutorTime: 33
|
||||
ForkJoinPoolTime: 39
|
||||
|
||||
### Для матрцы 11000*11000:
|
||||
{Тест 1} SingleThreadedTime: 402 ThreadPoolExecutorTime: 259 ForkJoinPoolTime: 271
|
||||
{Тест 2} SingleThreadedTime: 406 ThreadPoolExecutorTime: 273 ForkJoinPoolTime: 236
|
||||
{Тест 3} SingleThreadedTime: 388 ThreadPoolExecutorTime: 245 ForkJoinPoolTime: 226
|
||||
{Тест 4} SingleThreadedTime: 237 ThreadPoolExecutorTime: 329 ForkJoinPoolTime: 339
|
||||
{Тест 5} SingleThreadedTime: 333 ThreadPoolExecutorTime: 251 ForkJoinPoolTime: 227
|
||||
{Тест 6} SingleThreadedTime: 330 ThreadPoolExecutorTime: 231 ForkJoinPoolTime: 220
|
||||
{Тест 7} SingleThreadedTime: 344 ThreadPoolExecutorTime: 234 ForkJoinPoolTime: 224
|
||||
{Тест 8} SingleThreadedTime: 330 ThreadPoolExecutorTime: 224 ForkJoinPoolTime: 224
|
||||
{Тест 9} SingleThreadedTime: 329 ThreadPoolExecutorTime: 222 ForkJoinPoolTime: 220
|
||||
{Тест 10} SingleThreadedTime: 336 ThreadPoolExecutorTime: 230 ForkJoinPoolTime: 239
|
||||
{Тест 11} SingleThreadedTime: 238 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 222
|
||||
{Тест 12} SingleThreadedTime: 238 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 234
|
||||
{Тест 13} SingleThreadedTime: 243 ThreadPoolExecutorTime: 241 ForkJoinPoolTime: 223
|
||||
{Тест 14} SingleThreadedTime: 253 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 224
|
||||
{Тест 15} SingleThreadedTime: 235 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 223
|
||||
{Тест 16} SingleThreadedTime: 243 ThreadPoolExecutorTime: 222 ForkJoinPoolTime: 223
|
||||
{Тест 17} SingleThreadedTime: 236 ThreadPoolExecutorTime: 221 ForkJoinPoolTime: 221
|
||||
{Тест 18} SingleThreadedTime: 234 ThreadPoolExecutorTime: 225 ForkJoinPoolTime: 233
|
||||
{Тест 19} SingleThreadedTime: 238 ThreadPoolExecutorTime: 221 ForkJoinPoolTime: 221
|
||||
{Тест 20} SingleThreadedTime: 241 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 225
|
||||
Среднее время:
|
||||
SingleThreadedTime: 291
|
||||
ThreadPoolExecutorTime: 237
|
||||
ForkJoinPoolTime: 233
|
||||
|
||||
## Вывод
|
||||
В ходе анализа производительности алгоритмов обработки матриц разного размера выяснилось, что многопоточные алгоритмы
|
||||
не всегда оказываются быстрее однопоточных, особенно при работе с небольшими матрицами. В таких случаях многопоточные
|
||||
алгоритмы, использующие ThreadPoolExecutor и ForkJoinPool, показывают более плохие результаты из-за дополнительных
|
||||
затрат на создание потоков и управление ими.
|
||||
Но когда размеры матриц увеличиваются, многопоточные алгоритмы начинают показывать гораздо лучшую производительность
|
||||
по сравнению с однопоточным. Однопоточный алгоритм начинает замедляться из-за большого объема данных, тогда как
|
||||
многопоточные алгоритмы, начинают работать быстрее. Это происходит потому, что затраты на управление потоками
|
||||
оправдываются их параллельной работой, что позволяет значительно ускорить обработку данных.
|
||||
Вообщем, многопоточность действительно может значительно повысить производительность при обработке больших объемов
|
||||
данных.
|
||||
62
kukushkina_ekaterina_lab_1/README.md
Normal file
62
kukushkina_ekaterina_lab_1/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Лабораторная работа №1. Вариант 12.
|
||||
**Задание: определить среднее арифметическое элементов матрицы ниже главной
|
||||
диагонали.**
|
||||
|
||||
## Как запустить лабораторную работу?
|
||||
Чтобы её запустить нам потребуется:
|
||||
* Сомпилировать файл с кодом в формате java с помощью комманды: javac Main.java
|
||||
* Запустить программу с помощью команды: java Main
|
||||
|
||||
## Какие технологии использовали?
|
||||
Используемые технологии:
|
||||
* java.io.*: Для операций ввода-вывода, таких как чтение и запись файлов.
|
||||
* java.util.*: Содержит основные классы коллекций (List, ArrayList, Scanner и т.д.) и другие утилиты.
|
||||
* java.util.concurrent.*: Предоставляет инструменты для многопоточного программирования, включая ExecutorService, ThreadPoolExecutor, Future, Callable, ForkJoinPool и другие.
|
||||
ThreadPoolExecutor: Управляет пулом потоков фиксированного размера для параллельного выполнения задач, отправляемых в очередь.
|
||||
ForkJoinPool: Реализует "кражу работы" (work-stealing) для эффективного параллельного выполнения рекурсивно разделяемых задач.
|
||||
|
||||
## Что программа делает?
|
||||
Данная программа вычисляет среднее арифметическое элементов ниже главной диагонали квадратной матрицы, сравнивая однопоточный и два многопоточных подхода: ThreadPoolExecutor и ForkJoinPool.
|
||||
Пользователь вводит размер матрицы, она генерируется, сохраняется в файл, считывается обратно, а затем для каждого алгоритма измеряется время выполнения и выводится результат.
|
||||
Программа обрабатывает некорректный ввод пользователя и завершает работу с ошибкой.
|
||||
**Цель: продемонстрировать преимущества многопоточности в решении данной вычислительной задачи
|
||||
и сравнить производительность различных способов распараллеливания.**
|
||||
|
||||
## Примеры входных и выходных значений.
|
||||
|
||||
Введите размер матрицы (квадратной): 1000\
|
||||
Однопоточный алгоритм: 4 мс, Среднее арифметическое: 99.49616216216216\
|
||||
Многопоточный алгоритм с использованием ThreadPoolExecutor: 58 мс, Среднее арифметическое: 99.49616216216216\
|
||||
Многопоточный алгоритм с использованием ForkJoinPool: 9 мс, Среднее арифметическое: 99.49616216216216\
|
||||
|
||||
Введите размер матрицы (квадратной): 5000\
|
||||
Однопоточный алгоритм: 90 мс, Среднее арифметическое: 99.5001852370474\
|
||||
Многопоточный алгоритм с использованием ThreadPoolExecutor: 202 мс, Среднее арифметическое: 99.5001852370474\
|
||||
Многопоточный алгоритм с использованием ForkJoinPool: 68 мс, Среднее арифметическое: 99.5001852370474\
|
||||
|
||||
Введите размер матрицы (квадратной): 9000\
|
||||
Однопоточный алгоритм: 312 мс, Среднее арифметическое: 99.48965727056093\
|
||||
Многопоточный алгоритм с использованием ThreadPoolExecutor: 378 мс, Среднее арифметическое: 99.48965727056093\
|
||||
Многопоточный алгоритм с использованием ForkJoinPool: 113 мс, Среднее арифметическое: 99.48965727056093\
|
||||
|
||||
Введите размер матрицы (квадратной): 12000\
|
||||
Однопоточный алгоритм: 650 мс, Среднее арифметическое: 99.4960964941523\
|
||||
Многопоточный алгоритм с использованием ThreadPoolExecutor: 607 мс, Среднее арифметическое: 99.4960964941523\
|
||||
Многопоточный алгоритм с использованием ForkJoinPool: 207 мс, Среднее арифметическое: 99.4960964941523\
|
||||
|
||||
Введите размер матрицы (квадратной): 14000\
|
||||
Однопоточный алгоритм: 8901 мс, Среднее арифметическое: 99.50303716592002\
|
||||
Многопоточный алгоритм с использованием ThreadPoolExecutor: 1280 мс, Среднее арифметическое: 99.50303716592002\
|
||||
Многопоточный алгоритм с использованием ForkJoinPool: 142 мс, Среднее арифметическое: 99.50303716592002\
|
||||
|
||||
## Вывод по полученным результатам.
|
||||
Иссходя из полученных результатов мы можем сделать вывод,
|
||||
что чем больше объём входных данных тем более выгодно
|
||||
использовать многопоточность для реализации алгоритмов.
|
||||
Объясняется это тем, что чем больше объем входных данных, тем больше работы можно параллельно распределить между потоками,
|
||||
минимизируя время простоя и максимально используя ресурсы процессора,
|
||||
что приводит к значительному ускорению выполнения алгоритма по сравнению с однопоточным решением.\
|
||||
В данном варианте метод с использованием ThreadPoolExecutor показывает себя не очень эффективно,
|
||||
становясь быстрее однопоточного алгоритма только на очень больших размерах матрицы. Происходит это
|
||||
если задача занимает мало времени, и накладные расходы на создание задачи, переключение контекста между
|
||||
потоками и синхронизацию могут превысить выигрыш от распараллеливания, делая однопоточное решение более эффективным.
|
||||
205
kukushkina_ekaterina_lab_1/src/Main.java
Normal file
205
kukushkina_ekaterina_lab_1/src/Main.java
Normal file
@@ -0,0 +1,205 @@
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static int[][] generateMatrix(int size) {
|
||||
int[][] matrix = new int[size][size];
|
||||
Random rnd = new Random();
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = rnd.nextInt(200);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static void writeMatrix(int[][] matrix, String filename) throws IOException {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
|
||||
for (int[] row : matrix) {
|
||||
for (int value : row) {
|
||||
writer.write(value + " ");
|
||||
}
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int[][] readMatrix(String filename, int size) throws IOException {
|
||||
int[][] matrix = new int[size][size];
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
String[] values = reader.readLine().trim().split(" ");
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = Integer.parseInt(values[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static double singleThreaded(int[][] matrix) {
|
||||
int len = matrix.length;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double totalSum = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (int j = 0; j < len; j++) {
|
||||
if (i > j) {
|
||||
totalSum += matrix[i][j];
|
||||
totalCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalCount == 0 ? 0 : totalSum / totalCount;
|
||||
}
|
||||
|
||||
private static class PartialResult {
|
||||
double sum;
|
||||
int count;
|
||||
|
||||
public PartialResult(double sum, int count) {
|
||||
this.sum = sum;
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
public static double ThreadPoolExecutor(int[][] matrix, int numThreads) throws InterruptedException {
|
||||
int len = matrix.length;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
List<Future<PartialResult>> futures = new ArrayList<>();
|
||||
for (int i = 0; i < len; i++) {
|
||||
final int row = i;
|
||||
Callable<PartialResult> task = () -> {
|
||||
double localSum = 0;
|
||||
int localCount = 0;
|
||||
for (int j = 0; j < len; j++) {
|
||||
if (row > j) {
|
||||
localSum += matrix[row][j];
|
||||
localCount++;
|
||||
}
|
||||
}
|
||||
return new PartialResult(localSum, localCount);
|
||||
};
|
||||
futures.add(executor.submit(task));
|
||||
}
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.MINUTES);
|
||||
double totalSum = 0;
|
||||
int totalCount = 0;
|
||||
for (Future<PartialResult> future : futures) {
|
||||
try {
|
||||
PartialResult result = future.get();
|
||||
totalSum += result.sum;
|
||||
totalCount += result.count;
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return totalCount == 0 ? 0 : totalSum / totalCount;
|
||||
}
|
||||
|
||||
public static double ForkJoinPool(int[][] matrix) {
|
||||
int rows = matrix.length;
|
||||
if (rows == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
ForkJoinPool pool = new ForkJoinPool();
|
||||
PartialResult result = pool.invoke(new ForkJoinTask(matrix, 0, rows));
|
||||
|
||||
return result.count == 0 ? 0 : result.sum / result.count;
|
||||
}
|
||||
|
||||
private static class ForkJoinTask extends RecursiveTask<PartialResult> {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
public ForkJoinTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PartialResult compute() {
|
||||
if (endRow - startRow <= 100) {
|
||||
double sum = 0;
|
||||
int count = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
sum += matrix[i][j];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return new PartialResult(sum, count);
|
||||
} else {
|
||||
int mid = (startRow + endRow) / 2;
|
||||
ForkJoinTask left = new ForkJoinTask(matrix, startRow, mid);
|
||||
ForkJoinTask right = new ForkJoinTask(matrix, mid, endRow);
|
||||
left.fork();
|
||||
PartialResult rightResult = right.compute();
|
||||
PartialResult leftResult = left.join();
|
||||
|
||||
double totalSum = leftResult.sum + rightResult.sum;
|
||||
int totalCount = leftResult.count + rightResult.count;
|
||||
|
||||
return new PartialResult(totalSum, totalCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int size;
|
||||
|
||||
try {
|
||||
System.out.print("Введите размер матрицы (квадратной): ");
|
||||
if (!scanner.hasNextInt()) {
|
||||
throw new IllegalArgumentException("Ошибка: Вы должны ввести целое число.");
|
||||
}
|
||||
size = scanner.nextInt();
|
||||
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("Ошибка: Размер матрицы должен быть положительным числом.");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
int[][] matrix = generateMatrix(size);
|
||||
writeMatrix(matrix, "Matrix.txt");
|
||||
|
||||
int[][] matrixRead = readMatrix("Matrix.txt", size);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
double singleThreadedResult = singleThreaded(matrixRead);
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("Однопоточный алгоритм: " + (endTime - startTime) + " мс, Среднее арифметическое: " + singleThreadedResult);
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
double threadPoolResult = ThreadPoolExecutor(matrixRead, 4);
|
||||
endTime = System.currentTimeMillis();
|
||||
System.out.println("Многопоточный алгоритм с использованием ThreadPoolExecutor: " + (endTime - startTime) + " мс, Среднее арифметическое: " + threadPoolResult);
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
double ForkJoinResult = ForkJoinPool(matrixRead);
|
||||
endTime = System.currentTimeMillis();
|
||||
|
||||
System.out.println("Многопоточный алгоритм с использованием ForkJoinPool: " + (endTime - startTime) + " мс, Среднее арифметическое: " + ForkJoinResult);
|
||||
|
||||
}
|
||||
}
|
||||
38
kuznetsov_danila_lab_1/Lab 1/.gitignore
vendored
Normal file
38
kuznetsov_danila_lab_1/Lab 1/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
@@ -0,0 +1,163 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class MatrixMinFinder {
|
||||
|
||||
public static void main(String[] args) {
|
||||
int n = 10000;
|
||||
|
||||
int[][] matrix = generateMatrix(n);
|
||||
System.out.println("Сгенерирована матрица размером " + n + "x" + n);
|
||||
|
||||
// Однопоточный вариант
|
||||
long startTime = System.nanoTime();
|
||||
int minSingle = findMinSingleThread(matrix);
|
||||
long duration = System.nanoTime() - startTime;
|
||||
System.out.printf("Однопоточный вариант: min = %d, время = %.3f мс%n",
|
||||
minSingle, duration / 1_000_000.0);
|
||||
|
||||
// Параллельный вариант с использованием ThreadPoolExecutor
|
||||
startTime = System.nanoTime();
|
||||
int minThreadPool = findMinThreadPool(matrix);
|
||||
duration = System.nanoTime() - startTime;
|
||||
System.out.printf("ThreadPoolExecutor: min = %d, время = %.3f мс%n",
|
||||
minThreadPool, duration / 1_000_000.0);
|
||||
|
||||
// Параллельный вариант с использованием ForkJoinPool
|
||||
startTime = System.nanoTime();
|
||||
int minForkJoin = findMinForkJoin(matrix);
|
||||
duration = System.nanoTime() - startTime;
|
||||
System.out.printf("ForkJoinPool: min = %d, время = %.3f мс%n",
|
||||
minForkJoin, duration / 1_000_000.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация квадратной матрицы случайных чисел в диапазоне [-10000; 10000]
|
||||
*/
|
||||
private static int[][] generateMatrix(int n) {
|
||||
int[][] matrix = new int[n][n];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
matrix[i][j] = random.nextInt(20001) - 10000;
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Однопоточный вариант поиска минимума элементов ниже главной диагонали.
|
||||
* Для каждой строки i, начиная с i=1 (так как в строке 0 нет элементов ниже диагонали),
|
||||
* перебираются столбцы от 0 до i-1.
|
||||
*/
|
||||
public static int findMinSingleThread(int[][] matrix) {
|
||||
int n = matrix.length;
|
||||
int min = Integer.MAX_VALUE;
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < min) {
|
||||
min = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Параллельный вариант с использованием ThreadPoolExecutor.
|
||||
* Матрица делится на блоки строк, для каждого блока создаётся задача.
|
||||
*/
|
||||
public static int findMinThreadPool(int[][] matrix) {
|
||||
int n = matrix.length;
|
||||
int numThreads = Runtime.getRuntime().availableProcessors();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
List<Future<Integer>> futures = new ArrayList<>();
|
||||
|
||||
int blockSize = (n + numThreads - 1) / numThreads;
|
||||
for (int t = 0; t < numThreads; t++) {
|
||||
int startRow = t * blockSize;
|
||||
int endRow = Math.min(n, startRow + blockSize);
|
||||
Future<Integer> future = executor.submit(() -> {
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
for (int i = Math.max(startRow, 1); i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < localMin) {
|
||||
localMin = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return localMin;
|
||||
});
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
int min = Integer.MAX_VALUE;
|
||||
try {
|
||||
for (Future<Integer> future : futures) {
|
||||
int result = future.get();
|
||||
if (result < min) {
|
||||
min = result;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
executor.shutdown();
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Параллельный вариант с использованием ForkJoinPool.
|
||||
* Создаётся рекурсивная задача, которая делит диапазон строк, если он превышает порог.
|
||||
*/
|
||||
public static int findMinForkJoin(int[][] matrix) {
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
MatrixMinTask task = new MatrixMinTask(matrix, 1, matrix.length);
|
||||
int min = forkJoinPool.invoke(task);
|
||||
forkJoinPool.shutdown();
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс задачи для ForkJoinPool.
|
||||
* Если количество строк в диапазоне меньше порогового значения, производится последовательное вычисление,
|
||||
* иначе диапазон делится на две части.
|
||||
*/
|
||||
static class MatrixMinTask extends RecursiveTask<Integer> {
|
||||
private static final int THRESHOLD = 5;
|
||||
private int[][] matrix;
|
||||
private int startRow;
|
||||
private int endRow;
|
||||
|
||||
public MatrixMinTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer compute() {
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < localMin) {
|
||||
localMin = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return localMin;
|
||||
} else {
|
||||
int mid = (startRow + endRow) / 2;
|
||||
MatrixMinTask leftTask = new MatrixMinTask(matrix, startRow, mid);
|
||||
MatrixMinTask rightTask = new MatrixMinTask(matrix, mid, endRow);
|
||||
leftTask.fork();
|
||||
int rightResult = rightTask.compute();
|
||||
int leftResult = leftTask.join();
|
||||
return Math.min(leftResult, rightResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
181
kuznetsov_danila_lab_1/README.md
Normal file
181
kuznetsov_danila_lab_1/README.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# 1 Лабораторная работа
|
||||
|
||||
## Задание
|
||||
|
||||
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
|
||||
|
||||
**Необходимо:**
|
||||
|
||||
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2. Разработать параллельный вариант алгоритма с использованием `ThreadPoolExecutor` и замерить время его работы.
|
||||
3. Разработать параллельный вариант алгоритма с использованием `ForkJoinPool` и замерить время его работы.
|
||||
|
||||
**Вариант:** "Определить минимальный элемент матрицы ниже главной диагонали"
|
||||
|
||||
---
|
||||
|
||||
## Подготовка
|
||||
|
||||
### Генерация матрицы
|
||||
|
||||
```java
|
||||
private static int[][] generateMatrix(int n) {
|
||||
int[][] matrix = new int[n][n];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
matrix[i][j] = random.nextInt(20001) - 10000;
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
```
|
||||
|
||||
*Описание:*
|
||||
Метод получает размер матрицы `n` и создаёт массив размером `n x n`, заполняя его числами от **-10000** до **10000** (так как `random.nextInt(20001)` возвращает значения от 0 до 20000, а затем вычитается 10000).
|
||||
|
||||
---
|
||||
|
||||
## Пункт 1. Однопоточный вариант
|
||||
|
||||
```java
|
||||
public static int findMinSingleThread(int[][] matrix) {
|
||||
int n = matrix.length;
|
||||
int min = Integer.MAX_VALUE;
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < min) {
|
||||
min = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
```
|
||||
|
||||
*Описание:*
|
||||
Создаётся переменная `min`, инициализированная максимальным значением типа `int`. Далее происходит последовательный проход по элементам матрицы ниже главной диагонали (для строки `i` перебираются столбцы с `0` до `i-1`), и если найден элемент меньше текущего минимума, переменная `min` обновляется.
|
||||
|
||||
---
|
||||
|
||||
## Пункт 2. Параллельный вариант с использованием ThreadPoolExecutor
|
||||
|
||||
```java
|
||||
public static int findMinThreadPool(int[][] matrix) {
|
||||
int n = matrix.length;
|
||||
int numThreads = Runtime.getRuntime().availableProcessors();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
List<Future<Integer>> futures = new ArrayList<>();
|
||||
|
||||
int blockSize = (n + numThreads - 1) / numThreads;
|
||||
for (int t = 0; t < numThreads; t++) {
|
||||
int startRow = t * blockSize;
|
||||
int endRow = Math.min(n, startRow + blockSize);
|
||||
Future<Integer> future = executor.submit(() -> {
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
for (int i = Math.max(startRow, 1); i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < localMin) {
|
||||
localMin = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return localMin;
|
||||
});
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
int min = Integer.MAX_VALUE;
|
||||
try {
|
||||
for (Future<Integer> future : futures) {
|
||||
int result = future.get();
|
||||
if (result < min) {
|
||||
min = result;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
executor.shutdown();
|
||||
return min;
|
||||
}
|
||||
```
|
||||
|
||||
*Описание:*
|
||||
- Получаем число доступных процессоров и сохраняем его в переменную `numThreads`.
|
||||
- С помощью `Executors.newFixedThreadPool(numThreads)` создаём пул фиксированного размера.
|
||||
- Рассчитываем размер блока строк:
|
||||
```java
|
||||
int blockSize = (n + numThreads - 1) / numThreads;
|
||||
```
|
||||
Таким образом, матрица делится на равные (или почти равные) части.
|
||||
- Для каждого блока определяется диапазон строк от `startRow` до `endRow`. В лямбда-выражении происходит последовательный поиск минимума в этом диапазоне (начиная с `Math.max(startRow, 1)`, чтобы не обрабатывать строку 0).
|
||||
- Результаты задач собираются в список `futures`, после чего перебираются и вычисляется глобальный минимум.
|
||||
- После получения результатов пул потоков закрывается через `executor.shutdown()`.
|
||||
|
||||
---
|
||||
|
||||
## Пункт 3. Параллельный вариант с использованием ForkJoinPool
|
||||
|
||||
```java
|
||||
public static int findMinForkJoin(int[][] matrix) {
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
MatrixMinTask task = new MatrixMinTask(matrix, 1, matrix.length);
|
||||
int min = forkJoinPool.invoke(task);
|
||||
forkJoinPool.shutdown();
|
||||
return min;
|
||||
}
|
||||
|
||||
static class MatrixMinTask extends RecursiveTask<Integer> {
|
||||
private static final int THRESHOLD = 100;
|
||||
private int[][] matrix;
|
||||
private int startRow;
|
||||
private int endRow;
|
||||
|
||||
public MatrixMinTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer compute() {
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < localMin) {
|
||||
localMin = matrix[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return localMin;
|
||||
} else {
|
||||
int mid = (startRow + endRow) / 2;
|
||||
MatrixMinTask leftTask = new MatrixMinTask(matrix, startRow, mid);
|
||||
MatrixMinTask rightTask = new MatrixMinTask(matrix, mid, endRow);
|
||||
leftTask.fork();
|
||||
int rightResult = rightTask.compute();
|
||||
int leftResult = leftTask.join();
|
||||
return Math.min(leftResult, rightResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Описание:*
|
||||
- Создаётся экземпляр `ForkJoinPool` для распараллеливания рекурсивных задач.
|
||||
- Запускается задача `MatrixMinTask`, которая ищет минимум в строках с `startRow = 1` до `matrix.length`.
|
||||
- В классе `MatrixMinTask` определён порог `THRESHOLD = 100`. Если диапазон строк меньше или равен порогу, задача решается последовательно. В противном случае диапазон делится на две части:
|
||||
- **Левая задача:** обрабатывает строки от `startRow` до `mid`.
|
||||
- **Правая задача:** обрабатывает строки от `mid` до `endRow`.
|
||||
- Для асинхронного выполнения используется метод `fork()` (для левой задачи), а затем с помощью `compute()` и `join()` происходит объединение результатов двух подзадач.
|
||||
|
||||
---
|
||||
|
||||
## Запуск
|
||||
Запустить jar можно командой:
|
||||
|
||||
```bash
|
||||
java -jar Lab-1-jar-with-dependencies.jar
|
||||
```
|
||||
38
kuznetsov_danila_lab_2/Lab 2/.gitignore
vendored
Normal file
38
kuznetsov_danila_lab_2/Lab 2/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
@@ -0,0 +1,71 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import mpi.MPI;
|
||||
|
||||
public class MatrixMinBelowMainDiagonal {
|
||||
public static void main(String[] args) {
|
||||
MPI.Init(args);
|
||||
|
||||
int rank = MPI.COMM_WORLD.Rank();
|
||||
int size = MPI.COMM_WORLD.Size();
|
||||
|
||||
int n = 4;
|
||||
double[] matrix = new double[n * n];
|
||||
|
||||
if (rank == 0) {
|
||||
double[][] m = {
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
{9, 10, 11, 12},
|
||||
{13, 14, 15, 16}
|
||||
};
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
System.arraycopy(m[i], 0, matrix, i * n, n);
|
||||
}
|
||||
}
|
||||
|
||||
System.nanoTime();
|
||||
|
||||
int[] dim = new int[]{n};
|
||||
MPI.COMM_WORLD.Bcast(dim, 0, 1, MPI.INT, 0);
|
||||
n = dim[0];
|
||||
|
||||
MPI.COMM_WORLD.Bcast(matrix, 0, n * n, MPI.DOUBLE, 0);
|
||||
|
||||
int rowsPerProc = n / size;
|
||||
int extra = n % size;
|
||||
int startRow, endRow;
|
||||
if (rank < extra) {
|
||||
startRow = rank * (rowsPerProc + 1);
|
||||
endRow = startRow + rowsPerProc;
|
||||
} else {
|
||||
startRow = rank * rowsPerProc + extra;
|
||||
endRow = startRow + rowsPerProc - 1;
|
||||
}
|
||||
|
||||
double localMin = Double.MAX_VALUE;
|
||||
for (int i = startRow; i <= endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
double value = matrix[i * n + j];
|
||||
if (value < localMin) {
|
||||
localMin = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double[] sendBuf = new double[]{ localMin };
|
||||
double[] globalMin = new double[1];
|
||||
MPI.COMM_WORLD.Reduce(sendBuf, 0, globalMin, 0, 1, MPI.DOUBLE, MPI.MIN, 0);
|
||||
|
||||
if (rank == 0) {
|
||||
if (globalMin[0] == Double.MAX_VALUE) {
|
||||
System.out.println("Элементы ниже главной диагонали отсутствуют.");
|
||||
} else {
|
||||
System.out.println("Минимальный элемент ниже главной диагонали: " + globalMin[0]);
|
||||
}
|
||||
}
|
||||
|
||||
MPI.Finalize();
|
||||
}
|
||||
}
|
||||
141
kuznetsov_danila_lab_2/README.md
Normal file
141
kuznetsov_danila_lab_2/README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Лабораторная работа 2
|
||||
|
||||
## 1. Задание
|
||||
|
||||
Разработка параллельного MPI приложения на языке Java.
|
||||
Необходимо разработать параллельный вариант алгоритма с применением MPI и замерить время его работы.
|
||||
Запуск каждого экземпляра MPI происходит в своём контейнере.
|
||||
|
||||
**Вариант:** "Определить минимальный элемент матрицы ниже главной диагонали"
|
||||
|
||||
## 2. Описание кода
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import mpi.MPI;
|
||||
|
||||
public class MatrixMinBelowMainDiagonal {
|
||||
public static void main(String[] args) {
|
||||
MPI.Init(args);
|
||||
|
||||
int rank = MPI.COMM_WORLD.Rank();
|
||||
int size = MPI.COMM_WORLD.Size();
|
||||
|
||||
int n = 4;
|
||||
double[] matrix = new double[n * n];
|
||||
|
||||
if (rank == 0) {
|
||||
double[][] m = {
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
{9, 10, 11, 12},
|
||||
{13, 14, 15, 16}
|
||||
};
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
System.arraycopy(m[i], 0, matrix, i * n, n);
|
||||
}
|
||||
}
|
||||
|
||||
int[] dim = new int[]{n};
|
||||
MPI.COMM_WORLD.Bcast(dim, 0, 1, MPI.INT, 0);
|
||||
n = dim[0];
|
||||
|
||||
MPI.COMM_WORLD.Bcast(matrix, 0, n * n, MPI.DOUBLE, 0);
|
||||
|
||||
int rowsPerProc = n / size;
|
||||
int extra = n % size;
|
||||
int startRow, endRow;
|
||||
if (rank < extra) {
|
||||
startRow = rank * (rowsPerProc + 1);
|
||||
endRow = startRow + rowsPerProc;
|
||||
} else {
|
||||
startRow = rank * rowsPerProc + extra;
|
||||
endRow = startRow + rowsPerProc - 1;
|
||||
}
|
||||
|
||||
double localMin = Double.MAX_VALUE;
|
||||
for (int i = startRow; i <= endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
double value = matrix[i * n + j];
|
||||
if (value < localMin) {
|
||||
localMin = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double[] sendBuf = new double[]{ localMin };
|
||||
double[] globalMin = new double[1];
|
||||
MPI.COMM_WORLD.Reduce(sendBuf, 0, globalMin, 0, 1, MPI.DOUBLE, MPI.MIN, 0);
|
||||
|
||||
if (rank == 0) {
|
||||
if (globalMin[0] == Double.MAX_VALUE) {
|
||||
System.out.println("Элементы ниже главной диагонали отсутствуют.");
|
||||
} else {
|
||||
System.out.println("Минимальный элемент ниже главной диагонали: " + globalMin[0]);
|
||||
}
|
||||
}
|
||||
|
||||
MPI.Finalize();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
1. Метод `MPI.Init(args)` запускает параллельную среду. Каждый процесс получает свой уникальный ранг и общее число процессов через `MPI.COMM_WORLD.Rank()` и `MPI.COMM_WORLD.Size()`.
|
||||
|
||||
2. Только процесс с рангом 0 (мастер-процесс) задаёт исходную матрицу. Для удобства обмена данными матрица преобразуется в одномерный массив с помощью `System.arraycopy`.
|
||||
|
||||
3. Сначала размер матрицы (n) передаётся всем процессам, затем вся матрица передаётся всем процессам с помощью коллективной операции `Bcast`.
|
||||
|
||||
4. Матрица разбивается на блоки строк для каждого процесса. Если число процессов не делит размер матрицы нацело, первые процессы получают на одну строку больше (`extra`).
|
||||
|
||||
5. Каждый процесс просматривает свои строки и ищет минимальный элемент среди элементов ниже главной диагонали (для строки с индексом `i` обрабатываются столбцы от `0` до `i-1`).
|
||||
|
||||
6. Операция `Reduce` с оператором `MPI.MIN` объединяет локальные минимумы в глобальный, который выводится на экран процессом с рангом 0.
|
||||
|
||||
7. После выполнения всех вычислений вызывается `MPI.Finalize()`, что корректно завершает параллельную среду.
|
||||
|
||||
## 4. Действия, производимые на контейнерах
|
||||
|
||||
Для запуска этого MPI-приложения в распределённом режиме использовались контейнеры, каждый из которых настроен следующим образом:
|
||||
|
||||
- **Установка Java и MPJ Express:**
|
||||
На каждом контейнере установлена jdk-21 и распакована библиотека MPJ Express. Переменная `MPJ_HOME` настроена на путь к MPJ Express, а каталог `MPJ_HOME/bin` добавлен в переменную `PATH`.
|
||||
|
||||
- **Настройка сети и SSH:**
|
||||
Контейнеры настроены так, чтобы иметь возможность обмениваться данными по сети. Для работы MPJ Express настроен passwordless SSH между контейнерами. Файл `machines` содержит IP-адреса всех контейнеров:
|
||||
```
|
||||
192.168.10.101
|
||||
192.168.10.102
|
||||
192.168.10.103
|
||||
192.168.10.104
|
||||
```
|
||||
Это позволяет MPJ Express распределить процессы по разным контейнерам.
|
||||
|
||||
- **Запуск демонов MPJ Express:**
|
||||
На каждом контейнере запущен MPJ daemon, который слушает входящие соединения от мастера. Демоны обеспечивают обмен информацией о портах и корректное распределение процессов.
|
||||
```bash
|
||||
$MPJ_HOME/bin/mpjdaemon -boot 192.168.10.101
|
||||
$MPJ_HOME/bin/mpjdaemon -boot 192.168.10.102
|
||||
$MPJ_HOME/bin/mpjdaemon -boot 192.168.10.103
|
||||
$MPJ_HOME/bin/mpjdaemon -boot 192.168.10.104
|
||||
```
|
||||
|
||||
- **Добавление хостов в каждый контейнер**
|
||||
На каждом контейнере добавляем в файл ``/etc/hosts`` каждый хост, на одном из контейнеров это выглядит примерно так:
|
||||
```bash
|
||||
# --- BEGIN PVE ---
|
||||
192.168.10.101 node1.com node1
|
||||
192.168.10.102 node2.com node2
|
||||
192.168.10.103 node3.com node3
|
||||
192.168.10.104 node4.com node4
|
||||
# --- END PVE ---
|
||||
```
|
||||
|
||||
- **Запуск MPI-приложения:**
|
||||
Из одного из контейнеров запускается приложение с помощью команды:
|
||||
```bash
|
||||
mpjrun.sh -np 4 -dev niodev MatrixMinBelowMainDiagonal
|
||||
```
|
||||
Эта команда запускает 4 процесса, которые распределяются между контейнерами согласно файлу `machines`.
|
||||
38
kuznetsov_danila_lab_3/Lab 3.1/.gitignore
vendored
Normal file
38
kuznetsov_danila_lab_3/Lab 3.1/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MatrixGatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MatrixGatewayApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class MatrixGatewayController {
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
private String computeServiceUrl = "http://192.168.10.101:8080/computeMin";
|
||||
|
||||
/**
|
||||
* Принимает матрицу и перенаправляет запрос в сервис вычислений.
|
||||
*/
|
||||
@PostMapping("/findMin")
|
||||
public ResponseEntity<?> findMin(@RequestBody int[][] matrix) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<int[][]> request = new HttpEntity<>(matrix, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
computeServiceUrl,
|
||||
HttpMethod.POST,
|
||||
request,
|
||||
Map.class
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
38
kuznetsov_danila_lab_3/Lab 3/.gitignore
vendored
Normal file
38
kuznetsov_danila_lab_3/Lab 3/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MatrixComputeApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MatrixComputeApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class MatrixComputeController {
|
||||
|
||||
@Autowired
|
||||
private MatrixMinService matrixMinService;
|
||||
|
||||
/**
|
||||
* REST эндпоинт для вычисления минимального элемента.
|
||||
* Принимает матрицу в виде двумерного массива int.
|
||||
*/
|
||||
@PostMapping("/computeMin")
|
||||
public ResponseEntity<Map<String, Object>> computeMin(@RequestBody int[][] matrix) {
|
||||
long startTime = System.nanoTime();
|
||||
int min = matrixMinService.findMinBelowMainDiagonal(matrix);
|
||||
long endTime = System.nanoTime();
|
||||
double duration = (double) (endTime - startTime) / 1_000_000;
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("minValue", min);
|
||||
response.put("durationMs", duration);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Service
|
||||
public class MatrixMinService {
|
||||
|
||||
/**
|
||||
* Находит минимальный элемент матрицы, расположенный ниже главной диагонали.
|
||||
* @param matrix входная матрица
|
||||
* @return минимальное значение среди элементов ниже главной диагонали
|
||||
*/
|
||||
public int findMinBelowMainDiagonal(int[][] matrix) {
|
||||
return IntStream.range(1, matrix.length)
|
||||
.parallel()
|
||||
.map(i -> {
|
||||
int rowMin = Integer.MAX_VALUE;
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < rowMin) {
|
||||
rowMin = matrix[i][j];
|
||||
}
|
||||
}
|
||||
return rowMin;
|
||||
})
|
||||
.min()
|
||||
.orElse(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
220
kuznetsov_danila_lab_3/README.md
Normal file
220
kuznetsov_danila_lab_3/README.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Лабораторная работа 3
|
||||
|
||||
## 1. Задание
|
||||
|
||||
Разработка распределённого приложения с использованием Spring Boot.
|
||||
Нужно реализовать параллельный вариант алгоритма с применением сервис-ориентированного подхода, замерить время работы алгоритма и распределить функционал между двумя сервисами:
|
||||
- **Compute Service:** Находит минимальный элемент матрицы ниже главной диагонали.
|
||||
- **Gateway Service:** Принимает запросы от клиента и пересылает их на Compute Service.
|
||||
|
||||
**Вариант:** Определить минимальный элемент матрицы ниже главной диагонали.
|
||||
|
||||
---
|
||||
|
||||
## 2. Описание кода
|
||||
|
||||
Приложение состоит из двух частей, каждая из которых – отдельное Spring Boot приложение.
|
||||
|
||||
### 2.1. Compute Service
|
||||
|
||||
Этот сервис отвечает за вычисления. Он принимает матрицу, запускает алгоритм поиска минимального элемента ниже главной диагонали, замеряет время выполнения и возвращает результат.
|
||||
|
||||
#### MatrixComputeApplication.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MatrixComputeApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MatrixComputeApplication.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### MatrixComputeController.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class MatrixComputeController {
|
||||
|
||||
@Autowired
|
||||
private MatrixMinService matrixMinService;
|
||||
|
||||
@PostMapping("/computeMin")
|
||||
public ResponseEntity<Map<String, Object>> computeMin(@RequestBody int[][] matrix) {
|
||||
long startTime = System.nanoTime();
|
||||
int min = matrixMinService.findMinBelowMainDiagonal(matrix);
|
||||
long endTime = System.nanoTime();
|
||||
double duration = (double) (endTime - startTime) / 1_000_000;
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("minValue", min);
|
||||
response.put("durationMs", duration);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### MatrixMinService.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Service
|
||||
public class MatrixMinService {
|
||||
|
||||
public int findMinBelowMainDiagonal(int[][] matrix) {
|
||||
return IntStream.range(1, matrix.length)
|
||||
.parallel()
|
||||
.map(i -> {
|
||||
int rowMin = Integer.MAX_VALUE;
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (matrix[i][j] < rowMin) {
|
||||
rowMin = matrix[i][j];
|
||||
}
|
||||
}
|
||||
return rowMin;
|
||||
})
|
||||
.min()
|
||||
.orElse(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.2. Gateway Service
|
||||
|
||||
Этот сервис принимает клиентские запросы на эндпоинте `/findMin` и пересылает их на Compute Service. В настройках Gateway указан URL Compute Service с его IP-адресом (в нашем случае, например, `http://192.168.10.101:8080/computeMin`).
|
||||
|
||||
#### MatrixGatewayApplication.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MatrixGatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MatrixGatewayApplication.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### MatrixGatewayController.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class MatrixGatewayController {
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
// Адрес Compute Service (IP контейнера с Compute Service)
|
||||
private String computeServiceUrl = "http://192.168.10.101:8080/computeMin";
|
||||
|
||||
@PostMapping("/findMin")
|
||||
public ResponseEntity<?> findMin(@RequestBody int[][] matrix) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<int[][]> request = new HttpEntity<>(matrix, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
computeServiceUrl,
|
||||
HttpMethod.POST,
|
||||
request,
|
||||
Map.class
|
||||
);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### RestTemplateConfig.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Запуск приложения
|
||||
|
||||
Сборка каждого приложения приводит к созданию jar-файлов. Запускаем их на разных контейнерах:
|
||||
|
||||
- **Compute Service:**
|
||||
На контейнере с IP `192.168.10.101` запускаем:
|
||||
```bash
|
||||
java -jar Lab-3.jar --server.port=8080
|
||||
```
|
||||
Compute Service будет доступен по адресу:
|
||||
```
|
||||
http://192.168.10.101:8080/computeMin
|
||||
```
|
||||
|
||||
- **Gateway Service:**
|
||||
На контейнере с IP `192.168.10.102` запускаем:
|
||||
```bash
|
||||
java -jar Lab-3.1.jar --server.port=8081
|
||||
```
|
||||
Gateway Service будет доступен по адресу:
|
||||
```
|
||||
http://192.168.10.102:8081/findMin
|
||||
```
|
||||
|
||||
Gateway Service перенаправляет запросы на Compute Service по указанному адресу.
|
||||
|
||||
---
|
||||
|
||||
## 4. Проверка работы
|
||||
|
||||
Для проверки работы можно использовать curl, отправив запрос на Gateway Service:
|
||||
|
||||
```bash
|
||||
curl -X POST http://192.168.10.102:8081/findMin \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '[[5, 3, 8], [2, 6, 4], [7, 1, 9]]'
|
||||
```
|
||||
|
||||
В ответ вы получите JSON с полями `minValue` (минимальное значение) и `durationMs` (время выполнения алгоритма в миллисекундах).
|
||||
38
kuznetsov_danila_lab_4/Lab 4/.gitignore
vendored
Normal file
38
kuznetsov_danila_lab_4/Lab 4/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
7
kuznetsov_danila_lab_4/Lab 4/ignite/README.txt
Normal file
7
kuznetsov_danila_lab_4/Lab 4/ignite/README.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
This is Apache Ignite working directory that contains information that
|
||||
Ignite nodes need in order to function normally.
|
||||
Don't delete it unless you're sure you know what you're doing.
|
||||
|
||||
You can change the location of working directory with
|
||||
igniteConfiguration.setWorkDirectory(location) or
|
||||
<property name="workDirectory" value="location"/> in IgniteConfiguration <bean>.
|
||||
@@ -0,0 +1,53 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.apache.ignite.Ignite;
|
||||
import org.apache.ignite.Ignition;
|
||||
import org.apache.ignite.configuration.IgniteConfiguration;
|
||||
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
|
||||
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
public class MatrixMinAlgorithm {
|
||||
public static void main(String[] args) {
|
||||
IgniteConfiguration cfg = new IgniteConfiguration();
|
||||
cfg.setPeerClassLoadingEnabled(false);
|
||||
|
||||
TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
|
||||
TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder();
|
||||
ipFinder.setAddresses(Collections.singletonList("127.0.0.1:47500..47509"));
|
||||
discoSpi.setIpFinder(ipFinder);
|
||||
cfg.setDiscoverySpi(discoSpi);
|
||||
|
||||
try (Ignite ignite = Ignition.start(cfg)) {
|
||||
final int n = 1000;
|
||||
final int[][] matrix = new int[n][n];
|
||||
Random rnd = new Random();
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
matrix[i][j] = rnd.nextInt(10000);
|
||||
}
|
||||
}
|
||||
|
||||
final int numTasks = 4;
|
||||
final int rowsPerTask = n / numTasks;
|
||||
int globalMin = Integer.MAX_VALUE;
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
for (int task = 0; task < numTasks; task++) {
|
||||
int startRow = task * rowsPerTask;
|
||||
int endRow = (task == numTasks - 1) ? n : startRow + rowsPerTask;
|
||||
|
||||
int localMin = ignite.compute().execute(new MatrixMinTask(matrix, startRow, endRow), null);
|
||||
globalMin = Math.min(globalMin, localMin);
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
System.out.println("Минимальный элемент ниже главной диагонали: " + globalMin);
|
||||
System.out.println("Общее время выполнения: " + (endTime - startTime) + " мс");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.apache.ignite.IgniteException;
|
||||
import org.apache.ignite.compute.ComputeJob;
|
||||
|
||||
public class MatrixMinJob implements ComputeJob {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
public MatrixMinJob(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute() throws IgniteException {
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i && j < matrix[i].length; j++) {
|
||||
localMin = Math.min(localMin, matrix[i][j]);
|
||||
}
|
||||
}
|
||||
return localMin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package ru.kuznec;
|
||||
|
||||
import org.apache.ignite.IgniteException;
|
||||
import org.apache.ignite.cluster.ClusterNode;
|
||||
import org.apache.ignite.compute.ComputeJob;
|
||||
import org.apache.ignite.compute.ComputeJobResult;
|
||||
import org.apache.ignite.compute.ComputeJobResultPolicy;
|
||||
import org.apache.ignite.compute.ComputeTask;
|
||||
import org.apache.ignite.compute.ComputeTaskName;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ComputeTaskName("MatrixMinTaskUser")
|
||||
public class MatrixMinTask implements ComputeTask<Void, Integer> {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
public MatrixMinTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid, Void arg) throws IgniteException {
|
||||
ComputeJob job = new MatrixMinJob(matrix, startRow, endRow);
|
||||
return Collections.singletonMap(job, subgrid.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputeJobResultPolicy result(ComputeJobResult res, List<ComputeJobResult> received) throws IgniteException {
|
||||
return ComputeJobResultPolicy.WAIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer reduce(List<ComputeJobResult> results) throws IgniteException {
|
||||
int globalMin = Integer.MAX_VALUE;
|
||||
for (ComputeJobResult res : results) {
|
||||
globalMin = Math.min(globalMin, res.getData());
|
||||
}
|
||||
return globalMin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
artifactId=Lab
|
||||
groupId=ru.kuznec
|
||||
version=4
|
||||
@@ -0,0 +1,3 @@
|
||||
ru\kuznec\MatrixMinJob.class
|
||||
ru\kuznec\MatrixMinTask.class
|
||||
ru\kuznec\MatrixMinAlgorithm.class
|
||||
@@ -0,0 +1,3 @@
|
||||
C:\Users\kuzne\Documents\УЛГТУ\study\2 курс\2\ССПР\Lab 4\src\main\java\ru\kuznec\MatrixMinJob.java
|
||||
C:\Users\kuzne\Documents\УЛГТУ\study\2 курс\2\ССПР\Lab 4\src\main\java\ru\kuznec\MatrixMinTask.java
|
||||
C:\Users\kuzne\Documents\УЛГТУ\study\2 курс\2\ССПР\Lab 4\src\main\java\ru\kuznec\MatrixMinAlgorithm.java
|
||||
199
kuznetsov_danila_lab_4/README.md
Normal file
199
kuznetsov_danila_lab_4/README.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Лабораторная работа 4
|
||||
|
||||
## Задание
|
||||
|
||||
Разработка распределенного приложения с использованием платформы Apache Ignite.
|
||||
Необходимо разработать параллельный вариант алгоритма с применением подхода Grid Gain и платформа Apache Ignite, замерить время его работы.
|
||||
|
||||
**Вариант:** Определить минимальный элемент матрицы ниже главной диагонали.
|
||||
|
||||
## Описание работы приложения
|
||||
|
||||
В решении используется Apache Ignite для распределения вычислений. Программа делает следующее:
|
||||
1. Настраивает Ignite с нужной конфигурацией.
|
||||
2. Генерирует квадратную матрицу 1000×1000, заполненную случайными числами от 0 до 9999.
|
||||
3. Делит матрицу на 4 части по строкам. Для каждой части запускается ComputeTask, которая распределяет работу ComputeJob, ищущих локальный минимум среди элементов ниже главной диагонали.
|
||||
4. Полученные локальные минимумы объединяются для нахождения глобального минимума, и замеряется общее время выполнения.
|
||||
|
||||
---
|
||||
|
||||
## Объяснение
|
||||
### MatrixMinAlgorithm
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.apache.ignite.Ignite;
|
||||
import org.apache.ignite.Ignition;
|
||||
import org.apache.ignite.configuration.IgniteConfiguration;
|
||||
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
|
||||
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
public class MatrixMinAlgorithm {
|
||||
public static void main(String[] args) {
|
||||
IgniteConfiguration cfg = new IgniteConfiguration();
|
||||
cfg.setPeerClassLoadingEnabled(false);
|
||||
|
||||
TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
|
||||
TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder();
|
||||
|
||||
ipFinder.setAddresses(Collections.singletonList("127.0.0.1:47500..47509"));
|
||||
discoSpi.setIpFinder(ipFinder);
|
||||
cfg.setDiscoverySpi(discoSpi);
|
||||
|
||||
try (Ignite ignite = Ignition.start(cfg)) {
|
||||
final int n = 1000;
|
||||
final int[][] matrix = new int[n][n];
|
||||
Random rnd = new Random();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
matrix[i][j] = rnd.nextInt(10000);
|
||||
}
|
||||
}
|
||||
|
||||
final int numTasks = 4;
|
||||
final int rowsPerTask = n / numTasks;
|
||||
int globalMin = Integer.MAX_VALUE;
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
for (int task = 0; task < numTasks; task++) {
|
||||
int startRow = task * rowsPerTask;
|
||||
int endRow = (task == numTasks - 1) ? n : startRow + rowsPerTask;
|
||||
|
||||
int localMin = ignite.compute().execute(new MatrixMinTask(matrix, startRow, endRow), null);
|
||||
globalMin = Math.min(globalMin, localMin);
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
System.out.println("Минимальный элемент ниже главной диагонали: " + globalMin);
|
||||
System.out.println("Общее время выполнения: " + (endTime - startTime) + " мс");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Объяснение:*
|
||||
В этом файле происходит настройка Ignite и запуск узла. Затем генерируется матрица случайных чисел. Матрица делится на 4 части, и для каждой части запускается задача (MatrixMinTask), которая возвращает локальный минимум. В конце все локальные минимумы сравниваются, и выводится глобальный минимум, а также время работы.
|
||||
|
||||
---
|
||||
|
||||
### MatrixMinTask.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.apache.ignite.IgniteException;
|
||||
import org.apache.ignite.cluster.ClusterNode;
|
||||
import org.apache.ignite.compute.ComputeJob;
|
||||
import org.apache.ignite.compute.ComputeJobResult;
|
||||
import org.apache.ignite.compute.ComputeJobResultPolicy;
|
||||
import org.apache.ignite.compute.ComputeTask;
|
||||
import org.apache.ignite.compute.ComputeTaskName;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ComputeTaskName("MatrixMinTaskUser")
|
||||
public class MatrixMinTask implements ComputeTask<Void, Integer> {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
public MatrixMinTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid, Void arg) throws IgniteException {
|
||||
ComputeJob job = new MatrixMinJob(matrix, startRow, endRow);
|
||||
return Collections.singletonMap(job, subgrid.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputeJobResultPolicy result(ComputeJobResult res, List<ComputeJobResult> received) throws IgniteException {
|
||||
return ComputeJobResultPolicy.WAIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer reduce(List<ComputeJobResult> results) throws IgniteException {
|
||||
int globalMin = Integer.MAX_VALUE;
|
||||
|
||||
for (ComputeJobResult res : results) {
|
||||
globalMin = Math.min(globalMin, res.getData());
|
||||
}
|
||||
|
||||
return globalMin;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Объяснение:*
|
||||
Этот класс реализует интерфейс ComputeTask, который отвечает за распределение работы по узлам и объединение результатов.
|
||||
- В методе **map()** создается задание (ComputeJob) для заданного диапазона строк и привязывается к первому узлу в списке.
|
||||
- Метод **result()** всегда возвращает политику WAIT, чтобы дождаться выполнения всех заданий.
|
||||
- Метод **reduce()** проходит по всем полученным результатам и выбирает наименьшее значение.
|
||||
Аннотация @ComputeTaskName("MatrixMinTaskUser") задаёт имя задачи, чтобы Ignite не считал её системной.
|
||||
|
||||
---
|
||||
|
||||
### MatrixMinJob.java
|
||||
|
||||
```java
|
||||
package ru.kuznec;
|
||||
|
||||
import org.apache.ignite.IgniteException;
|
||||
import org.apache.ignite.compute.ComputeJob;
|
||||
|
||||
public class MatrixMinJob implements ComputeJob {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
public MatrixMinJob(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute() throws IgniteException {
|
||||
int localMin = Integer.MAX_VALUE;
|
||||
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i && j < matrix[i].length; j++) {
|
||||
localMin = Math.min(localMin, matrix[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
return localMin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {}
|
||||
}
|
||||
```
|
||||
|
||||
*Объяснение:*
|
||||
Класс MatrixMinJob реализует ComputeJob и отвечает за выполнение вычислительной части задачи.
|
||||
В методе **execute()** происходит проход по заданному диапазону строк матрицы. Для каждой строки ищутся элементы с индексами от 0 до i‑1 (то есть элементы, находящиеся ниже главной диагонали). Вычисляется локальный минимум, который затем возвращается. Метод **cancel()** оставлен пустым, так как отмена не требуется по заданию.
|
||||
|
||||
---
|
||||
|
||||
## Запуск
|
||||
|
||||
Запустить jar можно командой (обязательно Java 8):
|
||||
|
||||
```bash
|
||||
java -jar Lab-4-jar-with-dependencies.jar
|
||||
```
|
||||
|
||||
При запуске Ignite запускает узел, распределяет задачу по 4 частям матрицы, и в конце выводится минимальный элемент и общее время выполнения.
|
||||
103
pokladov_nikita_lab_2/MatrixSortMPI.java
Normal file
103
pokladov_nikita_lab_2/MatrixSortMPI.java
Normal file
@@ -0,0 +1,103 @@
|
||||
import mpi.*;
|
||||
import java.net.*;
|
||||
|
||||
public class MatrixSortMPI {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Инициализация MPI
|
||||
MPI.Init(args);
|
||||
|
||||
// Получение ранга и количества процессов
|
||||
int rank = MPI.COMM_WORLD.Rank();
|
||||
int size = MPI.COMM_WORLD.Size();
|
||||
|
||||
// Получение IP-адреса машины
|
||||
String ipAddress = "Unknown";
|
||||
try {
|
||||
ipAddress = InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("Процесс " + rank + " запущен на IP: " + ipAddress);
|
||||
|
||||
// Размер матрицы
|
||||
int rows = 10000;
|
||||
int cols = 1000;
|
||||
|
||||
int rowsPerProcess = rows / size; // Количество строк, обрабатываемых каждым процессом
|
||||
int[][] localMatrix = new int[rowsPerProcess][cols]; // Локальная матрица для каждого процесса
|
||||
int[] localRowSums = new int[rowsPerProcess]; // Массив для хранения сумм строк
|
||||
|
||||
// Начало измерения времени
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 1. Генерация матрицы (каждый процесс генерирует свою часть)
|
||||
System.out.println("Процесс " + rank + " считает сумму строк...");
|
||||
for (int i = 0; i < rowsPerProcess; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
localMatrix[i][j] = (int) (Math.random() * 100);
|
||||
}
|
||||
}
|
||||
long genTime = System.currentTimeMillis();
|
||||
|
||||
// 2. Подсчет суммы строк
|
||||
System.out.println("Процесс " + rank + " считает сумму строк...");
|
||||
for (int i = 0; i < rowsPerProcess; i++) {
|
||||
int sum = 0;
|
||||
for (int j = 0; j < cols; j++) {
|
||||
sum += localMatrix[i][j];
|
||||
}
|
||||
localRowSums[i] = sum;
|
||||
}
|
||||
long sumTime = System.currentTimeMillis();
|
||||
|
||||
// 3. Сбор сумм строк на процессе 0
|
||||
int[] allRowSums = new int[rows];
|
||||
MPI.COMM_WORLD.Gather(localRowSums, 0, rowsPerProcess, MPI.INT, allRowSums, 0, rowsPerProcess, MPI.INT, 0);
|
||||
|
||||
// 4. Сортировка индексов на процессе 0
|
||||
int[] sortedIndexes = new int[rows];
|
||||
if (rank == 0) {
|
||||
System.out.println("Процесс 0 сортирует строки...");
|
||||
for (int i = 0; i < rows; i++) {
|
||||
sortedIndexes[i] = i; // Инициализируем массив индексов строк
|
||||
}
|
||||
|
||||
// Сортируем индексы по суммам строк
|
||||
for (int i = 0; i < rows - 1; i++) {
|
||||
for (int j = i + 1; j < rows; j++) {
|
||||
if (allRowSums[sortedIndexes[i]] < allRowSums[sortedIndexes[j]]) {
|
||||
int temp = sortedIndexes[i];
|
||||
sortedIndexes[i] = sortedIndexes[j];
|
||||
sortedIndexes[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
long sortTime = System.currentTimeMillis();
|
||||
|
||||
// 5. Перестановка строк согласно отсортированным индексам
|
||||
MPI.COMM_WORLD.Bcast(sortedIndexes, 0, rows, MPI.INT, 0); // Рассылаем отсортированные индексы всем процессам
|
||||
|
||||
// Локальная перестановка строк
|
||||
System.out.println("Процесс " + rank + " выполняет перестановку строк...");
|
||||
int[][] sortedMatrix = new int[rowsPerProcess][cols];
|
||||
for (int i = 0; i < rowsPerProcess; i++) {
|
||||
int globalRowIndex = rank * rowsPerProcess + i; // Глобальный индекс строки
|
||||
sortedMatrix[i] = localMatrix[i]; // По умолчанию строка остается без изменений
|
||||
}
|
||||
long replaceTime = System.currentTimeMillis();
|
||||
|
||||
// Завершение работы MPI
|
||||
MPI.Finalize();
|
||||
long endTime = System.currentTimeMillis();
|
||||
if (rank == 0) {
|
||||
System.out.println("\nВремя выполнения генерации: " + (genTime - startTime) + " миллисекунд");
|
||||
System.out.println("\nВремя выполнения суммтрования: " + (sumTime - genTime) + " миллисекунд");
|
||||
System.out.println("\nВремя выполнения сортровки: " + (sortTime - sumTime) + " миллисекунд");
|
||||
System.out.println("\nВремя выполнения перестановки: " + (replaceTime - sortTime) + " миллисекунд");
|
||||
System.out.println("\nВремя выполнения: " + (endTime - startTime) + " миллисекунд");
|
||||
}
|
||||
}
|
||||
}
|
||||
65
pokladov_nikita_lab_2/README.md
Normal file
65
pokladov_nikita_lab_2/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Лабораторная №1
|
||||
Разработка параллельного MPI приложения на языке Java.
|
||||
Необходимо разработать параллельный вариант алгоритма с применением MPI и
|
||||
замерить время его работы. В рамках работы программы должно быть две копии
|
||||
приложения, которые соединяются друг с другом по сети. Сообщения о статусе
|
||||
соединения (например, что соединение установлено) должны выводиться в консоль.
|
||||
Запуск каждого экземпляра MPI происходит в своём LXC контейнере. Такие
|
||||
сервисы, как Docker, использовать нельзя.
|
||||
|
||||
## Вариант и задание
|
||||
17 Упорядочить строки матрицы по убыванию суммы элементов.
|
||||
|
||||
## Как запустить лабораторную работу
|
||||
* Скачать MPJ Express на два (или более) контейнера
|
||||
* Добавить переменные окружения MPJ_HOME
|
||||
> export MPJ_HOME=/path/to/mpj/
|
||||
> export PATH=$MPJ_HOME/bin:$PATH
|
||||
* При компиляции программы нужно использовать скачанные классы, скомпилировать во всех контейнерах
|
||||
> javac -cp ~$MPJ_HOME/lib/mpj.jar MatrixSortMPI.java
|
||||
* Запустить MPJ Daemon в каждом контейнере (используется файл macines с IP)
|
||||
> mpjboot machines
|
||||
* Запуск программы (с отключением всего вывода кроме консольного)
|
||||
> mpjrun.sh -np 2 -dev niodev MatrixSortMPI 2>/dev/null
|
||||
mpj для кластерного режима использует использует ssh, поэтому надо настроить его между контейнерами.
|
||||
|
||||
## Какие технологии использовались
|
||||
Программа использует MPI (Message Passing Interface) для параллельных вычислений и обмена данными между процессами, Java для реализации алгоритма (работа с массивами, генерация случайных чисел, измерение времени) и LXC-контейнеры для изоляции процессов и эмуляции распределённой системы.
|
||||
|
||||
## Что она делает
|
||||
Программа генерирует матрицу, распределяет её части между процессами, вычисляет суммы строк, собирает результаты на главном процессе, сортирует строки по убыванию суммы и переставляет их. В конце выводится время выполнения каждого этапа для анализа производительности.
|
||||
|
||||
## Результаты работы
|
||||
```
|
||||
MPJ Express (0.44) is started in the cluster configuration with niodev
|
||||
Starting process <0> on <CT116>
|
||||
Starting process <1> on <CT118>
|
||||
Процесс 0 запущен на 192.168.16.116
|
||||
Процесс 0: генерация матрицы
|
||||
Процесс 1 запущен на 192.168.16.118
|
||||
Процесс 0: генерация матрицы
|
||||
Процесс 1: получение данных через Scatter
|
||||
Процесс 0: получение данных через Scatter
|
||||
Процесс 0: вычисление сумм строк
|
||||
Процесс 1: вычисление сумм строк
|
||||
Процесс 1: получение отсортированных индексов через Bcast
|
||||
Процесс 0: сортировка строк по сумме
|
||||
Процесс 0: получение отсортированных индексов через Bcast
|
||||
Процесс 0: перестановка строк
|
||||
Процесс 1: перестановка строк
|
||||
Процесс 0: отправка данных через Gather
|
||||
Процесс 1: отправка данных через Gather
|
||||
Процесс 0: финальные результаты
|
||||
Время генерации: 117 мс
|
||||
Время суммирования: 31 мс
|
||||
Время сортировки: 101 мс
|
||||
Время перестановки: 58 мс
|
||||
Общее время: 421 мс
|
||||
Stopping Process <0> on <CT116>
|
||||
Stopping Process <1> on <CT118>
|
||||
```
|
||||
|
||||
## Вывод
|
||||
Программа успешно выполняет параллельную сортировку строк матрицы по убыванию суммы их элементов с использованием MPI. Время выполнения каждого этапа выводится в консоль, что позволяет оценить эффективность работы программы. Контейнеры успешно взаимодействуют между собой, обмениваются данными и выдают результат.
|
||||
|
||||
|
||||
249
shurygin_dima_lab_1/DividingByAverage.java
Normal file
249
shurygin_dima_lab_1/DividingByAverage.java
Normal file
@@ -0,0 +1,249 @@
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
|
||||
public class DividingByAverage {
|
||||
public static void main(String[] args) {
|
||||
Random random = new Random();
|
||||
|
||||
// Создание массива
|
||||
double[][] array = new double[8000][8000];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = random.nextInt(101);
|
||||
}
|
||||
}
|
||||
|
||||
// Создание копии исходного массива
|
||||
double[][] arraySave = new double[array.length][array.length];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
arraySave[i][j] = array[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Тест однопоточного алгоритма
|
||||
System.out.println("Однопоточный алгоритм:");
|
||||
long startTime1 = System.nanoTime();
|
||||
singleThreadedAlgorithm(array);
|
||||
long endTime1 = System.nanoTime();
|
||||
long duration1 = endTime1 - startTime1;
|
||||
System.out.println("Время выполнения: " + (duration1 / 1000000) + " мс");
|
||||
System.out.println(" ");
|
||||
|
||||
// Восстановление массива
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = arraySave[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Тест многопоточного алгоритма с ThreadPoolExecutor
|
||||
System.out.println("Алгоритм с ThreadPoolExecutor:");
|
||||
long startTime2 = System.nanoTime();
|
||||
threadPoolExecutorAlgorithm(array);
|
||||
long endTime2 = System.nanoTime();
|
||||
long duration2 = endTime2 - startTime2;
|
||||
System.out.println("Время выполнения: " + (duration2 / 1000000) + " мс");
|
||||
System.out.println(" ");
|
||||
|
||||
// Восстановление массива
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = arraySave[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Тест многопоточного алгоритма с ForkJoinPool
|
||||
System.out.println("Алгоритм с ForkJoinPool:");
|
||||
long startTime3 = System.nanoTime();
|
||||
forkJoinPoolAlgorithm(array);
|
||||
long endTime3 = System.nanoTime();
|
||||
long duration3 = endTime3 - startTime3;
|
||||
System.out.println("Время выполнения: " + (duration3 / 1000000) + " мс");
|
||||
System.out.println(" ");
|
||||
|
||||
// Восстановление массива
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = arraySave[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Однопоточный алгоритм
|
||||
public static void singleThreadedAlgorithm(double[][] array) {
|
||||
// Сумма всех элементов массива
|
||||
double sum = 0;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
sum += array[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Среднее арифметическое
|
||||
double average = (double) sum / (array.length * array.length);
|
||||
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
System.out.println("Среднее арифметическое: " + average);
|
||||
|
||||
// Деление элементов массива на среднее
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = array[i][j] / average;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
}
|
||||
|
||||
// Тест многопоточного алгоритма с ThreadPoolExecutor
|
||||
public static void threadPoolExecutorAlgorithm(double[][] array) {
|
||||
int size = array.length;
|
||||
// Количество потоков
|
||||
int threads = 4;
|
||||
// Создаем пул потоков
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threads);
|
||||
// Используем AtomicLong для безопасного накопления суммы в потоках
|
||||
AtomicLong totalSum = new AtomicLong(0);
|
||||
|
||||
// Считаем сумму элементов массива с использованием потоков
|
||||
for (int t = 0; t < threads; t++) {
|
||||
// Начало потока
|
||||
final int startRow = t * (size / threads);
|
||||
// Конец потока
|
||||
final int endRow = (t == threads - 1) ? size : (t + 1) * (size / threads);
|
||||
executor.submit(() -> {
|
||||
// Считаем сумму в пределах выделенного потоку диапазона строк
|
||||
double localSum = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
localSum += array[i][j];
|
||||
}
|
||||
}
|
||||
// Добавляем локальную сумму в атомарную переменную.
|
||||
// Преобразуем в long для атомарного сложения
|
||||
totalSum.addAndGet((long) localSum);
|
||||
});
|
||||
}
|
||||
|
||||
// Делим элементы массива на среднее значение
|
||||
for (int t = 0; t < threads; t++) {
|
||||
final int startRow = t * (size / threads);
|
||||
final int endRow = (t == threads - 1) ? size : (t + 1) * (size / threads);
|
||||
executor.submit(() -> {
|
||||
// Разделяем элементы массива на среднее значение
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
array[i][j] /= totalSum.get() / (double) (size * size);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Закрываем пул после того, как все задачи были отправлены
|
||||
executor.shutdown();
|
||||
|
||||
// Ожидаем завершения всех задач
|
||||
while (!executor.isTerminated()) {
|
||||
// Ждем завершения всех задач
|
||||
}
|
||||
|
||||
double average = totalSum.get() / (double) (size * size);
|
||||
System.out.println("Среднее арифметическое: " + average);
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
}
|
||||
|
||||
// Порог разбиения
|
||||
private static final int THRESHOLD = 50000;
|
||||
|
||||
// Многопоточный алгоритм с ForkJoinPool
|
||||
public static void forkJoinPoolAlgorithm(double[][] array) {
|
||||
int size = array.length;
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
|
||||
// Подсчет суммы
|
||||
SumTask sumTask = new SumTask(array, 0, size);
|
||||
double totalSum = forkJoinPool.invoke(sumTask);
|
||||
|
||||
// Вычисление среднего
|
||||
double average = totalSum / (size * size);
|
||||
System.out.println("Среднее арифметическое: " + average);
|
||||
|
||||
// Деление массива
|
||||
forkJoinPool.invoke(new NormalizeTask(array, 0, size, average));
|
||||
|
||||
forkJoinPool.shutdown();
|
||||
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
}
|
||||
|
||||
// Подсчет суммы
|
||||
static class SumTask extends RecursiveTask<Double> {
|
||||
private final double[][] array;
|
||||
private final int startRow, endRow;
|
||||
|
||||
SumTask(double[][] array, int startRow, int endRow) {
|
||||
this.array = array;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double compute() {
|
||||
// Если строк меньше порога, считаем напрямую
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
double sum = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < array[i].length; j++) {
|
||||
sum += array[i][j];
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
} else {
|
||||
// Разделяем массив на две части
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
|
||||
SumTask top = new SumTask(array, startRow, midRow);
|
||||
SumTask bottom = new SumTask(array, midRow, endRow);
|
||||
|
||||
// Запускаем верхнюю часть в новом потоке
|
||||
top.fork();
|
||||
// Вычисляем нижнюю часть в текущем потоке
|
||||
double bottomResult = bottom.compute();
|
||||
// Ждём завершения верхней части и суммируем
|
||||
return top.join() + bottomResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Деление массива
|
||||
static class NormalizeTask extends RecursiveAction {
|
||||
private final double[][] array;
|
||||
private final int startRow, endRow;
|
||||
private final double average;
|
||||
|
||||
NormalizeTask(double[][] array, int startRow, int endRow, double average) {
|
||||
this.array = array;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
this.average = average;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < array[i].length; j++) {
|
||||
array[i][j] /= average;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
invokeAll(new NormalizeTask(array, startRow, midRow, average),
|
||||
new NormalizeTask(array, midRow, endRow, average));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
112
shurygin_dima_lab_1/README.md
Normal file
112
shurygin_dima_lab_1/README.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Лабораторная №1
|
||||
Разработка многопоточного приложения с использованием Java Concurrency
|
||||
согласно варианту задания.
|
||||
|
||||
Необходимо:
|
||||
1) Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2) Разработать параллельный вариант алгоритма с использованием
|
||||
ThreadPoolExecutor и замерить время его работы
|
||||
3) Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
|
||||
и замерить время его работы.
|
||||
|
||||
Массив генерируется до работы всех вариантов алгоритмов. Все три
|
||||
алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
## Вариант и задание
|
||||
3) Разделить элементы матрицы на среднее арифметическое всех ее элементов.
|
||||
|
||||
### Описание
|
||||
Данная программа разработана для сравнения производительности однопоточного и многопоточного подходов к делению элементов матрицы на среднее арифметическое её элементов. Задача состоит из двух основных частей:
|
||||
|
||||
1. Вычисление среднего арифметического
|
||||
2. Деление элементов матрицы на среднее арифметическое
|
||||
|
||||
Эти этапы выполняются с разными вариантами многопоточности, чтобы выявить их влияние на производительность.
|
||||
|
||||
### Как запустить
|
||||
```sh
|
||||
javac DividingByAverage.java
|
||||
java DividingByAverage
|
||||
```
|
||||
|
||||
### Используемые технологии
|
||||
Программа использует Java Concurrency, включая классы и интерфейсы:
|
||||
1) `ExecutorService`
|
||||
2) `ThreadPoolExecutor`
|
||||
3) `ForkJoinPool`
|
||||
4) `RecursiveAction`
|
||||
|
||||
### Что делает программа
|
||||
1. Генерирует матрицу заданного размера, заполненную случайными значениями.
|
||||
2. Вычисляет среднее арифметическое элементов матрицы.
|
||||
3. Делит все элементы матрицы на среднее арифметическое.
|
||||
4. Замеряет время выполнения для однопоточного и двух многопоточных методов.
|
||||
|
||||
## Результаты тестов
|
||||
|
||||
### Для матрцы 5000*5000:
|
||||
|
||||
```
|
||||
Однопоточный алгоритм:
|
||||
array[100][100] = 15.0
|
||||
Среднее арифметическое: 50.00305776
|
||||
array[100][100] = 0.29998165456191894
|
||||
Время выполнения: 59 мс
|
||||
|
||||
Алгоритм с ThreadPoolExecutor:
|
||||
Среднее арифметическое: 50.00305776
|
||||
array[100][100] = 0.29998165456191894
|
||||
Время выполнения: 56 мс
|
||||
|
||||
Алгоритм с ForkJoinPool:
|
||||
Среднее арифметическое: 50.00305776
|
||||
array[100][100] = 0.29998165456191894
|
||||
Время выполнения: 66 мс
|
||||
```
|
||||
|
||||
### Для матрцы 10000*10000:
|
||||
|
||||
```
|
||||
Однопоточный алгоритм:
|
||||
array[100][100] = 38.0
|
||||
Среднее арифметическое: 50.00220663
|
||||
array[100][100] = 0.7599664607041763
|
||||
Время выполнения: 186 мс
|
||||
|
||||
Алгоритм с ThreadPoolExecutor:
|
||||
Среднее арифметическое: 50.00220663
|
||||
array[100][100] = 0.7599664607041763
|
||||
Время выполнения: 140 мс
|
||||
|
||||
Алгоритм с ForkJoinPool:
|
||||
Среднее арифметическое: 50.00220663
|
||||
array[100][100] = 0.7599664607041763
|
||||
Время выполнения: 174 мс
|
||||
```
|
||||
|
||||
### Для матрцы 15000*15000:
|
||||
|
||||
```
|
||||
Однопоточный алгоритм:
|
||||
array[100][100] = 4.0
|
||||
Среднее арифметическое: 49.99820225777778
|
||||
array[100][100] = 0.08000287649097934
|
||||
Время выполнения: 388 мс
|
||||
|
||||
Алгоритм с ThreadPoolExecutor:
|
||||
Среднее арифметическое: 49.99820225777778
|
||||
array[100][100] = 0.08000287649097934
|
||||
Время выполнения: 323 мс
|
||||
|
||||
Алгоритм с ForkJoinPool:
|
||||
Среднее арифметическое: 49.99820225777778
|
||||
array[100][100] = 0.08000287649097934
|
||||
Время выполнения: 379 мс
|
||||
```
|
||||
|
||||
## Вывод ##
|
||||
Пока матрицы небольших размеров, можно заметить, что многопоточные алгоритмы могут не иметь преимущества по времени выполнения перед однопоточным алгоритмом или даже быть хуже.
|
||||
Это происходит из-за того, что ThreadPool и ForkJoin затрачивают дополнительные ресурсы на создание и поддержание потоков.
|
||||
|
||||
По мере роста размеров матрицы, многопоточные алгоритмы показывают большую эффективность, чем однопоточный. Однопоточный алгоритм начинает работать медленнее из-за большого количества элементов.
|
||||
ThreadPool и ForkJoin начинают работать быстрее, так как дополнительные траты на поддержание потоков с лихвой компенсируются эффективностью многопоточного метода решения задачи.
|
||||
85
tangatarov_ivan_lab_1/src/ExecutorThreadPool.java
Normal file
85
tangatarov_ivan_lab_1/src/ExecutorThreadPool.java
Normal file
@@ -0,0 +1,85 @@
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ExecutorThreadPool {
|
||||
private final int[][] array;
|
||||
private final ThreadPoolExecutor threadPoolExecutor;
|
||||
private static final int THRESHOLD = 20;
|
||||
|
||||
public ExecutorThreadPool(int countThread, int[][] array) {
|
||||
this.threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(countThread);
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public int[][] operationMatrixThreadPoolExecutor() throws ExecutionException, InterruptedException {
|
||||
int[][] result = operationMatrix(array);
|
||||
threadPoolExecutor.shutdown();
|
||||
return result;
|
||||
}
|
||||
|
||||
private int[][] operationMatrix(int[][] array) throws ExecutionException, InterruptedException {
|
||||
if (array.length <= 1) {
|
||||
return array;
|
||||
}
|
||||
if (array.length <= THRESHOLD) {
|
||||
return singleOperationMatrix(array);
|
||||
}
|
||||
|
||||
int mid = array.length / 2;
|
||||
int[][] leftPart = Arrays.copyOfRange(array, 0, mid);
|
||||
int[][] rightPart = Arrays.copyOfRange(array, mid, array.length);
|
||||
|
||||
Future<int[][]> futureLeft = threadPoolExecutor.submit(() -> singleOperationMatrix(leftPart));
|
||||
Future<int[][]> futureRight = threadPoolExecutor.submit(() -> singleOperationMatrix(rightPart));
|
||||
|
||||
int[][] left = futureLeft.get();
|
||||
int[][] right = futureRight.get();
|
||||
|
||||
return merge(left, right);
|
||||
}
|
||||
|
||||
private int[][] singleOperationMatrix(int[][] array) {
|
||||
if (array.length <= 1) {
|
||||
return array;
|
||||
}
|
||||
|
||||
int mid = array.length / 2;
|
||||
int[][] left = Arrays.copyOfRange(array, 0, mid);
|
||||
int[][] right = Arrays.copyOfRange(array, mid, array.length);
|
||||
|
||||
left = singleOperationMatrix(left);
|
||||
right = singleOperationMatrix(right);
|
||||
|
||||
return merge(left, right);
|
||||
}
|
||||
|
||||
private int[][] merge(int[][] left, int[][] right) {
|
||||
int[][] result = new int[left.length + right.length][];
|
||||
int i = 0, j = 0, k = 0;
|
||||
|
||||
while (i < left.length && j < right.length) {
|
||||
if (findMaxElementRow(left[i]) >= findMaxElementRow(right[j])) {
|
||||
result[k++] = left[i++];
|
||||
} else {
|
||||
result[k++] = right[j++];
|
||||
}
|
||||
}
|
||||
|
||||
while (i < left.length) {
|
||||
result[k++] = left[i++];
|
||||
}
|
||||
while (j < right.length) {
|
||||
result[k++] = right[j++];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int findMaxElementRow(int[] row){
|
||||
int maxCurrentValue = Integer.MIN_VALUE;
|
||||
for(int i = 0; i < row.length; i++){
|
||||
maxCurrentValue = (row[i] >= maxCurrentValue ? row[i] : maxCurrentValue);
|
||||
}
|
||||
return maxCurrentValue;
|
||||
}
|
||||
}
|
||||
86
tangatarov_ivan_lab_1/src/ForkJoinT.java
Normal file
86
tangatarov_ivan_lab_1/src/ForkJoinT.java
Normal file
@@ -0,0 +1,86 @@
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
|
||||
public class ForkJoinT extends RecursiveTask<int[][]> {
|
||||
private final int[][] array;
|
||||
private static final int THRESHOLD = 20;
|
||||
|
||||
public ForkJoinT(int[][] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public int[][] operationMatrixForkJoinPool(){
|
||||
ForkJoinPool pool = new ForkJoinPool();
|
||||
ForkJoinT task = new ForkJoinT(array);
|
||||
return pool.invoke(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[][] compute() {
|
||||
if (array.length <= 1) {
|
||||
return array;
|
||||
}
|
||||
if (array.length <= THRESHOLD) {
|
||||
return singleOperationMatrix(array);
|
||||
}
|
||||
|
||||
int mid = array.length / 2;
|
||||
int[][] leftPart = Arrays.copyOfRange(array, 0, mid);
|
||||
int[][] rightPart = Arrays.copyOfRange(array, mid, array.length);
|
||||
|
||||
ForkJoinT leftTask = new ForkJoinT(leftPart);
|
||||
ForkJoinT rightTask = new ForkJoinT(rightPart);
|
||||
|
||||
leftTask.fork();
|
||||
int[][] right = rightTask.compute();
|
||||
int[][] left = leftTask.join();
|
||||
|
||||
return merge(left, right);
|
||||
}
|
||||
|
||||
private int[][] singleOperationMatrix(int[][] array) {
|
||||
if (array.length <= 1) {
|
||||
return array;
|
||||
}
|
||||
|
||||
int mid = array.length / 2;
|
||||
int[][] left = Arrays.copyOfRange(array, 0, mid);
|
||||
int[][] right = Arrays.copyOfRange(array, mid, array.length);
|
||||
|
||||
left = singleOperationMatrix(left);
|
||||
right = singleOperationMatrix(right);
|
||||
|
||||
return merge(left, right);
|
||||
}
|
||||
|
||||
private int[][] merge(int[][] left, int[][] right) {
|
||||
int[][] result = new int[left.length + right.length][];
|
||||
int i = 0, j = 0, k = 0;
|
||||
|
||||
while (i < left.length && j < right.length) {
|
||||
if (findMaxElementRow(left[i]) >= findMaxElementRow(right[j])) {
|
||||
result[k++] = left[i++];
|
||||
} else {
|
||||
result[k++] = right[j++];
|
||||
}
|
||||
}
|
||||
|
||||
while (i < left.length) {
|
||||
result[k++] = left[i++];
|
||||
}
|
||||
while (j < right.length) {
|
||||
result[k++] = right[j++];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int findMaxElementRow(int[] row) {
|
||||
int maxCurrentValue = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < row.length; i++) {
|
||||
maxCurrentValue = (row[i] >= maxCurrentValue ? row[i] : maxCurrentValue);
|
||||
}
|
||||
return maxCurrentValue;
|
||||
}
|
||||
}
|
||||
94
tangatarov_ivan_lab_1/src/Program.java
Normal file
94
tangatarov_ivan_lab_1/src/Program.java
Normal file
@@ -0,0 +1,94 @@
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Program {
|
||||
private static final int ARRAYS_LENGTH = 1000;
|
||||
private static final String PATH_FOR_TESTS = "./tangatarov_ivan_lab_1/src/tests/";
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException {
|
||||
|
||||
int[][] array = GenerateMatrix(ARRAYS_LENGTH);
|
||||
int[][] arraySinge = deepCopy(array);
|
||||
int[][] arrayThreadPool = deepCopy(array);
|
||||
int[][] arrayForkJoin = deepCopy(array);
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
|
||||
|
||||
SingleThread single = new SingleThread(arraySinge);
|
||||
ExecutorThreadPool executor = new ExecutorThreadPool(Runtime.getRuntime().availableProcessors(), arrayThreadPool);
|
||||
ForkJoinT forkJoin = new ForkJoinT(arrayForkJoin);
|
||||
|
||||
|
||||
System.out.println("result single: ");
|
||||
long diapStartTestOne = System.nanoTime();
|
||||
int[][] resultSingleThread = single.OperationMatrixSingleThread();
|
||||
long diapEndTestOne = System.nanoTime();
|
||||
System.out.println((diapEndTestOne - diapStartTestOne)/1000000 + " ms");
|
||||
|
||||
|
||||
System.out.println("result threadPool: ");
|
||||
long diapStartTestTwo = System.nanoTime();
|
||||
int[][] resultThreadPool = executor.operationMatrixThreadPoolExecutor();
|
||||
long diapEndTestTwo = System.nanoTime();
|
||||
System.out.println((diapEndTestTwo - diapStartTestTwo)/1000000 + " ms");
|
||||
|
||||
|
||||
System.out.println("result forkJoin: ");
|
||||
long diapStartTestTree = System.nanoTime();
|
||||
int[][] resultForkJoinPool = forkJoin.operationMatrixForkJoinPool();
|
||||
long diapEndTestTree = System.nanoTime();
|
||||
System.out.println((diapEndTestTree - diapStartTestTree)/1000000 + " ms");
|
||||
|
||||
|
||||
runTests(resultSingleThread, resultThreadPool, resultForkJoinPool);
|
||||
}
|
||||
|
||||
private static int[][] GenerateMatrix(int size){
|
||||
int[][] array = new int[size][size];
|
||||
Random rand = new Random();
|
||||
for (int i = 0; i < size; i++){
|
||||
for (int j = 0; j < size; j++){
|
||||
array[i][j] = rand.nextInt(-100, 100);
|
||||
//System.out.print(array[i][j] + " ");
|
||||
}
|
||||
//System.out.println();
|
||||
}
|
||||
//System.out.println();
|
||||
return array;
|
||||
}
|
||||
|
||||
private static void printArray(int[][] array){
|
||||
for(int i = 0; i < array.length; i++){
|
||||
for (int j = 0; j < array.length; j++){
|
||||
System.out.print(array[i][j] + " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
private static int[][] deepCopy(int[][] original) {
|
||||
return Arrays.stream(original).map(int[]::clone).toArray(int[][]::new);
|
||||
}
|
||||
|
||||
private static void writeToFile(String fileName, int[][] array) {
|
||||
try (FileWriter writer = new FileWriter(fileName)) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array[i].length; j++) {
|
||||
writer.write(array[i][j] + " ");
|
||||
}
|
||||
writer.write("\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Ошибка при записи в файл: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void runTests(int[][] arr1, int[][] arr2, int[][] arr3){
|
||||
writeToFile( PATH_FOR_TESTS + "single-test.txt", arr1);
|
||||
writeToFile(PATH_FOR_TESTS + "threadPool-test.txt", arr2);
|
||||
writeToFile(PATH_FOR_TESTS + "forkJoin-test.txt", arr3);
|
||||
}
|
||||
}
|
||||
90
tangatarov_ivan_lab_1/src/Readme.md
Normal file
90
tangatarov_ivan_lab_1/src/Readme.md
Normal file
@@ -0,0 +1,90 @@
|
||||
## Вариант 25. *[Упорядочить строки матрицы по убыванию наибольших элементов]*
|
||||
|
||||
### <span style="color: blue">Как работает программа?</span>
|
||||
Для корректного выполнения лабораторной работы, мне пришлось
|
||||
выбирать правильный способ сортировки матрицы,
|
||||
чтобы в дальнейшем внедрить реализацию с использованием ThreadPoolExecutor
|
||||
и ForkJoinPool. Сначала глаз пал на сортировку пузырьком (очень простая реализация),
|
||||
но в дальнейшем пришло осознание того,
|
||||
что каждый шаг алгоритма сильно зависит от предыдущего,
|
||||
поэтому будет очень сложно разбить задачу на несколько/подзадачи.
|
||||
По итогу я выбрал сортировку слиянием, которая отлично подходит.
|
||||
|
||||
### <span style="color: blue"> - *Однопоточный режим* </span>
|
||||
В классе Program происходит генерация матрицы. матрица передается в конструктор класса SingleThread Pool. Запуск алгоритма просходи в методе OperationMatrixSingleThread(). Работает только один поток, что является большим минусом при работе с большой матрицей.
|
||||
### <span style="color: blue"> - *Многопоточный режим [ThreadPoolExecutor]* </span>
|
||||
По условию лабораторной работы, все операции происходят с одной матрицей, поэтому клонируем исходную матрицу и также передаем ее в конструктор класса ExecutorThreadPool. Но конструктор также требует параметр, обозначающий максимальное задействованное количество потоков, которое будет находится в пулле. Передаем значение, равное оптимальному значению нашего железа. ThreadPoolExecutor использует пулл потоков, который поочередно берет задачи из очереди задач. *__но как же пополнять очередь задач задачами?__* для этого необходимо исследовать алгоритм и узнать, что стоит разбить на многопоточное выполнение. В моем случае я сделал разбиение массива многопоточно, то есть разбиение будет не в один поток, а в несколько. Выполнение алгоритма данным способом проигрывает однопоточной реализации при маленьком размере матрицы, но существенно опережает на больших размерах.
|
||||
### <span style="color: blue"> - *Многопоточный режим [ForkJoinPool]* </span>
|
||||
Также в конструктор передаем исходную матрицу. Выполнение отличается от предыдущего способа. Принцип работы ForkJoinPool в том, что мы задачу разбиваем на множество подзадач, которые выполняются параллельно в разных потоках и результаты выполнения сливаются в один. Основная цель для выполнения моей задачи: __разбить матрицу на части, отсортировать их по строкам и склеить обратно.__
|
||||
|
||||
<hr/>
|
||||
|
||||
## <span style="color: red">Запуск лабораторной работы </span>
|
||||
1. Переходи в папку с файлами программы
|
||||
2. Ищем запускаемый файл
|
||||
3. javac *.java __[компиляция файлов]__
|
||||
4. java filename.java __[запуск исполняемого файла]__
|
||||
|
||||
<hr/>
|
||||
|
||||
## <span style="color: red">Используемые технологии</span>
|
||||
1. ThreadPoolExecutor **<span style="color: green">[1]</span>** - используется для выполнения независимых задач в пуле потоков. Можно задать количество выделяемых потоков
|
||||
2. ForkJoinPool **<span style="color: green">[2]</span>** - используется для рекурсивных задач, которые можно разбить на подзадачи
|
||||
|
||||
<br/>**<span style="color: green">[1]</span>** Потоки после выполнения ждут новые задачи
|
||||
<br/>**<span style="color: green">[2]</span>** Потоки не стоят, а воруют задачи у других потоков, если у них нет работы
|
||||
|
||||
<br/>**<span style="color: green">[1]</span>** Создается большое количество потоков => больше переключений => больше трата времени
|
||||
<br/>**<span style="color: green">[2]</span>** Оптимально использует CPU, минимизируя переключение потоков.
|
||||
|
||||
<br/>**<span style="color: green">[1]</span>** Обрабатывает независимые задачи без разбиения
|
||||
<br/>**<span style="color: green">[2]</span>** Разбивает задачи на мелкие части и обрабатывает их параллельно
|
||||
|
||||
## Тестирование
|
||||
|
||||
### *Тест 1й. Размер матрицы 10 элементов:*
|
||||
result <span style="color: red">single</span>:
|
||||
34500 nano sek </br>
|
||||
result <span style="color: red">threadPool</span>:
|
||||
188600 nano sek</br>
|
||||
result <span style="color: red">forkJoin</span>:
|
||||
1005100 nano sek
|
||||
|
||||
|
||||
### *Тест 2й. Размер матрицы 100 элементов:*
|
||||
result <span style="color: red">single</span>:
|
||||
1052400 nano sek</br>
|
||||
result <span style="color: red">threadPool</span>:
|
||||
3056600 nano sek</br>
|
||||
result <span style="color: red">forkJoin</span>:
|
||||
2552200 nano sek
|
||||
|
||||
|
||||
### *Тест 3й. Размер матрицы 1000 элементов:*
|
||||
result <span style="color: red">single</span>:
|
||||
11658600 nano sek</br>
|
||||
result <span style="color: red">threadPool</span>:
|
||||
13080700 nano sek</br>
|
||||
result <span style="color: red">forkJoin</span>:
|
||||
15294800 nano sek
|
||||
|
||||
|
||||
### <span style="color: gray">Заметим, что при маленьком размере матрицы, однопоток работает в разы эффективней</span>
|
||||
|
||||
|
||||
### *Тест 4й. Размер матрицы 5000 элементов:*
|
||||
result <span style="color: red">single</span>:
|
||||
91840100 nano sek</br>
|
||||
result <span style="color: red">threadPool</span>:
|
||||
77622100 nano sek</br>
|
||||
result <span style="color: red">forkJoin</span>:
|
||||
60115500 nano sek
|
||||
|
||||
|
||||
### *Тест 5й. Размер матрицы 10000 элементов:*
|
||||
result <span style="color: red">single</span>:
|
||||
343642000 nano sek</br>
|
||||
result <span style="color: red">threadPool</span>:
|
||||
222304000 nano sek</br>
|
||||
result <span style="color: red">forkJoin</span>:
|
||||
144117500 nano sek
|
||||
59
tangatarov_ivan_lab_1/src/SingleThread.java
Normal file
59
tangatarov_ivan_lab_1/src/SingleThread.java
Normal file
@@ -0,0 +1,59 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SingleThread {
|
||||
|
||||
private int[][] array;
|
||||
|
||||
public SingleThread(int[][] array){
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public int[][] OperationMatrixSingleThread(){
|
||||
int[][] result = operationMatrix(array);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int[][] operationMatrix(int[][] array) {
|
||||
if (array.length <= 1) {
|
||||
return array;
|
||||
}
|
||||
|
||||
int mid = array.length / 2;
|
||||
int[][] left = Arrays.copyOfRange(array, 0, mid);
|
||||
int[][] right = Arrays.copyOfRange(array, mid, array.length);
|
||||
|
||||
left = operationMatrix(left);
|
||||
right = operationMatrix(right);
|
||||
|
||||
return merge(left, right);
|
||||
}
|
||||
|
||||
private int[][] merge(int[][] left, int[][] right) {
|
||||
int[][] result = new int[left.length + right.length][];
|
||||
int i = 0, j = 0, k = 0;
|
||||
|
||||
while (i < left.length && j < right.length) {
|
||||
if (findMaxElementRow(left[i]) >= findMaxElementRow(right[j])) {
|
||||
result[k++] = left[i++];
|
||||
} else {
|
||||
result[k++] = right[j++];
|
||||
}
|
||||
}
|
||||
|
||||
while (i < left.length) {
|
||||
result[k++] = left[i++];
|
||||
}
|
||||
while (j < right.length) {
|
||||
result[k++] = right[j++];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
private static int findMaxElementRow(int[] row){
|
||||
int maxCurrentValue = Integer.MIN_VALUE;
|
||||
for(int i = 0; i < row.length; i++){
|
||||
maxCurrentValue = (row[i] >= maxCurrentValue ? row[i] : maxCurrentValue);
|
||||
}
|
||||
return maxCurrentValue;
|
||||
}
|
||||
}
|
||||
1000
tangatarov_ivan_lab_1/src/tests/forkJoin-test.txt
Normal file
1000
tangatarov_ivan_lab_1/src/tests/forkJoin-test.txt
Normal file
File diff suppressed because it is too large
Load Diff
1000
tangatarov_ivan_lab_1/src/tests/single-test.txt
Normal file
1000
tangatarov_ivan_lab_1/src/tests/single-test.txt
Normal file
File diff suppressed because it is too large
Load Diff
1000
tangatarov_ivan_lab_1/src/tests/threadPool-test.txt
Normal file
1000
tangatarov_ivan_lab_1/src/tests/threadPool-test.txt
Normal file
File diff suppressed because it is too large
Load Diff
74
valiulov_ilyas_lab_2/Main.java
Normal file
74
valiulov_ilyas_lab_2/Main.java
Normal file
@@ -0,0 +1,74 @@
|
||||
import mpi.MPI;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
MPI.Init(args);
|
||||
int rank = MPI.COMM_WORLD.Rank();
|
||||
int size = MPI.COMM_WORLD.Size();
|
||||
int[][] matrix = null;
|
||||
int rows = 1000;
|
||||
int cols = 1000;
|
||||
long starttime = 0;
|
||||
long endtime = 0;
|
||||
|
||||
if (rank == 0) {
|
||||
matrix = MatrixGenerator.generateMatrix(rows, cols);
|
||||
starttime = System.nanoTime();
|
||||
}
|
||||
|
||||
int halfRows = rows / 2;
|
||||
int[] localMatrixFlat = new int[halfRows * cols];
|
||||
|
||||
if (rank == 0) {
|
||||
int index = 0;
|
||||
for (int i = halfRows; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
localMatrixFlat[index++] = matrix[i][j];
|
||||
}
|
||||
}
|
||||
MPI.COMM_WORLD.Send(localMatrixFlat, 0, localMatrixFlat.length, MPI.INT, 1, 0);
|
||||
index = 0;
|
||||
for (int i = 0; i < halfRows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
localMatrixFlat[index++] = matrix[i][j];
|
||||
}
|
||||
}
|
||||
} else if (rank == 1) {
|
||||
MPI.COMM_WORLD.Recv(localMatrixFlat, 0, localMatrixFlat.length, MPI.INT, 0, 0);
|
||||
}
|
||||
|
||||
int[][] localMatrix = new int[halfRows][cols];
|
||||
int index = 0;
|
||||
for (int i = 0; i < halfRows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
localMatrix[i][j] = localMatrixFlat[index++];
|
||||
}
|
||||
}
|
||||
|
||||
int localMax = findMaxInMatrix(localMatrix, rank);
|
||||
System.out.println("Process " + rank + " local max: " + localMax);
|
||||
|
||||
int[] globalMax = new int[1];
|
||||
MPI.COMM_WORLD.Reduce(new int[]{localMax}, 0, globalMax, 0, 1, MPI.INT, MPI.MAX, 0);
|
||||
if (rank == 0) {
|
||||
System.out.println("Global max element: " + globalMax[0]);
|
||||
endtime = System.nanoTime();
|
||||
long duration = (endtime - starttime);
|
||||
System.out.println("Time: " + duration);
|
||||
}
|
||||
|
||||
MPI.Finalize();
|
||||
}
|
||||
private static int findMaxInMatrix(int[][] matrix, int rank) {
|
||||
int max = Integer.MIN_VALUE;
|
||||
for (int[] row : matrix) {
|
||||
for (int value : row) {
|
||||
if (value > max) {
|
||||
max = value;
|
||||
System.out.println("New max "+max+" in container "+rank);
|
||||
}
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
}
|
||||
14
valiulov_ilyas_lab_2/MatrixGenerator.java
Normal file
14
valiulov_ilyas_lab_2/MatrixGenerator.java
Normal file
@@ -0,0 +1,14 @@
|
||||
import java.util.Random;
|
||||
|
||||
public class MatrixGenerator {
|
||||
public static int[][] generateMatrix(int rows, int cols) {
|
||||
int[][] matrix = new int[rows][cols];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[i][j] = random.nextInt(100000);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
66
valiulov_ilyas_lab_2/Readme.md
Normal file
66
valiulov_ilyas_lab_2/Readme.md
Normal file
@@ -0,0 +1,66 @@
|
||||
## Лабораторная работа №1.
|
||||
|
||||
Разработка параллельного MPI приложения на языке Java.
|
||||
|
||||
Вариант 5: Нужно определить максимальный элемент матрицы
|
||||
|
||||
**Необходимо:**
|
||||
1. Разработать параллельный вариант алгоритма с применением MPI, где должны быть две копии приложения,
|
||||
которые соединяются между собой по сети.
|
||||
2. Замерить время работы алгоритма
|
||||
|
||||
**Используемые технологии**
|
||||
1. MPJ Express - библиотека, реализующая MPI для Java.
|
||||
|
||||
**Запуск лабораторной работы**
|
||||
1. `mpjdaemon -boot 192.168.5.107` - включение демона mpj-express на первом контейнере
|
||||
2. `mpjdaemon -boot 192.168.5.106` - включение демона mpj-express на втором контейнере
|
||||
3. `mpjrun.sh -np 2 -dev niodev Main` - запуск приложения
|
||||
|
||||
**Что делает программа**
|
||||
1. Генерирация матрицы
|
||||
2. Матрица разделяется на две половины. Половина матрицы отправляется во второй контейнер
|
||||
3. Обработка матрицы и поиск локального максимального элемента в каждой половине
|
||||
4. Определение глобального максимума
|
||||
|
||||
**Тесты**
|
||||
```
|
||||
mpjrun.sh -np 2 -dev niodev Main
|
||||
MPJ Express (0.44) is started in the cluster configuration with niodev
|
||||
Starting process <0> on <CT107>
|
||||
Starting process <1> on <secondcontainer>
|
||||
New max 36703 in container 0
|
||||
New max 40049 in container 0
|
||||
New max 93370 in container 1
|
||||
New max 95505 in container 0
|
||||
New max 97047 in container 0
|
||||
New max 99536 in container 0
|
||||
New max 99823 in container 0
|
||||
New max 99875 in container 0
|
||||
New max 99896 in container 0
|
||||
New max 99901 in container 0
|
||||
New max 99988 in container 0
|
||||
New max 94820 in container 1
|
||||
New max 98237 in container 1
|
||||
New max 99825 in container 1
|
||||
New max 99929 in container 1
|
||||
New max 99965 in container 1
|
||||
New max 99966 in container 1
|
||||
New max 99975 in container 1
|
||||
New max 99981 in container 1
|
||||
New max 99996 in container 1
|
||||
New max 99998 in container 1
|
||||
New max 99999 in container 1
|
||||
New max 99996 in container 0
|
||||
New max 99998 in container 0
|
||||
New max 99999 in container 0
|
||||
Process 1 local max: 99999
|
||||
Process 0 local max: 99999
|
||||
Global max element: 99999
|
||||
Time: 485045144
|
||||
Stopping Process <0> on <CT107>
|
||||
Stopping Process <1> on <secondcontainer>
|
||||
```
|
||||
|
||||
**Вывод**
|
||||
С помощью MPI можно реализировать паралельный алгоритм некоторой программы. Если сравнивать время поиска максимального элемента с алгоритмами, реализованными в прошлой лабораторной работе(ForkJoinPool и ThreadPoolExecutor), то на одних и тех же значения алгоритм с MPI работает быстрее.
|
||||
2
valiulov_ilyas_lab_2/machines
Normal file
2
valiulov_ilyas_lab_2/machines
Normal file
@@ -0,0 +1,2 @@
|
||||
192.168.5.107
|
||||
192.168.5.106
|
||||
93
zubkova_sofya_lab_1/readme.md
Normal file
93
zubkova_sofya_lab_1/readme.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Лабораторная работа №1
|
||||
|
||||
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
|
||||
|
||||
**Необходимо:**
|
||||
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2. Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
|
||||
3. Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
|
||||
Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
|
||||
## Вариант 12.
|
||||
Определить среднее арифметическое элементов матрицы ниже главной
|
||||
диагонали.
|
||||
|
||||
### Запуск лаборатороной работы:
|
||||
1. Компиляция программы осуществляется с помощью команды:
|
||||
> javac Main.java
|
||||
|
||||
2. Запуск скомпилированного класса:
|
||||
> java Main
|
||||
|
||||
### Использованные технологии:
|
||||
- Пакет `java.util.concurrent` предоставляет мощные инструменты для работы с многопоточностью и параллельными вычислениями
|
||||
- `ExecutorService` и `Executors` используются для управления пулами потоков и выполнения задач
|
||||
- `ThreadPoolExecutor` - в этом подходе используется пул потоков для параллельного вычисления суммы элементов матрицы. Матрица разбивается на части, и каждая часть обрабатывается отдельным потоком.
|
||||
- `ForkJoinPool` — специализированный пул потоков, предназначенный для выполнения задач, которые могут быть рекурсивно разбиты на более мелкие подзадачи (fork), а затем объединены (join).
|
||||
### Как работает программа:
|
||||
1. Происходит генерация массива размером = size в классе `Main.java`
|
||||
```
|
||||
public class Main {
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException {
|
||||
int size = 1000; // size matrix
|
||||
int[][] matrix = generateMatrix(size);
|
||||
```
|
||||
Метод для генерации массива:
|
||||
```
|
||||
private static int[][] generateMatrix(int size) {
|
||||
int[][] matrix = new int[size][size];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = random.nextInt(1000);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
```
|
||||
2. Создание трех копий исходного массива.
|
||||
3. Происходит обработка массива с помощью однопоточного варианта алгоритма ( класс SingleThreadedAverage).
|
||||
4. Класс ThreadPoolAverage обрабатывает матрицу с использованием пула потоков создается пул потоков (задается количество потоков = 4), а каждая строка матрицы обрабатывается в отдельном потоке.
|
||||
5. Класс ForkJoinAverage обрабатывает матрицу с использованием ForkJoinPool и рекурсивного разделения задачи: если блок большой, он разделяется на две части, и каждая часть обрабатывается рекурсивно.
|
||||
6. Происходит замер времени работы всех алгоритмов в классе `Main.java`:
|
||||
```
|
||||
// SingleThreaded algorithm
|
||||
long startTime = System.nanoTime();
|
||||
double singleThreadedResult = SingleThreadedAverage.calculate(matrix);
|
||||
long endTime = System.nanoTime();
|
||||
System.out.println("Result: " + singleThreadedResult);
|
||||
System.out.println("Single-threaded time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
// ThreadPoolExecutor algorithm
|
||||
startTime = System.nanoTime();
|
||||
double threadPoolResult = ThreadPoolAverage.calculate(matrix, 4);
|
||||
endTime = System.nanoTime();
|
||||
System.out.println("ThreadPoolExecutor time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
// ForkJoinPool algorithm
|
||||
startTime = System.nanoTime();
|
||||
double forkJoinResult = ForkJoinAverage.calculate(matrix);
|
||||
endTime = System.nanoTime();
|
||||
System.out.println("ForkJoinPool time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
```
|
||||
### Тесты:
|
||||
- при size = 1000:
|
||||
Result: 498.8887907907908
|
||||
Single-threaded time: 4 ms
|
||||
ThreadPoolExecutor time: 104 ms
|
||||
ForkJoinPool time: 42 ms
|
||||
|
||||
Result: 499.8382882882883
|
||||
Single-threaded time: 1 ms
|
||||
ThreadPoolExecutor time: 35 ms
|
||||
ForkJoinPool time: 15 ms
|
||||
|
||||
- при size = 10000:
|
||||
Result: 499.5273448344835
|
||||
Single-threaded time: 26 ms
|
||||
ThreadPoolExecutor time: 42 ms
|
||||
ForkJoinPool time: 18 ms
|
||||
|
||||
## Вывод:
|
||||
При небольших размерах матрицы лучше использовать однопоточный вариант алгоритма, так как он дает лучший показатель времени. Однако, если размер генерируемой матрицы большой, то нужно использовать ForkJoinPool. А ThreadPoolExecutor - самый медленный.
|
||||
43
zubkova_sofya_lab_1/src/ForkJoinAverage.java
Normal file
43
zubkova_sofya_lab_1/src/ForkJoinAverage.java
Normal file
@@ -0,0 +1,43 @@
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
public class ForkJoinAverage {
|
||||
private static class AverageTask extends RecursiveTask<Long> {
|
||||
private final int[][] matrix;
|
||||
private final int startRow;
|
||||
private final int endRow;
|
||||
|
||||
AverageTask(int[][] matrix, int startRow, int endRow) {
|
||||
this.matrix = matrix;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (endRow - startRow <= 10) {
|
||||
long sum = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
sum += matrix[i][j];
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
} else {
|
||||
int mid = (startRow + endRow) / 2;
|
||||
AverageTask left = new AverageTask(matrix, startRow, mid);
|
||||
AverageTask right = new AverageTask(matrix, mid, endRow);
|
||||
left.fork();
|
||||
long rightResult = right.compute();
|
||||
long leftResult = left.join();
|
||||
return leftResult + rightResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static double calculate(int[][] matrix) {
|
||||
ForkJoinPool pool = new ForkJoinPool();
|
||||
long totalSum = pool.invoke(new AverageTask(matrix, 0, matrix.length));
|
||||
int totalCount = matrix.length * (matrix.length - 1) / 2;
|
||||
return (double) totalSum / totalCount;
|
||||
}
|
||||
}
|
||||
41
zubkova_sofya_lab_1/src/Main.java
Normal file
41
zubkova_sofya_lab_1/src/Main.java
Normal file
@@ -0,0 +1,41 @@
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.Random;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException {
|
||||
int size = 10000; // size matrix
|
||||
int[][] matrix = generateMatrix(size);
|
||||
|
||||
// SingleThreaded algorithm
|
||||
long startTime = System.nanoTime();
|
||||
double singleThreadedResult = SingleThreadedAverage.calculate(matrix);
|
||||
long endTime = System.nanoTime();
|
||||
System.out.println("Single-threaded result: " + singleThreadedResult);
|
||||
System.out.println("Single-threaded time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
// ThreadPoolExecutor algorithm
|
||||
startTime = System.nanoTime();
|
||||
double threadPoolResult = ThreadPoolAverage.calculate(matrix, 4);
|
||||
endTime = System.nanoTime();
|
||||
//System.out.println("ThreadPool result: " + threadPoolResult);
|
||||
System.out.println("ThreadPoolExecutor time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
// ForkJoinPool algorithm
|
||||
startTime = System.nanoTime();
|
||||
double forkJoinResult = ForkJoinAverage.calculate(matrix);
|
||||
endTime = System.nanoTime();
|
||||
//System.out.println("ForkJoin result: " + forkJoinResult);
|
||||
System.out.println("ForkJoinPool time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
|
||||
}
|
||||
private static int[][] generateMatrix(int size) {
|
||||
int[][] matrix = new int[size][size];
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = random.nextInt(1000);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
15
zubkova_sofya_lab_1/src/SingleThreadedAverage.java
Normal file
15
zubkova_sofya_lab_1/src/SingleThreadedAverage.java
Normal file
@@ -0,0 +1,15 @@
|
||||
public class SingleThreadedAverage {
|
||||
public static double calculate(int[][] matrix) {
|
||||
long sum = 0;
|
||||
int count = 0;
|
||||
int size = matrix.length;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
sum += matrix[i][j];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (double) sum / count;
|
||||
}
|
||||
38
zubkova_sofya_lab_1/src/ThreadPoolAverage.java
Normal file
38
zubkova_sofya_lab_1/src/ThreadPoolAverage.java
Normal file
@@ -0,0 +1,38 @@
|
||||
import java.util.concurrent.*;
|
||||
public class ThreadPoolAverage {
|
||||
public static double calculate(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
int size = matrix.length;
|
||||
int chunkSize = size / numThreads;
|
||||
Future<Long>[] futures = new Future[numThreads];
|
||||
int[] counts = new int[numThreads];
|
||||
|
||||
for (int t = 0; t < numThreads; t++) {
|
||||
final int startRow = t * chunkSize;
|
||||
final int endRow = (t == numThreads - 1) ? size : startRow + chunkSize;
|
||||
final int threadIndex = t;
|
||||
futures[t] = executor.submit(() -> {
|
||||
long localSum = 0;
|
||||
int localCount = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
localSum += matrix[i][j];
|
||||
localCount++;
|
||||
}
|
||||
}
|
||||
counts[threadIndex] = localCount;
|
||||
return localSum;
|
||||
});
|
||||
}
|
||||
|
||||
long totalSum = 0;
|
||||
int totalCount = 0;
|
||||
for (int t = 0; t < numThreads; t++) {
|
||||
totalSum += futures[t].get();
|
||||
totalCount += counts[t];
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
return (double) totalSum / totalCount;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user