Merge branch 'main' into shurygin_dima_lab_2

This commit is contained in:
2025-03-24 22:24:44 +04:00
117 changed files with 11480 additions and 0 deletions

2
.gitignore vendored
View File

@@ -29,3 +29,5 @@ replay_pid*
/.idea/.name
.idea
/.vs/repos
/.vs

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,63 @@
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.ForkJoinPool;
public class ForkJoinMatrixDivider {
public static void divideMatrix(int[][] matrix) {
long startTime = System.currentTimeMillis();
int max = findMax(matrix);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new DivideTask(matrix, 0, matrix.length, max));
pool.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("ForkJoinPool: " + (endTime - startTime) + "ms");
}
private static class DivideTask extends RecursiveAction {
private final int[][] matrix;
private final int start;
private final int end;
private final int max;
DivideTask(int[][] matrix, int start, int end, int max) {
this.matrix = matrix;
this.start = start;
this.end = end;
this.max = max;
}
@Override
protected void compute() {
if (end - start <= 10) { // porog
for (int i = start; i < end; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= max;
}
}
} else {
int mid = (start + end) / 2;
DivideTask left = new DivideTask(matrix, start, mid, max);
DivideTask right = new DivideTask(matrix, mid, end, max);
invokeAll(left, right);
}
}
}
private static int findMax(int[][] matrix) {
int max = matrix[0][0];
for (int[] row : matrix) {
for (int value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
public static void main(String[] args) {
int[][] matrix = MatrixGenerator.generateMatrix(1000, 1000);
divideMatrix(matrix);
}
}

View 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] = i+j;
}
}
return matrix;
}
}

View File

@@ -0,0 +1,28 @@
public class SingleThreadedMatrixDivider {
public static void main(String[] args) {
int[][] matrix = MatrixGenerator.generateMatrix(1000, 1000);
long startTime = System.currentTimeMillis();
int max = findMax(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= max;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Single-flow: " + (endTime - startTime) + "ms");
}
private static int findMax(int[][] matrix) {
int max = matrix[0][0];
for (int[] row : matrix) {
for (int value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
}

View File

@@ -0,0 +1,48 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolMatrixDivider {
public static void divideMatrix(int[][] matrix, int numThreads) {
long startTime = System.currentTimeMillis();
int max = findMax(matrix);
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < matrix.length; i++) {
final int row = i;
executor.submit(() -> {
for (int j = 0; j < matrix[row].length; j++) {
matrix[row][j] /= max;
}
});
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("ThreadPoolExecutor: " + (endTime - startTime) + "ms");
}
private static int findMax(int[][] matrix) {
int max = matrix[0][0];
for (int[] row : matrix) {
for (int value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
public static void main(String[] args) {
int[][] matrix = MatrixGenerator.generateMatrix(1000, 1000);
divideMatrix(matrix, 4);
}
}

View File

@@ -0,0 +1,93 @@
# Лабораторная работа №1
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
### Необходимо:
Разработать однопоточный вариант алгоритма и замерить время его работы.
Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы.
Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
!!!. Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
Вариант задания 1: Разделить элементы матрицы на наибольший элемент.
### Как запустить лабораторную работу:
javac ForkJoinMatrixDivider.java компилирует исходный код java ForkJoinMatrixDivider команда запускает скомпилированный класс
javac SingleThreadedMatrixDivider.java компилирует исходный код java SingleThreadedMatrixDivider команда запускает скомпилированный класс
javac ThreadPoolMatrixDivider.java компилирует исходный код java ThreadPoolMatrixDivider команда запускает скомпилированный класс
### Какие технологии использовали:
Java 17+
ExecutorService (ThreadPoolExecutor)
ForkJoinPool
System.currentTimeMillis() для замеров времени выполнения
### Как работает программа:
Генерация матрицы:
Программа создаёт двумерный массив заданного размера.
Заполняет его случайными значениями.
Определяет наибольший элемент матрицы.
Однопоточный алгоритм:
Проходит по всей матрице в одном потоке.
Делит каждый элемент матрицы на наибольший элемент.
Записывает результат в новую матрицу.
Измеряет время выполнения.
Параллельный алгоритм с ThreadPoolExecutor:
Создаёт пул потоков с фиксированным количеством потоков (обычно равное количеству ядер CPU).
Делит матрицу на строки, назначая обработку каждой строки отдельному потоку.
Потоки выполняют деление элементов и записывают результаты.
Ожидает завершения всех потоков.
Измеряет время выполнения.
Параллельный алгоритм с ForkJoinPool:
Использует принцип рекурсивного разбиения задач (Fork/Join).
Разбивает матрицу на более мелкие подзадачи, пока они не станут достаточно малы для обработки в одном потоке.
Выполняет деление элементов параллельно.
Объединяет результаты.
Измеряет время выполнения.
Вывод результатов:
Для каждого метода измеряется время выполнения.
Результаты выводятся в консоль.
(Размер матрицы 1000х1000)
Single thread alghoritm: 51 ms
ThreadPoolExecutor: 150 ms
ForkJoinPool: 90 ms
(Размер матрицы 2000х2000)
Single thread alghoritm: 242 ms
ThreadPoolExecutor: 300 ms
ForkJoinPool: 139 ms.
(Размер матрицы 3000x3000)
Single thread alghoritm: 452 ms
ThreadPoolExecutor: 495 ms
ForkJoinPool: 411 ms
### Вывод
Использование ForkJoinPool демонстрирует наивысшую эффективность среди многопоточных подходов в этом алгоритме. ThreadPoolExecutor оказывается самым невыгодным из-за значительных накладных расходов, связанных с управлением потоками. Однопоточный метод работает достаточно хорошо на небольших объемах данных, однако с ростом вычислительной нагрузки он существенно уступает ForkJoinPool.

View File

@@ -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();
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}

View 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 — самый медленный в данном случае.

View 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());
}
}
}

View 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** часто дает еще более высокое ускорение за счет механизма рекурсивного разбиения задач, но реальная производительность зависит от конфигурации процессоров и параметров разбиения.

View 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;
}
}

View 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`. Подход хорошо масштабируется и демонстрирует, как распределённые процессы могут совместно решать одну задачу.

View 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

View 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));
}
}

View 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");
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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.

View 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 все равно остается эффективным.

View 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();
}
}

View 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;
}
}

View 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();
}
}

View File

@@ -0,0 +1,2 @@
192.168.28.250
192.168.28.129

View 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

View File

@@ -0,0 +1,95 @@
import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.*;
public class MatrixNormalization {
private static int SIZE;
private static final int THREADS = Runtime.getRuntime().availableProcessors();
private static double[][] matrix;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter the size of the matrix: ");
SIZE = scanner.nextInt();
matrix = new double[SIZE][SIZE];
generateMatrix();
System.out.println("Matrix generated.......");
long startTime = System.nanoTime();
normalizeSingleThread();
long endTime = System.nanoTime();
System.out.println("Single-threaded time: " + (endTime - startTime) / 1_000_000 + " ms");
generateMatrix();
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
startTime = System.nanoTime();
normalizeThreadPool(executor);
endTime = System.nanoTime();
System.out.println("ThreadPoolExecutor time: " + (endTime - startTime) / 1_000_000 + " ms");
executor.shutdown();
generateMatrix();
ForkJoinPool forkJoinPool = new ForkJoinPool(THREADS);
startTime = System.nanoTime();
forkJoinPool.invoke(new ForkJoinNormalize(0, SIZE));
endTime = System.nanoTime();
System.out.println("ForkJoinPool time: " + (endTime - startTime) / 1_000_000 + " ms");
}
private static void generateMatrix() {
Random rand = new Random();
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
matrix[i][j] = rand.nextDouble() * 100;
}
private static double findMax() {
double max = Double.MIN_VALUE;
for (double[] row : matrix)
for (double val : row)
max = Math.max(max, val);
return max;
}
private static void normalizeSingleThread() {
double max = findMax();
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
matrix[i][j] /= max;
}
private static void normalizeThreadPool(ExecutorService executor) {
double max = findMax();
for (int i = 0; i < SIZE; i++) {
final int row = i;
executor.submit(() -> {
for (int j = 0; j < SIZE; j++)
matrix[row][j] /= max;
});
}
}
static class ForkJoinNormalize extends RecursiveAction {
private static final int THRESHOLD = 100;
private final int start, end;
ForkJoinNormalize(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected void compute() {
double max = findMax();
if (end - start <= THRESHOLD) {
for (int i = start; i < end; i++)
for (int j = 0; j < SIZE; j++)
matrix[i][j] /= max;
} else {
int mid = (start + end) / 2;
invokeAll(new ForkJoinNormalize(start, mid), new ForkJoinNormalize(mid, end));
}
}
}
}

View File

@@ -0,0 +1,119 @@
# **Лабораторная работа №1**
## **Тема:** Разработка многопоточного приложения с использованием Java Concurrency
### **Цель работы:**
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
Необходимо:
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
2. Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
3. Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
ВАЖНО: Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
---
### **Вариант:**
1 Разделить элементы матрицы на наибольший элемент.
---
### **Шаги для запуска:**
1. Перейти в директорию с исходными файлами.
2. Скомпилировать Java-код:
```sh
javac MatrixNormalization.java
```
3. Запустить программу:
```sh
java MatrixNormalization
```
---
## **Используемые технологии:**
- `java.util.concurrent` — инструменты для работы с многопоточностью (пулы потоков, синхронизация).
- `ForkJoinPool` — пул потоков для выполнения параллельных задач.
- `ExecutorService` управление пулом потоков.
- `RecursiveAction` — абстрактный класс из Fork/Join Framework, используемый для выполнения параллельных задач без возвращаемого результата.
---
## **Код:**
1. Однопоточный алгоритм:
```java
private static void normalizeSingleThread() {
double max = findMax();
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
matrix[i][j] /= max;
}
```
2. ThreadPoolExecutor
```java
private static void normalizeThreadPool(ExecutorService executor) {
double max = findMax();
for (int i = 0; i < SIZE; i++) {
final int row = i;
executor.submit(() -> {
for (int j = 0; j < SIZE; j++)
matrix[row][j] /= max;
});
}
}
```
3. ForkJoinPoll
```java
static class ForkJoinNormalize extends RecursiveAction {
private static final int THRESHOLD = 100;
private final int start, end;
ForkJoinNormalize(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected void compute() {
double max = findMax();
if (end - start <= THRESHOLD) {
for (int i = start; i < end; i++)
for (int j = 0; j < SIZE; j++)
matrix[i][j] /= max;
} else {
int mid = (start + end) / 2;
invokeAll(new ForkJoinNormalize(start, mid), new ForkJoinNormalize(mid, end));
}
}
}
```
---
## **Описание работы программы:**
Программа создаёт квадратную матрицу **cols × rows** и выполняет нормализацию матрицы с использованием разных подходов к многопоточной обработке. Нормализация заключается в делении всех элементов матрицы на ее максимальный элемент.
---
## **Результаты работы:**
1. Работа алгоритмов на матрице 100 на 100:
```
Время выполнения однопоточного алгоритма: 23 мс
Время выполнения с потоками ThreadPoolExecutor: 22 мс
Время выполнения с потоками ForkJoinPool: 16 мс
```
2. Работа алгоритмов на матрице 500 на 500:
```
Время выполнения однопоточного алгоритма: 76 мс
Время выполнения с потоками ThreadPoolExecutor: 48 мс
Время выполнения с потоками ForkJoinPool: 37 мс
```
3. Работа алгоритмов на матрице 1000 на 1000:
```
Время выполнения однопоточного алгоритма: 118 мс
Время выполнения с потоками ThreadPoolExecutor: 82 мс
Время выполнения с потоками ForkJoinPool: 70 мс
```
---
## **Вывод:**
- При небольших матрицах многопоточные алгоритмы не дают значительных преимуществ из-за накладных расходов.
- При увеличении размера матрицы многопоточные подходы заметно ускоряют вычисления.
- ForkJoinPool эффективнее ThreadPoolExecutor за счет более гибкого механизма балансировки нагрузки.

View 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));
}
}
}
}

View 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, показывают более плохие результаты из-за дополнительных
затрат на создание потоков и управление ими.
Но когда размеры матриц увеличиваются, многопоточные алгоритмы начинают показывать гораздо лучшую производительность
по сравнению с однопоточным. Однопоточный алгоритм начинает замедляться из-за большого объема данных, тогда как
многопоточные алгоритмы, начинают работать быстрее. Это происходит потому, что затраты на управление потоками
оправдываются их параллельной работой, что позволяет значительно ускорить обработку данных.
Вообщем, многопоточность действительно может значительно повысить производительность при обработке больших объемов
данных.

View 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 показывает себя не очень эффективно,
становясь быстрее однопоточного алгоритма только на очень больших размерах матрицы. Происходит это
если задача занимает мало времени, и накладные расходы на создание задачи, переключение контекста между
потоками и синхронизацию могут превысить выигрыш от распараллеливания, делая однопоточное решение более эффективным.

View 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
View 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

View File

@@ -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);
}
}
}
}

View 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
View 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

View File

@@ -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();
}
}

View 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`.

View 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

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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
View 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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View 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
View 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

View 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>.

View File

@@ -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) + " мс");
}
}
}

View File

@@ -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() {}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
artifactId=Lab
groupId=ru.kuznec
version=4

View File

@@ -0,0 +1,3 @@
ru\kuznec\MatrixMinJob.class
ru\kuznec\MatrixMinTask.class
ru\kuznec\MatrixMinAlgorithm.class

View File

@@ -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

View 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 до i1 (то есть элементы, находящиеся ниже главной диагонали). Вычисляется локальный минимум, который затем возвращается. Метод **cancel()** оставлен пустым, так как отмена не требуется по заданию.
---
## Запуск
Запустить jar можно командой (обязательно Java 8):
```bash
java -jar Lab-4-jar-with-dependencies.jar
```
При запуске Ignite запускает узел, распределяет задачу по 4 частям матрицы, и в конце выводится минимальный элемент и общее время выполнения.

View 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) + " миллисекунд");
}
}
}

View 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. Время выполнения каждого этапа выводится в консоль, что позволяет оценить эффективность работы программы. Контейнеры успешно взаимодействуют между собой, обмениваются данными и выдают результат.

179
pyzhov_egor_lab1/readme.md Normal file
View File

@@ -0,0 +1,179 @@
# 1 Лабораторная работа
## Задание
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
**Необходимо:**
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
2. Разработать параллельный вариант алгоритма с использованием `ThreadPoolExecutor` и замерить время его работы.
3. Разработать параллельный вариант алгоритма с использованием `ForkJoinPool` и замерить время его работы.
**Вариант:** "Определить минимальный элемент матрицы ниже главной диагонали"
---
## Описание
Это программа для сортировки матрицы по убыванию значений в первом столбце. Программа поддерживает три режима сортировки:
1. Однопоточная сортировка.
2. Многопоточная сортировка с использованием ThreadPoolExecutor.
3. Многопоточная сортировка с использованием ForkJoinPool.
Программа генерирует случайную матрицу и сохраняет её в файл, а после сортировки сохраняет отсортированную матрицу в другой файл.
## Подготовка
## Однопоточная сортировка
```java
private static void singleThreadSort(int[][] matrix) {
Arrays.sort(matrix, Comparator.comparingInt(a -> -a[0]));
}
```
## Параллельная сортировка с использованием ThreadPoolExecutor
```java
private static void multiThreadSort(int[][] matrix) throws InterruptedException {
int processors = Runtime.getRuntime().availableProcessors();
int chunkSize = matrix.length / processors;
ExecutorService executor = Executors.newFixedThreadPool(processors);
Future<?>[] futures = new Future[processors];
for (int i = 0; i < processors; i++) {
int start = i * chunkSize;
int end = (i == processors - 1) ? matrix.length : start + chunkSize;
futures[i] = executor.submit(() -> {
Arrays.sort(matrix, start, end, Comparator.comparingInt(a -> -a[0]));
});
}
for (Future<?> future : futures) {
try {
future.get();
} catch (ExecutionException e) {
throw new RuntimeException("Sorting error", e);
}
}
executor.shutdown();
}
```
- Определение количества потоков: Количество потоков равно количеству доступных процессоров `(Runtime.getRuntime().availableProcessors())`.
- Разделение матрицы на части: Матрица делится на processors частей, где каждая часть содержит примерно одинаковое количество строк.
- Создание и запуск задач: Для каждой части создается задача, которая сортирует эту часть с помощью `Arrays.sort`.
- Задачи выполняются параллельно в пуле потоков `(ExecutorService)`.
- Ожидание завершения всех задач: Основной поток ждет завершения всех задач с помощью `future.get()`.
- Завершение работы пула потоков: После завершения всех задач пул потоков закрывается `(executor.shutdown())`.
---
## Параллельная сортировка с использованием ForkJoinPool
```java
private static void forkJoinSort(int[][] matrix) {
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new ForkJoinTask(matrix));
pool.shutdown();
}
static class ForkJoinTask extends RecursiveAction {
private static final int THRESHOLD = 500;
private final int[][] matrix;
ForkJoinTask(int[][] matrix) {
this.matrix = matrix;
}
@Override
protected void compute() {
if (matrix.length <= THRESHOLD) {
Arrays.sort(matrix, Comparator.comparingInt(a -> -a[0]));
} else {
int mid = matrix.length / 2;
int[][] left = Arrays.copyOfRange(matrix, 0, mid);
int[][] right = Arrays.copyOfRange(matrix, mid, matrix.length);
ForkJoinTask leftTask = new ForkJoinTask(left);
ForkJoinTask rightTask = new ForkJoinTask(right);
invokeAll(leftTask, rightTask);
System.arraycopy(left, 0, matrix, 0, left.length);
System.arraycopy(right, 0, matrix, left.length, right.length);
}
}
}
```
- Создание пула потоков: Создается пул потоков `ForkJoinPool`, который автоматически управляет потоками и задачами.
- Запуск задачи: В пул передается задача `ForkJoinTask`, которая начинает выполнение.
- Рекурсивное разделение задачи: Если размер матрицы больше порога `(THRESHOLD)`, матрица разделяется на левую и праву часть, каждая подзадача выполняется рекурсивно.
- Базовый случай: Если размер матрицы меньше или равен порогу `(THRESHOLD)`, выполняется сортировка с помощью `Arrays.sort`.
- Объединение результатов: После завершения сортировки левой и правой частей результаты объединяются в исходную матрицу с помощью `System.arraycopy`.
- Завершение работы пула потоков:
- После завершения всех задач пул потоков закрывается `(pool.shutdown())`.
---
## Как запустить
```sh
javac Matrix.java
java Matrix
```
## Используемые технологии
Программа использует **Java Concurrency**, включая классы и интерфейсы:
- `ExecutorService`, `ThreadPoolExecutor`, `ForkJoinPool`, `Future`, `CompletableFuture`, `RecursiveAction` — для организации многопоточности.
## Результаты работы алгоритма
#### Пример 1
##### Входные данные
```
65 649 455 884 387
174 805 983 575 957
597 789 601 674 916
689 703 885 353 412
```
##### Входные данные
```
689 703 885 353 412
597 789 601 674 916
174 805 983 575 957
65 649 455 884 387
```
## Анализ замеров программы на разных объёмах данных
### Матрица 100x100
```
Single-threaded algorithm: 0.106388 ms
Multi-threaded algorithm (ThreadPoolExecutor): 32.085907 ms
Multi-threaded algorithm (ForkJoinPool): 17.146648 ms
```
### Матрица 10000x1000
```
Single-threaded algorithm: 92.179132 ms
Multi-threaded algorithm (ThreadPoolExecutor): 54.21498 ms
Multi-threaded algorithm (ForkJoinPool): 45.481057 ms
```
### Матрица 50000x1000
```
Single-threaded algorithm: 379.713324 ms
Multi-threaded algorithm (ThreadPoolExecutor): 130.736923 ms
Multi-threaded algorithm (ForkJoinPool): 71.736064 ms
```
## Вывод
- Можно сказать, что на маленьких объемах многопоточность негативно сказывается на эффективности, на размере 10000x100 уже многопоточность оптимизирует задачу, ThreadPool и ForkJoin примерно на одинаковом уровне. На размере 50000x1000 ForkJoin лидирует над ThreadPool. Можно сделать вывод, что ForkJoin в целом работает лучше, чем ThreadPool, но наверное не всегда можно сделать рекурсивное разделение, как в нашем варианте.

View File

@@ -0,0 +1,145 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;
import java.util.concurrent.*;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Matrix {
private static final int rows = 300000;
private static final int cols = 100;
private static final int[][] matrix = new int[rows][cols];
public static void main(String[] args) throws InterruptedException {
generateMatrix();
try {
writeMatrixToFile(matrix, "generated_matrix.txt");
System.out.println("Generated matrix has been written to generated_matrix.txt");
} catch (IOException e) {
System.err.println("Error writing generated matrix to file: " + e.getMessage());
}
int[][] matrixCopy1 = copyArray(matrix);
int[][] matrixCopy2 = copyArray(matrix);
int[][] matrixCopy3 = copyArray(matrix);
long start = System.nanoTime();
singleThreadSort(matrixCopy1);
long end = System.nanoTime();
System.out.println("Single-threaded algorithm: " + (end - start) / 1_000_000.0 + " ms");
start = System.nanoTime();
multiThreadSort(matrixCopy2);
end = System.nanoTime();
System.out.println("Multi-threaded algorithm (ThreadPoolExecutor): " + (end - start) / 1_000_000.0 + " ms");
start = System.nanoTime();
forkJoinSort(matrixCopy3);
end = System.nanoTime();
System.out.println("Multi-threaded algorithm (ForkJoinPool): " + (end - start) / 1_000_000.0 + " ms");
try {
writeMatrixToFile(matrixCopy3, "sorted_matrix.txt");
System.out.println("Sorted matrix has been written to sorted_matrix.txt");
} catch (IOException e) {
System.err.println("Error writing sorted matrix to file: " + e.getMessage());
}
}
private static void generateMatrix() {
Random rand = new Random();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = rand.nextInt(1000);
}
}
}
private static int[][] copyArray(int[][] original) {
int[][] copy = new int[original.length][];
for (int i = 0; i < original.length; i++) {
copy[i] = Arrays.copyOf(original[i], original[i].length);
}
return copy;
}
private static void singleThreadSort(int[][] matrix) {
Arrays.sort(matrix, Comparator.comparingInt(a -> -a[0]));
}
private static void multiThreadSort(int[][] matrix) throws InterruptedException {
int processors = Runtime.getRuntime().availableProcessors();
int chunkSize = matrix.length / processors;
ExecutorService executor = Executors.newFixedThreadPool(processors);
Future<?>[] futures = new Future[processors];
for (int i = 0; i < processors; i++) {
int start = i * chunkSize;
int end = (i == processors - 1) ? matrix.length : start + chunkSize;
futures[i] = executor.submit(() -> {
Arrays.sort(matrix, start, end, Comparator.comparingInt(a -> -a[0]));
});
}
for (Future<?> future : futures) {
try {
future.get();
} catch (ExecutionException e) {
throw new RuntimeException("Sorting error", e);
}
}
executor.shutdown();
}
private static void forkJoinSort(int[][] matrix) {
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new ForkJoinTask(matrix));
pool.shutdown();
}
static class ForkJoinTask extends RecursiveAction {
private static final int THRESHOLD = 500;
private final int[][] matrix;
ForkJoinTask(int[][] matrix) {
this.matrix = matrix;
}
@Override
protected void compute() {
if (matrix.length <= THRESHOLD) {
Arrays.sort(matrix, Comparator.comparingInt(a -> -a[0]));
} else {
int mid = matrix.length / 2;
int[][] left = Arrays.copyOfRange(matrix, 0, mid);
int[][] right = Arrays.copyOfRange(matrix, mid, matrix.length);
ForkJoinTask leftTask = new ForkJoinTask(left);
ForkJoinTask rightTask = new ForkJoinTask(right);
invokeAll(leftTask, rightTask);
System.arraycopy(left, 0, matrix, 0, left.length);
System.arraycopy(right, 0, matrix, left.length, right.length);
}
}
}
private static void writeMatrixToFile(int[][] matrix, String filename) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
for (int[] row : matrix) {
for (int j = 0; j < row.length; j++) {
writer.write(String.valueOf(row[j]));
if (j < row.length - 1) {
writer.write(" ");
}
}
writer.newLine();
}
}
}
}

View File

@@ -0,0 +1,202 @@
import java.util.*;
import java.util.concurrent.*;
// Класс для сортировки столбцов матрицы по значению в первой строке с использованием разных подходов
public class MatrixColumnSorter {
// Генерация матрицы заданного размера со случайными значениями от 0 до 999
public static int[][] generateMatrix(int rows, int cols) {
Random random = new Random();
int[][] matrix = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = random.nextInt(1000); // Генерация случайного числа
}
}
return matrix;
}
// Вывод части матрицы с ограничением по количеству строк и столбцов
public static void printMatrixPart(int[][] matrix, int maxRows, int maxCols) {
System.out.println("Matrix preview (" + maxRows + "x" + maxCols + "):");
for (int i = 0; i < Math.min(matrix.length, maxRows); i++) {
for (int j = 0; j < Math.min(matrix[0].length, maxCols); j++) {
System.out.printf("%4d", matrix[i][j]); // Форматированный вывод чисел
}
System.out.println();
}
System.out.println();
}
// Однопоточная сортировка столбцов по первой строке
public static int[][] sortColumnsSingleThread(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;
// Создаем список индексов столбцов и сортируем их по значениям в первой строке
List<Integer> columnIndices = new ArrayList<>();
for (int i = 0; i < cols; i++) {
columnIndices.add(i);
}
columnIndices.sort(Comparator.comparingInt(col -> matrix[0][col]));
// Создаем новую матрицу с переупорядоченными столбцами
int[][] sortedMatrix = new int[rows][cols];
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
int srcCol = columnIndices.get(col); // Получаем исходный столбец
sortedMatrix[row][col] = matrix[row][srcCol];
}
}
return sortedMatrix;
}
// Многопоточная сортировка с использованием ThreadPool
public static int[][] sortColumnsThreadPool(int[][] matrix, int numThreads)
throws InterruptedException, ExecutionException {
int rows = matrix.length;
int cols = matrix[0].length;
// Аналогичная сортировка индексов столбцов
List<Integer> columnIndices = new ArrayList<>();
for (int i = 0; i < cols; i++) {
columnIndices.add(i);
}
columnIndices.sort(Comparator.comparingInt(col -> matrix[0][col]));
int[][] sortedMatrix = new int[rows][cols];
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numThreads);
List<Callable<Void>> tasks = new ArrayList<>();
// Разделение работы на пакеты по строкам
int chunkSize = (rows + numThreads - 1) / numThreads;
for (int i = 0; i < numThreads; i++) {
final int startRow = i * chunkSize;
final int endRow = Math.min((i + 1) * chunkSize, rows);
tasks.add(() -> {
// Каждая задача обрабатывает свой диапазон строк
for (int row = startRow; row < endRow; row++) {
for (int col = 0; col < cols; col++) {
int srcCol = columnIndices.get(col);
sortedMatrix[row][col] = matrix[row][srcCol];
}
}
return null;
});
}
// Запуск всех задач и ожидание завершения
executor.invokeAll(tasks);
executor.shutdown();
return sortedMatrix;
}
// Сортировка с использованием ForkJoin Framework
public static int[][] sortColumnsForkJoin(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;
// Сортировка индексов как в предыдущих методах
List<Integer> columnIndices = new ArrayList<>();
for (int i = 0; i < cols; i++) {
columnIndices.add(i);
}
columnIndices.sort(Comparator.comparingInt(col -> matrix[0][col]));
int[][] sortedMatrix = new int[rows][cols];
ForkJoinPool pool = new ForkJoinPool();
// Запуск рекурсивной задачи
pool.invoke(new CopyTask(matrix, sortedMatrix, columnIndices, 0, rows));
pool.shutdown();
return sortedMatrix;
}
// Внутренний класс для реализации рекурсивной задачи ForkJoin
static class CopyTask extends RecursiveAction {
private final int[][] source;
private final int[][] dest;
private final List<Integer> columnIndices;
private final int startRow;
private final int endRow;
private static final int THRESHOLD = 100; // Порог для разделения задачи
public CopyTask(int[][] source, int[][] dest, List<Integer> columnIndices, int startRow, int endRow) {
this.source = source;
this.dest = dest;
this.columnIndices = columnIndices;
this.startRow = startRow;
this.endRow = endRow;
}
@Override
protected void compute() {
// Если диапазон строк меньше порога - выполняем копирование
if (endRow - startRow <= THRESHOLD) {
for (int row = startRow; row < endRow; row++) {
for (int col = 0; col < columnIndices.size(); col++) {
int srcCol = columnIndices.get(col);
dest[row][col] = source[row][srcCol];
}
}
} else {
// Рекурсивное разделение задачи на две части
int mid = (startRow + endRow) / 2;
CopyTask left = new CopyTask(source, dest, columnIndices, startRow, mid);
CopyTask right = new CopyTask(source, dest, columnIndices, mid, endRow);
invokeAll(left, right); // Запуск подзадач
}
}
}
// Проверка отсортированности первой строки матрицы
public static boolean isSorted(int[][] matrix) {
for (int i = 1; i < matrix[0].length; i++) {
if (matrix[0][i - 1] > matrix[0][i]) {
return false;
}
}
return true;
}
// Основной метод для тестирования
public static void main(String[] args) throws InterruptedException, ExecutionException {
int rows = 5000;
int cols = 5000;
// Генерация матрицы
System.out.println("Generating matrix...");
int[][] matrix = generateMatrix(rows, cols);
System.out.println("Matrix generated.");
// Вывод части исходной матрицы
printMatrixPart(matrix, 10, 10);
// Тестирование однопоточной версии
long start = System.currentTimeMillis();
int[][] sortedSingle = sortColumnsSingleThread(matrix);
long end = System.currentTimeMillis();
System.out.println("\nAfter single-threaded sorting:");
printMatrixPart(sortedSingle, 10, 10);
System.out.println("Single-threaded time: " + (end - start) + " ms");
System.out.println("Is sorted: " + isSorted(sortedSingle));
// Тестирование ThreadPool версии
start = System.currentTimeMillis();
int[][] sortedThreadPool = sortColumnsThreadPool(matrix, Runtime.getRuntime().availableProcessors());
end = System.currentTimeMillis();
System.out.println("\nAfter ThreadPool sorting:");
printMatrixPart(sortedThreadPool, 10, 10);
System.out.println("ThreadPool time: " + (end - start) + " ms");
System.out.println("Is sorted: " + isSorted(sortedThreadPool));
// Тестирование ForkJoin версии
start = System.currentTimeMillis();
int[][] sortedForkJoin = sortColumnsForkJoin(matrix);
end = System.currentTimeMillis();
System.out.println("\nAfter ForkJoin sorting:");
printMatrixPart(sortedForkJoin, 10, 10);
System.out.println("ForkJoin time: " + (end - start) + " ms");
System.out.println("Is sorted: " + isSorted(sortedForkJoin));
}
}

186
ryabov_ilya_lab1/README.md Normal file
View File

@@ -0,0 +1,186 @@
Лабораторная работа №1
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания. Необходимо:
Разработать однопоточный вариант алгоритма и замерить время его работы.
Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
Вариант 22
Упорядочить столбцы матрицы по возрастанию первых элементов.
Как запустить лабораторную работу:
javac MatrixColumnSorter.java компилирует исходный код
java MatrixColumnSorter команда запускает скомпилированный класс
Какие технологии использовали:
java.util.*: Содержит основные классы коллекций (List, ArrayList, Scanner и т.д.) и другие утилиты.
java.util.concurrent.*: Используется для работы с многопоточностью и параллельными алгоритмами.
Как работает программа:
Генерация матрицы случайных чисел размером rows x cols.
Вывод части матрицы: Перед сортировкой программа выводит первые 10 строк и 10 столбцов матрицы для наглядности.
Однопоточная сортировка:
Создание списка индексов столбцов.
Сортировка индексов: Индексы сортируются по возрастанию первых элементов соответствующих столбцов.
Создание новой матрицы: Программа создаёт новую матрицу, в которой столбцы переупорядочены согласно отсортированным индексам.
Вывод результата - первые 10 строк и 10 столбцов новой матрицы.
Замер времени.
Проверка первой строки: Программа проверяет, что первые элементы столбцов отсортированы по возрастанию.
Многопоточная сортировка с использованием ThreadPoolExecutor:
Создание списка индексов столбцов.
Создание пула потоков.
Разделение задачи на подзадачи: Матрица разделяется на части по строкам. Каждый поток обрабатывает свой диапазон строк.
Выполнение задач в пуле потоков.
Завершение работы пула.
Вывод результата - первые 10 строк и 10 столбцов новой матрицы.
Замер времени.
Проверка первой строки: Программа проверяет, что первые элементы столбцов отсортированы по возрастанию.
Многопоточная сортировка с использованием ForkJoinPool
Создание списка индексов столбцов.
Создание пула ForkJoinPool.
Рекурсивное разделение задачи: задача разделяется на подзадачи рекурсивно, пока размер подзадачи не станет меньше порогового значения (THRESHOLD).
Выполнение подзадач.
Завершение работы пула.
Вывод результата - первые 10 строк и 10 столбцов новой матрицы.
Замер времени.
Проверка первой строки: Программа проверяет, что первые элементы столбцов отсортированы по возрастанию.
Тесты:
==========================
(Размер матрицы 1000х1000)
==========================
Generating matrix...
Matrix generated.
Matrix preview (10x10):
533 76 916 191 716 162 404 425 446 310
408 937 746 171 962 546 654 865 231 670
356 365 494 873 59 961 677 718 857 386
466 674 622 588 486 115 195 310 720 860
148 354 312 279 745 434 623 629 169 976
511 313 350 179 719 156 828 998 754 286
603 413 940 641 419 960 916 672 53 510
280 345 125 67 723 745 858 689 924 658
217 917 627 814 488 161 249 838 755 119
482 951 678 90 995 223 791 840 698 723
After single-threaded sorting:
Matrix preview (10x10):
1 2 2 2 3 5 5 5 6 7
850 585 7 574 557 281 209 428 161 218
663 592 395 349 997 370 879 720 218 794
342 582 702 444 482 665 28 679 312 623
168 229 298 393 643 263 463 317 709 259
503 271 354 295 483 291 83 628 228 368
486 881 588 457 526 835 436 979 580 765
796 954 551 501 371 551 687 161 644 729
225 204 309 947 65 947 820 872 846 142
830 618 538 779 452 412 277 20 263 263
Single-threaded time: 14 ms
Is sorted: true
After ThreadPool sorting:
Matrix preview (10x10):
1 2 2 2 3 5 5 5 6 7
850 585 7 574 557 281 209 428 161 218
663 592 395 349 997 370 879 720 218 794
342 582 702 444 482 665 28 679 312 623
168 229 298 393 643 263 463 317 709 259
503 271 354 295 483 291 83 628 228 368
486 881 588 457 526 835 436 979 580 765
796 954 551 501 371 551 687 161 644 729
225 204 309 947 65 947 820 872 846 142
830 618 538 779 452 412 277 20 263 263
ThreadPool time: 44 ms
Is sorted: true
After ForkJoin sorting:
Matrix preview (10x10):
1 2 2 2 3 5 5 5 6 7
850 585 7 574 557 281 209 428 161 218
663 592 395 349 997 370 879 720 218 794
342 582 702 444 482 665 28 679 312 623
168 229 298 393 643 263 463 317 709 259
503 271 354 295 483 291 83 628 228 368
486 881 588 457 526 835 436 979 580 765
796 954 551 501 371 551 687 161 644 729
225 204 309 947 65 947 820 872 846 142
830 618 538 779 452 412 277 20 263 263
ForkJoin time: 43 ms
Is sorted: true
==========================
(Размер матрицы 5000х5000)
==========================
Generating matrix...
Matrix generated.
Matrix preview (10x10):
341 367 943 684 620 102 667 938 959 94
893 685 483 313 165 602 151 350 936 559
883 640 642 875 128 694 96 98 886 684
661 984 299 604 113 432 662 684 70 373
816 104 160 583 368 289 940 44 698 618
806 167 284 98 416 2 701 294 355 930
481 539 851 245 403 217 991 30 559 781
997 46 965 618 831 76 947 843 460 535
481 179 35 621 691 795 338 669 867 613
958 72 119 19 258 840 225 785 670 463
After single-threaded sorting:
Matrix preview (10x10):
0 0 0 0 0 1 1 1 2 2
513 916 262 774 269 757 773 30 786 811
513 178 835 572 502 681 114 907 998 677
437 554 916 445 626 700 724 659 276 144
228 876 323 830 874 138 467 514 103 328
846 251 192 157 329 540 913 917 225 328
37 508 76 613 333 763 322 963 771 504
599 77 809 216 588 357 52 543 105 600
962 335 811 387 225 774 735 131 913 300
86 710 561 13 550 280 507 662 122 161
Single-threaded time: 98 ms
Is sorted: true
After ThreadPool sorting:
Matrix preview (10x10):
0 0 0 0 0 1 1 1 2 2
513 916 262 774 269 757 773 30 786 811
513 178 835 572 502 681 114 907 998 677
437 554 916 445 626 700 724 659 276 144
228 876 323 830 874 138 467 514 103 328
846 251 192 157 329 540 913 917 225 328
37 508 76 613 333 763 322 963 771 504
599 77 809 216 588 357 52 543 105 600
962 335 811 387 225 774 735 131 913 300
86 710 561 13 550 280 507 662 122 161
ThreadPool time: 72 ms
Is sorted: true
After ForkJoin sorting:
Matrix preview (10x10):
0 0 0 0 0 1 1 1 2 2
513 916 262 774 269 757 773 30 786 811
513 178 835 572 502 681 114 907 998 677
437 554 916 445 626 700 724 659 276 144
228 876 323 830 874 138 467 514 103 328
846 251 192 157 329 540 913 917 225 328
37 508 76 613 333 763 322 963 771 504
599 77 809 216 588 357 52 543 105 600
962 335 811 387 225 774 735 131 913 300
86 710 561 13 550 280 507 662 122 161
ForkJoin time: 88 ms
Is sorted: true
Вывод:
На маленьких матрицах однопоточное решение быстрее из-за отсутствия накладных расходов на создание потоков и синхронизацию.
На больших матрицах многопоточное решение выигрывает за счёт параллелизма и эффективного использования ресурсов процессора.
Исходя из полученных результатов мы можем сделать вывод, что чем больше объём входных данных тем более выгодно использовать многопоточность для реализации алгоритмов.

138
ryabov_ilya_lab_2/Main.java Normal file
View File

@@ -0,0 +1,138 @@
import mpi.MPI;
public class Main {
private static final int PRINT_SIZE = 10; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public static void main(String[] args) {
MPI.Init(args);
int rank = MPI.COMM_WORLD.Rank(); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int size = MPI.COMM_WORLD.Size(); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int[][] matrix = null;
int rows = 5000;
int cols = 5000;
long starttime = 0;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (rank == 0) {
matrix = MatrixGenerator.generateMatrix(rows, cols);
System.out.println("Initial matrix (first 10x10):");
printMatrixHead(matrix);
starttime = System.currentTimeMillis(); // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int halfRows = rows / 2;
int[] localMatrixFlat = new int[halfRows * cols]; // "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1
if (rank == 0) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
MPI.COMM_WORLD.Recv(localMatrixFlat, 0, localMatrixFlat.length, MPI.INT, 0, 0);
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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++];
}
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0)
int[] columnOrder = new int[cols];
if (rank == 0) {
int[] firstRow = new int[cols];
for (int j = 0; j < cols; j++) {
firstRow[j] = localMatrix[0][j];
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Integer[] indices = new Integer[cols];
for (int i = 0; i < cols; i++) indices[i] = i;
java.util.Arrays.sort(indices, (a, b) -> Integer.compare(firstRow[a], firstRow[b]));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (int i = 0; i < cols; i++) columnOrder[i] = indices[i];
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
MPI.COMM_WORLD.Bcast(columnOrder, 0, cols, MPI.INT, 0);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> columnOrder
int[][] sortedLocalMatrix = new int[halfRows][cols];
for (int j = 0; j < cols; j++) {
int newCol = columnOrder[j]; // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (int i = 0; i < halfRows; i++) {
sortedLocalMatrix[i][j] = localMatrix[i][newCol];
}
}
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0
if (rank == 0) {
int[] secondPart = new int[halfRows * cols];
MPI.COMM_WORLD.Recv(secondPart, 0, secondPart.length, MPI.INT, 1, 1);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int[][] finalMatrix = new int[rows][cols];
index = 0;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
finalMatrix[i][j] = sortedLocalMatrix[i][j];
}
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
index = 0;
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
finalMatrix[i + halfRows][j] = secondPart[index++];
}
}
System.out.println("\nFinal sorted matrix (first 10x10):");
printMatrixHead(finalMatrix);
long duration = System.currentTimeMillis() - starttime;
System.out.println("Execution time: " + duration + " ms");
} else if (rank == 1) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0
int[] sendBuffer = new int[halfRows * cols];
index = 0;
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
sendBuffer[index++] = sortedLocalMatrix[i][j];
}
}
MPI.COMM_WORLD.Send(sendBuffer, 0, sendBuffer.length, MPI.INT, 0, 1);
}
MPI.Finalize();
}
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
private static void printMatrixHead(int[][] matrix) {
for (int i = 0; i < Math.min(PRINT_SIZE, matrix.length); i++) {
for (int j = 0; j < Math.min(PRINT_SIZE, matrix[0].length); j++) {
System.out.printf("%5d", matrix[i][j]);
}
System.out.println();
}
}
}

View File

@@ -0,0 +1,15 @@
import java.util.Random;
public class MatrixGenerator {
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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(1000);
}
}
return matrix;
}
}

182
ryabov_ilya_lab_2/README.md Normal file
View File

@@ -0,0 +1,182 @@
Лабораторная работа №2
Разработка параллельного MPI приложения на языке Java.
Необходимо разработать параллельный вариант алгоритма с применением MPI и
замерить время его работы. В рамках работы программы должно быть две копии
приложения, которые соединяются друг с другом по сети. Сообщения о статусе
соединения (например, что соединение установлено) должны выводиться в консоль.
Запуск каждого экземпляра MPI происходит в своём LXC контейнере. Такие
сервисы, как Docker, использовать нельзя.
Вариант 22
Упорядочить столбцы матрицы по возрастанию первых элементов.
Запуск лабораторной работы
`mpjdaemon -boot 192.168.22.123` - включение демона mpj-express на первом контейнере
`mpjdaemon -boot 192.168.22.124` - включение демона mpj-express на втором контейнере
`mpjrun.sh -np 2 -dev niodev Main` - запуск приложения
Используемые технологии
MPJ Express - библиотека, реализующая MPI для Java.
Как работает программа:
Основная идея программы
Генерация матрицы:
Процесс с рангом 0 создает матрицу размером 1000x1000 и заполняет ее случайными числами.
Распределение данных:
Матрица делится пополам, и вторая половина отправляется процессу с рангом 1.
Определение порядка столбцов:
Процесс 0 определяет порядок сортировки столбцов на основе значений первой строки своей части матрицы.
Сортировка: Оба процесса перестраивают свои локальные матрицы в соответствии с этим порядком.
Сбор результатов: Процесс 1 отправляет свою отсортированную часть обратно процессу 0, который собирает итоговую матрицу и выводит результат.
Комментарии к коду:
int rank = MPI.COMM_WORLD.Rank(); // Получаем ранг текущего процесса
int size = MPI.COMM_WORLD.Size(); // Получаем общее количество процессов
// Процесс 0 генерирует и распределяет данные
if (rank == 0) {
matrix = MatrixGenerator.generateMatrix(rows, cols);
System.out.println("Initial matrix (first 10x10):");
printMatrixHead(matrix);
starttime = System.currentTimeMillis(); // Старт замера времени
}
// Разделение матрицы между процессами
int halfRows = rows / 2;
int[] localMatrixFlat = new int[halfRows * cols]; // "Плоский" массив для пересылки данных
// Процесс 0 отправляет вторую половину данных процессу 1
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) {
// Процесс 1 получает свою часть данных
MPI.COMM_WORLD.Recv(localMatrixFlat, 0, localMatrixFlat.length, MPI.INT, 0, 0);
}
// Рассылка порядка сортировки всем процессам
MPI.COMM_WORLD.Bcast(columnOrder, 0, cols, MPI.INT, 0);
// Переупорядочивание столбцов в локальной матрице согласно columnOrder
int[][] sortedLocalMatrix = new int[halfRows][cols];
for (int j = 0; j < cols; j++) {
int newCol = columnOrder[j]; // Новый индекс столбца
for (int i = 0; i < halfRows; i++) {
sortedLocalMatrix[i][j] = localMatrix[i][newCol];
}
}
// Сбор результатов в процессе 0
if (rank == 0) {
int[] secondPart = new int[halfRows * cols];
MPI.COMM_WORLD.Recv(secondPart, 0, secondPart.length, MPI.INT, 1, 1);
// Сборка финальной матрицы из частей
int[][] finalMatrix = new int[rows][cols];
index = 0;
// Заполнение первой половины из локальных данных
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
finalMatrix[i][j] = sortedLocalMatrix[i][j];
}
}
// Заполнение второй половины из полученных данных
index = 0;
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
finalMatrix[i + halfRows][j] = secondPart[index++];
}
}
else if (rank == 1) {
// Отправка отсортированной части обратно процессу 0
int[] sendBuffer = new int[halfRows * cols];
Тесты:
==========================
(Размер матрицы 1000х1000)
==========================
Starting process <1> on <CT123>
Starting process <0> on <CT124>
Initial matrix (first 10x10):
928 539 223 594 59 535 871 366 402 268
498 133 901 319 957 823 251 117 50 38
743 929 53 865 254 195 557 243 766 840
813 903 96 723 285 427 297 3 940 757
225 991 272 624 7 666 911 659 900 8
19 470 129 478 302 421 176 138 9 781
124 513 609 221 724 243 48 584 733 110
633 704 737 624 383 523 179 785 118 667
543 827 723 50 884 224 154 498 541 344
454 624 828 777 183 767 93 546 507 618
Final sorted matrix (first 10x10):
0 1 2 3 3 4 4 5 5 5
698 457 593 875 186 711 231 931 476 691
903 472 250 978 762 133 409 925 830 408
447 421 768 811 924 869 32 461 346 966
158 439 27 493 240 401 715 964 446 426
438 389 11 215 250 525 573 207 359 563
727 725 812 162 749 677 567 92 811 65
649 386 154 946 924 401 191 276 717 248
262 179 875 980 219 834 85 945 193 637
690 10 938 1 66 188 883 335 11 790
Execution time: 99 ms
Stopping Process <0> on <CT123>
Stopping Process <1> on <CT124>
==========================
(Размер матрицы 5000х5000)
==========================
Starting process <0> on <CT123>
Starting process <1> on <CT124>
Initial matrix (first 10x10):
901 322 301 286 241 742 44 969 404 653
599 518 520 752 360 821 664 132 337 69
331 670 599 211 935 612 816 396 482 722
514 965 243 640 89 129 685 581 426 88
407 153 404 672 75 137 258 991 175 968
56 650 134 105 866 368 499 996 872 892
755 633 79 696 654 876 271 887 516 687
890 235 630 45 769 591 718 412 577 775
31 45 5 668 392 83 60 980 706 157
952 399 608 736 464 142 619 320 254 17
Final sorted matrix (first 10x10):
0 0 0 0 1 1 1 2 2 2
143 544 907 902 717 646 139 825 891 786
47 199 906 55 704 155 523 201 790 491
522 401 68 438 70 596 22 422 848 486
426 433 599 73 345 685 162 503 860 812
413 136 475 569 66 72 34 246 733 426
761 486 211 822 876 836 770 86 944 546
265 854 613 453 86 845 246 540 231 608
598 998 751 955 798 544 670 344 841 394
870 836 953 137 793 885 77 643 954 415
Execution time: 3549 ms
Stopping Process <0> on <CT123>
Stopping Process <1> on <CT124>
Вывод:
Сравнивая с результатом первой лабораторной работы, можно сделать вывод, что MPI-реализация уступает в производительности многопоточным подходам на одной машине из-за высоких накладных расходов на передачу данных и ограниченного параллелизма. Однако MPI становится предпочтительным выбором в распределенных системах (кластерах), где данные физически размещены на разных узлах
MPI эффективен для задач с минимальной коммуникацией между процессами и большим объемом независимых вычислений. В данной задаче коммуникационные издержки и необходимость глобальной синхронизации перевешивают выгоду от распределения вычислений. Многопоточные решения, работающие в рамках общей памяти, избегают этих проблем, обеспечивая лучшую производительность для сортировки матриц.

View File

@@ -0,0 +1,2 @@
192.168.22.124
192.168.22.123

View File

@@ -0,0 +1,11 @@
package example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@@ -0,0 +1,14 @@
package example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,157 @@
package example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
@RestController
@RequestMapping("/master")
public class MasterController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private ApplicationContext context;
private static final String WORKER1_URL = "http://localhost:8080/worker/sort";
private static final String WORKER2_URL = "http://192.168.19.120:8080/worker/sort";
// Максимальное количество элементов для логирования
private static final int MAX_LOG_ELEMENTS = 8;
@GetMapping("/generate-matrix")
public ResponseEntity<int[][]> generateAndSendMatrix() {
int[][] matrix = generateMatrix();
int size = 8; // хотел добавить пользовательский размер для вывода матрицы (но пофиг)
logMatrixPreview(matrix, "Generated matrix");
int mid = matrix[0].length / 2; // Половина количества столбцов
int[][] matrixPart1 = copyColumns(matrix, 0, mid);
int[][] matrixPart2 = copyColumns(matrix, mid, matrix[0].length);
long startTime = System.currentTimeMillis();
ResponseEntity<int[][]> response1 = restTemplate.postForEntity(WORKER1_URL, matrixPart1, int[][].class);
ResponseEntity<int[][]> response2 = restTemplate.postForEntity(WORKER2_URL, matrixPart2, int[][].class);
if (response1.getStatusCode().is2xxSuccessful() && response2.getStatusCode().is2xxSuccessful()) {
int[][] sortedMatrix1 = response1.getBody();
int[][] sortedMatrix2 = response2.getBody();
int[][] mergedMatrix = mergeSortedMatrices(sortedMatrix1, sortedMatrix2);
logMatrixPreview(mergedMatrix, "Final Sorted Matrix");
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
System.out.println("Total sorting time: " + elapsedTime + " ms");
// Извлекаем нужную часть матрицы для клиента
int[][] subMatrix = extractSubMatrix(mergedMatrix, size);
// Возвращаем её клиенту
return ResponseEntity.ok(subMatrix);
}
return ResponseEntity.status(500).body(null);
}
// Метод для получения части матрицы
private int[][] extractSubMatrix(int[][] matrix, int size) {
int rows = Math.min(size, matrix.length);
int cols = Math.min(size, matrix[0].length);
int[][] subMatrix = new int[rows][cols];
for (int i = 0; i < rows; i++) {
System.arraycopy(matrix[i], 0, subMatrix[i], 0, cols);
}
return subMatrix;
}
private int[][] copyColumns(int[][] matrix, int startCol, int endCol) {
int[][] subMatrix = new int[matrix.length][endCol - startCol];
for (int i = 0; i < matrix.length; i++) {
System.arraycopy(matrix[i], startCol, subMatrix[i], 0, endCol - startCol);
}
return subMatrix;
}
// Метод для логирования части матрицы с ограничением по числу элементов
private void logMatrixPreview(int[][] matrix, String message) {
StringBuilder preview = new StringBuilder(message + ": \n");
int elementCount = 0;
outerLoop:
for (int i = 0; i < Math.min(8, matrix.length); i++) { // Логируем только 8 строк
preview.append("[");
for (int j = 0; j < Math.min(8, matrix[i].length); j++) { // Логируем только 8 столбцов
preview.append(matrix[i][j]).append(j < Math.min(8, matrix[i].length) - 1 ? ", " : "");
elementCount++;
if (elementCount >= 64) { // 8x8 = 64 элемента
break outerLoop;
}
}
preview.append("]");
if (elementCount < 64) {
preview.append("\n");
}
}
System.out.println(preview.toString()); // Можно использовать логирование через SLF4J, если нужно
}
private int[][] mergeSortedMatrices(int[][] sortedMatrix1, int[][] sortedMatrix2) {
int rows = sortedMatrix1.length;
int cols = sortedMatrix1[0].length + sortedMatrix2[0].length;
int[][] mergedMatrix = new int[rows][cols];
// Слияние по строкам
for (int i = 0; i < rows; i++) {
int[] mergedRow = new int[cols];
int index1 = 0, index2 = 0, mergedIndex = 0;
while (index1 < sortedMatrix1[i].length && index2 < sortedMatrix2[i].length) {
if (sortedMatrix1[i][index1] < sortedMatrix2[i][index2]) {
mergedRow[mergedIndex++] = sortedMatrix1[i][index1++];
} else {
mergedRow[mergedIndex++] = sortedMatrix2[i][index2++];
}
}
// Добавляем оставшиеся элементы из первой части
while (index1 < sortedMatrix1[i].length) {
mergedRow[mergedIndex++] = sortedMatrix1[i][index1++];
}
// Добавляем оставшиеся элементы из второй части
while (index2 < sortedMatrix2[i].length) {
mergedRow[mergedIndex++] = sortedMatrix2[i][index2++];
}
mergedMatrix[i] = mergedRow;
}
return mergedMatrix;
}
private int[][] generateMatrix() {
int rows = 5000;
int cols = 5000;
int[][] matrix = new int[rows][cols];
Random rand = new Random();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = rand.nextInt(100); // Случайные числа от 0 до 99
}
}
return matrix;
}
}

View File

@@ -0,0 +1,393 @@
## Лабораторная работа №3
Разработка распределенного приложения с использованием фреймворка Spring Boot.
Действия по варианту должны производиться в LXC контейнерах. Необходимо
разработать параллельный вариант алгоритма с применением сервис-ориентированного подхода и фреймворка Spring Boot, замерить время его работы.
## Вариант и задание
22 Упорядочить столбцы матрицы по возрастанию первых элементов.
## Что делают сервисы
Сервис работник получает матрицу по запросу. Логирует сортировку и возрващает матрицу обратно.
Сервисы работники запускаются на двух контейнерах, на одном работает также сервис-управленец. На втором контейнере работает только сервис-работник.
Сервис-управленец генерирует матрицу по GET запросу, делит её пополам и распределяет между работниками (локальному и удаленному). Потом
получает матрицы от двух работников и сливает их воедино.
Затем возвращает эту матрицу пользователю в виде json.
## Описание кода
```java
package example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
```
Этот класс используется для запуска обоих сервисов.
Класс используется каждый раз когда запускается сервис Master или Worker. Оба сервиса имею свой App класс, он не отличается у обоих.
Ничего особо интересного.
```java
package example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
```
Этот класс `AppConfig` создаёт и управляет
единственным экземпляром `RestTemplate` в приложении Spring,
позволяя удобно внедрять его через `@Autowired` без создания вручную.
```java
@GetMapping("/generate-matrix")
public ResponseEntity<int[][]> generateAndSendMatrix() {
int[][] matrix = generateMatrix();
int size = 8; // хотел добавить пользовательский размер для вывода матрицы (но пофиг)
logMatrixPreview(matrix, "Generated matrix");
int mid = matrix[0].length / 2; // Половина количества столбцов
int[][] matrixPart1 = copyColumns(matrix, 0, mid);
int[][] matrixPart2 = copyColumns(matrix, mid, matrix[0].length);
long startTime = System.currentTimeMillis();
ResponseEntity<int[][]> response1 = restTemplate.postForEntity(WORKER1_URL, matrixPart1, int[][].class);
ResponseEntity<int[][]> response2 = restTemplate.postForEntity(WORKER2_URL, matrixPart2, int[][].class);
if (response1.getStatusCode().is2xxSuccessful() && response2.getStatusCode().is2xxSuccessful()) {
int[][] sortedMatrix1 = response1.getBody();
int[][] sortedMatrix2 = response2.getBody();
int[][] mergedMatrix = mergeSortedMatrices(sortedMatrix1, sortedMatrix2);
logMatrixPreview(mergedMatrix, "Final Sorted Matrix");
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
System.out.println("Total sorting time: " + elapsedTime + " ms");
// Извлекаем нужную часть матрицы для клиента
int[][] subMatrix = extractSubMatrix(mergedMatrix, size);
// Возвращаем её клиенту
return ResponseEntity.ok(subMatrix);
}
return ResponseEntity.status(500).body(null);
}
```
Метод создаёт случайную матрицу `5000x5000`, логирует её
часть и разделяет на две равные части по столбцам. Затем каждая часть
отправляется на свой воркер (`WORKER1_URL` и `WORKER2_URL`) для
сортировки. После успешного ответа от обоих воркеров
их отсортированные части сливаются в одну матрицу с
помощью алгоритма слияния двух отсортированных
массивов.
Итоговая отсортированная матрица логируется, измеряется время выполнения, и клиенту
возвращается её `8x8` фрагмент. Если в процессе
что-то пошло не так, метод возвращает HTTP 500.
```java
@RestController
public class WorkerController {
private static final Logger logger = LoggerFactory.getLogger(WorkerController.class);
// Максимальное количество элементов, которые мы хотим выводить в логах
private static final int MAX_LOG_ELEMENTS = 64; // 8x8 = 64 элемента
// Endpoint для сортировки части матрицы
@PostMapping("/worker/sort")
public ResponseEntity<int[][]> sortMatrix(@RequestBody int[][] matrixPart) {
// Логируем получение запроса от клиента
logger.info("Received request to sort matrix");
// Логируем часть матрицы с ограничением по числу элементов
logMatrixPreview(matrixPart, "Received matrix part");
// Сортировка столбцов по первому элементу в каждой колонке
int[][] sortedMatrix = sortColumnsByFirstElement(matrixPart);
// Логируем отсортированную часть матрицы с ограничением
logMatrixPreview(sortedMatrix, "Sorted matrix part");
// Логируем результат сортировки
logger.info("Successfully sorted matrix part ");
// Возвращаем отсортированную часть матрицы с HTTP-статусом 200 (OK)
return ResponseEntity.ok(sortedMatrix);
}
// Метод для сортировки столбцов по первому элементу в каждом столбце
private static int[][] sortColumnsByFirstElement(int[][] matrix) {
int cols = matrix[0].length;
int rows = matrix.length;
Integer[] indices = new Integer[cols];
for (int i = 0; i < cols; i++) {
indices[i] = i;
}
// Сортируем индексы столбцов по значению первого элемента
Arrays.sort(indices, (a, b) -> Integer.compare(matrix[0][a], matrix[0][b]));
// Создаем новую матрицу с отсортированными столбцами
int[][] sortedMatrix = new int[rows][cols];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
sortedMatrix[j][i] = matrix[j][indices[i]];
}
}
return sortedMatrix;
}
}
```
* Класс `WorkerController` представляет собой REST-контроллер в Spring Boot, который обрабатывает HTTP-запросы на сортировку частей матрицы. Он принимает матрицу через POST-запрос на `/worker/sort`, сортирует ее столбцы по значениям первой строки и возвращает результат.
* Метод `sortColumnsByFirstElement()` выполняет сортировку столбцов, используя массив индексов и создавая новую матрицу в отсортированном порядке. Метод `logMatrixPreview()` ограничивает количество выводимых в лог элементов (до 64), чтобы не перегружать логи.
* Контроллер использует `Logger` для записи информации о входных и выходных данных. В результате клиент получает отсортированную часть матрицы в виде JSON-ответа со статусом `200 OK`.
## Как запустить
Для сборки этого проекта потребуются следующие зависимости:
```xml
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.2</version>
</dependency>
<!-- Spring Boot Starter для веб-приложений -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.2</version>
</dependency>
<!-- Логирование -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
```
---
После сборки я получил два jar архива со всеми зависимостями:
* MasterService-1.0-SNAPSHOT-exec.jar и
* WorkerService-1.0-SNAPSHOT-exec.jar
> Примечание: перед тем как собрать проект нужно изменить URL для второго работника. Указать соответствующий ip адрес.
Сервисы можно независимо друг от друга запускать.
То есть порядок запуска не важен.
> java -jar {name}Service-1.0-SNAPSHOT-exec.jar
Также на втором работнике нужно не забыть запустить воркера
После нужно на первом контейнере обратиться к MasterService. В консоли для этого нужно использовать следующую команду.
> curl http://localhost:8081/master/generate-matrix
## Выходные значения
**Запрос пользователя**
```bash
[root@CT119 ~]# curl http://localhost:8081/master/generate-matrix
[ [0,0,0,0,0,0,0,0],
[88,5,9,25,58,52,51,19],
[33,46,23,54,44,27,16,82],
[44,24,33,7,15,9,52,14],
[60,7,25,50,69,86,12,86],
[51,50,88,54,66,4,45,43],
[17,44,46,94,71,4,64,47],
[90,3,35,45,89,10,38,49]]#
```
**Первый работник**
```bash
[root@CT119 Lab3]# java -jar WorkerService-1.0-SNAPSHOT-exec.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.2)
2025-03-15T23:31:25.081Z INFO 3158 --- [ main] com.example.App : Starting App using Java 23.0.2 with PID 3158 (/labs/Lab3/WorkerService-1.0-SNAPSHOT-exec.jar started by root in /labs/Lab3)
2025-03-15T23:31:25.093Z INFO 3158 --- [ main] com.example.App : No active profile set, falling back to 1 default profile: "default"
2025-03-15T23:31:27.140Z INFO 3158 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-15T23:31:27.160Z INFO 3158 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-15T23:31:27.160Z INFO 3158 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.18]
2025-03-15T23:31:27.215Z INFO 3158 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-15T23:31:27.217Z INFO 3158 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1880 ms
2025-03-15T23:31:27.878Z INFO 3158 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
2025-03-15T23:31:27.909Z INFO 3158 --- [ main] com.example.App : Started App in 3.664 seconds (process running for 4.523)
2025-03-15T23:33:55.166Z INFO 3158 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-03-15T23:33:55.171Z INFO 3158 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-03-15T23:33:55.174Z INFO 3158 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2025-03-15T23:33:56.179Z INFO 3158 --- [nio-8080-exec-1] com.example.WorkerController : Received request to sort matrix
2025-03-15T23:33:56.181Z INFO 3158 --- [nio-8080-exec-1] com.example.WorkerController : Received matrix part:
[0, 28, 30, 90, 96, 67, 7, 25]
[88, 63, 65, 43, 34, 28, 2, 64]
[33, 35, 51, 30, 41, 40, 71, 30]
[44, 23, 10, 3, 60, 35, 7, 35]
[60, 45, 43, 2, 90, 63, 70, 51]
[51, 83, 79, 29, 53, 24, 88, 57]
[17, 54, 93, 59, 7, 4, 72, 21]
[90, 81, 32, 38, 53, 67, 43, 30
2025-03-15T23:33:56.662Z INFO 3158 --- [nio-8080-exec-1] com.example.WorkerController : Sorted matrix part:
[0, 0, 0, 0, 0, 0, 0, 0]
[88, 5, 9, 25, 58, 52, 51, 19]
[33, 46, 23, 54, 44, 27, 16, 82]
[44, 24, 33, 7, 15, 9, 52, 14]
[60, 7, 25, 50, 69, 86, 12, 86]
[51, 50, 88, 54, 66, 4, 45, 43]
[17, 44, 46, 94, 71, 4, 64, 47]
[90, 3, 35, 45, 89, 10, 38, 49
2025-03-15T23:33:56.664Z INFO 3158 --- [nio-8080-exec-1] com.example.WorkerController : Successfully sorted matrix part
```
**Второй работник**
```bash
[root@CT120 Lab3]# java -jar WorkerService-1.0-SNAPSHOT-exec.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.2)
2025-03-15T23:33:24.294Z INFO 1235 --- [ main] com.example.App : Starting App using Java 21.0.6 with PID 1235 (/labs/Lab3/WorkerService-1.0-SNAPSHOT-exec.jar started by root in /labs/Lab3)
2025-03-15T23:33:24.305Z INFO 1235 --- [ main] com.example.App : No active profile set, falling back to 1 default profile: "default"
2025-03-15T23:33:26.106Z INFO 1235 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-15T23:33:26.132Z INFO 1235 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-15T23:33:26.133Z INFO 1235 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.18]
2025-03-15T23:33:26.177Z INFO 1235 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-15T23:33:26.181Z INFO 1235 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1614 ms
2025-03-15T23:33:27.095Z INFO 1235 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
2025-03-15T23:33:27.132Z INFO 1235 --- [ main] com.example.App : Started App in 3.561 seconds (process running for 4.401)
2025-03-15T23:33:57.843Z INFO 1235 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-03-15T23:33:57.844Z INFO 1235 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-03-15T23:33:57.846Z INFO 1235 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2025-03-15T23:33:58.898Z INFO 1235 --- [nio-8080-exec-1] com.example.WorkerController : Received request to sort matrix
2025-03-15T23:33:58.900Z INFO 1235 --- [nio-8080-exec-1] com.example.WorkerController : Received matrix part:
[17, 35, 65, 0, 45, 96, 82, 26]
[45, 30, 88, 89, 69, 6, 19, 32]
[76, 91, 13, 82, 42, 44, 17, 14]
[47, 88, 69, 28, 31, 97, 19, 38]
[23, 92, 79, 89, 61, 70, 85, 42]
[82, 21, 39, 72, 8, 5, 38, 9]
[73, 13, 9, 8, 1, 70, 97, 3]
[90, 12, 21, 73, 15, 84, 59, 52
2025-03-15T23:33:59.288Z INFO 1235 --- [nio-8080-exec-1] com.example.WorkerController : Sorted matrix part:
[0, 0, 0, 0, 0, 0, 0, 0]
[89, 38, 75, 33, 95, 34, 41, 24]
[82, 45, 40, 17, 2, 57, 38, 95]
[28, 21, 58, 89, 28, 29, 38, 36]
[89, 67, 77, 69, 76, 0, 54, 30]
[72, 26, 11, 65, 4, 33, 1, 68]
[8, 75, 34, 83, 38, 37, 63, 11]
[73, 50, 60, 74, 83, 64, 28, 49
2025-03-15T23:33:59.289Z INFO 1235 --- [nio-8080-exec-1] com.example.WorkerController : Successfully sorted matrix part
```
**Главный сервис**
```bash
[root@CT119 Lab3]# java -jar MasterService-1.0-SNAPSHOT-exec.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.2)
2025-03-15T23:31:04.797Z INFO 3118 --- [ main] example.App : Starting App using Java 23.0.2 with PID 3118 (/labs/Lab3/MasterService-1.0-SNAPSHOT-exec.jar started by root in /labs/Lab3)
2025-03-15T23:31:04.803Z INFO 3118 --- [ main] example.App : No active profile set, falling back to 1 default profile: "default"
2025-03-15T23:31:06.599Z INFO 3118 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8081 (http)
2025-03-15T23:31:06.625Z INFO 3118 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-15T23:31:06.625Z INFO 3118 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.18]
2025-03-15T23:31:06.683Z INFO 3118 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-15T23:31:06.686Z INFO 3118 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1652 ms
2025-03-15T23:31:07.317Z INFO 3118 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8081 (http) with context path ''
2025-03-15T23:31:07.338Z INFO 3118 --- [ main] example.App : Started App in 3.342 seconds (process running for 4.217)
2025-03-15T23:33:54.028Z INFO 3118 --- [nio-8081-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-03-15T23:33:54.029Z INFO 3118 --- [nio-8081-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-03-15T23:33:54.031Z INFO 3118 --- [nio-8081-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Generated matrix:
[0, 28, 30, 90, 96, 67, 7, 25]
[88, 63, 65, 43, 34, 28, 2, 64]
[33, 35, 51, 30, 41, 40, 71, 30]
[44, 23, 10, 3, 60, 35, 7, 35]
[60, 45, 43, 2, 90, 63, 70, 51]
[51, 83, 79, 29, 53, 24, 88, 57]
[17, 54, 93, 59, 7, 4, 72, 21]
[90, 81, 32, 38, 53, 67, 43, 30
Final Sorted Matrix:
[0, 0, 0, 0, 0, 0, 0, 0]
[88, 5, 9, 25, 58, 52, 51, 19]
[33, 46, 23, 54, 44, 27, 16, 82]
[44, 24, 33, 7, 15, 9, 52, 14]
[60, 7, 25, 50, 69, 86, 12, 86]
[51, 50, 88, 54, 66, 4, 45, 43]
[17, 44, 46, 94, 71, 4, 64, 47]
[90, 3, 35, 45, 89, 10, 38, 49
Total sorting time: 8693 ms
```
В процессе я слегка отредактировал логику. Изначально я отправлял первому работнику на повторную сортировку матрицы
для того чтобы отсортировались между собой уже отсортированнные части матрицы. Однако я все же решил изменить логику и сделать слияние матрицы внутри мастера.
Удалось получить такой результат (В остальном выходные значения аналогичные)
```bash
Generated matrix:
[8, 79, 10, 58, 77, 94, 71, 79]
[40, 85, 10, 99, 92, 67, 74, 53]
[44, 69, 19, 59, 18, 82, 23, 4]
[88, 94, 17, 5, 45, 55, 27, 73]
[11, 27, 40, 94, 45, 17, 88, 9]
[38, 2, 41, 48, 16, 26, 14, 95]
[69, 71, 69, 66, 98, 99, 70, 65]
[0, 23, 61, 4, 30, 71, 1, 5
Final Sorted Matrix:
[0, 0, 0, 0, 0, 0, 0, 0]
[11, 46, 69, 1, 84, 5, 23, 29]
[17, 6, 83, 11, 12, 77, 33, 86]
[50, 54, 24, 59, 16, 68, 43, 19]
[7, 33, 17, 39, 81, 19, 73, 60]
[5, 16, 24, 41, 63, 22, 24, 91]
[1, 54, 59, 5, 55, 68, 80, 23]
[21, 35, 5, 35, 46, 9, 18, 51
Total sorting time: 2757 ms
```
Время сократилось весьма значительно (но только после второго запроса).
## Вывод
Была успешно реализована микро-сервисная архитектура, где матрица по запросу пользователя генерируется матрица 5000×5000. Она делится на две
части и отправляется сервисам-работникам для сортировки. Это ускоряет обработку,
но добавляет издержки на передачу данных. После сортировки части объединяются и возвращаются
пользователю. Сервисы работают независимо, что упрощает масштабирование.

View File

@@ -0,0 +1,11 @@
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@@ -0,0 +1,89 @@
package com.example;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RestController
public class WorkerController {
private static final Logger logger = LoggerFactory.getLogger(WorkerController.class);
// Максимальное количество элементов, которые мы хотим выводить в логах
private static final int MAX_LOG_ELEMENTS = 64; // 8x8 = 64 элемента
// Endpoint для сортировки части матрицы
@PostMapping("/worker/sort")
public ResponseEntity<int[][]> sortMatrix(@RequestBody int[][] matrixPart) {
// Логируем получение запроса от клиента
logger.info("Received request to sort matrix");
// Логируем часть матрицы с ограничением по числу элементов
logMatrixPreview(matrixPart, "Received matrix part");
// Сортировка столбцов по первому элементу в каждой колонке
int[][] sortedMatrix = sortColumnsByFirstElement(matrixPart);
// Логируем отсортированную часть матрицы с ограничением
logMatrixPreview(sortedMatrix, "Sorted matrix part");
// Логируем результат сортировки
logger.info("Successfully sorted matrix part ");
// Возвращаем отсортированную часть матрицы с HTTP-статусом 200 (OK)
return ResponseEntity.ok(sortedMatrix);
}
// Метод для сортировки столбцов по первому элементу в каждом столбце
private static int[][] sortColumnsByFirstElement(int[][] matrix) {
int cols = matrix[0].length;
int rows = matrix.length;
Integer[] indices = new Integer[cols];
for (int i = 0; i < cols; i++) {
indices[i] = i;
}
// Сортируем индексы столбцов по значению первого элемента
Arrays.sort(indices, (a, b) -> Integer.compare(matrix[0][a], matrix[0][b]));
// Создаем новую матрицу с отсортированными столбцами
int[][] sortedMatrix = new int[rows][cols];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
sortedMatrix[j][i] = matrix[j][indices[i]];
}
}
return sortedMatrix;
}
// Метод для логирования части матрицы с ограничением по числу элементов
private void logMatrixPreview(int[][] matrix, String message) {
StringBuilder preview = new StringBuilder(message + ": \n");
int elementCount = 0;
outerLoop:
for (int i = 0; i < Math.min(8, matrix.length); i++) { // Логируем только 8 строк
preview.append("[");
for (int j = 0; j < Math.min(8, matrix[i].length); j++) { // Логируем только 8 столбцов
preview.append(matrix[i][j]).append(j < Math.min(8, matrix[i].length) - 1 ? ", " : "");
elementCount++;
if (elementCount >= MAX_LOG_ELEMENTS) { // 8x8 = 64 элемента
break outerLoop;
}
}
preview.append("]");
if (elementCount < MAX_LOG_ELEMENTS) {
preview.append("\n");
}
}
logger.info(preview.toString());
}
}

View File

@@ -0,0 +1,325 @@
# Лабораторная №4
Разработка распределенного приложения с использованием платформы Apache Ignite.
Необходимо разработать параллельный вариант алгоритма с применением подхода Grid
Gain и платформа Apache Ignite, замерить время его работы.
## Вариант и задание
22 Упорядочить столбцы матрицы по возрастанию первых элементов.
## Что программа делает
- **MatrixSortingTask** распределяет работу по сортировке столбцов между узлами.
- Каждый узел выполняет сортировку своих столбцов с помощью **ColumnSortJob**.
- После завершения сортировки результаты агрегации собираются и объединяются в итоговую отсортированную матрицу.
Таким образом, задача сортировки матрицы с помощью распределённой обработки на кластере Apache Ignite.
## Описание кода
### **MatrixSortingTask** (Задача сортировки матрицы)
Этот класс отвечает за распределение работы по сортировке матрицы на несколько узлов Apache Ignite и последующую агрегацию результатов.
#### **Основные моменты**:
1. **Распределение работы**:
- Задача делит столбцы матрицы между несколькими узлами кластера для параллельной сортировки.
- Каждый узел сортирует определённые столбцы матрицы.
2. **Реализация метода `map`**:
- Метод `map` делит работу на несколько частей (по столбцам).
- Для каждого узла создаётся задание (задача сортировки столбцов), которое будет обрабатываться этим узлом.
```java
@Override
public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> nodes, int[][] matrix) {
int cols = matrix[0].length;
int nodeCount = nodes.size();
int chunkSize = (int) Math.ceil((double) cols / nodeCount);
Map<ComputeJob, ClusterNode> map = new HashMap<>();
// Разбиваем работу на части по столбцам
for (int i = 0; i < nodeCount; i++) {
int startCol = i * chunkSize;
int endCol = Math.min(startCol + chunkSize, cols);
int[][] subMatrix = extractColumns(matrix, startCol, endCol);
map.put(new ColumnSortJob(subMatrix, startCol), nodes.get(i));
}
return map;
}
```
- **`map`**:
- Вычисляется количество узлов и делится матрица на части (столбцы), которые будут обработаны каждым узлом.
- Создаётся задание `ColumnSortJob` для каждого узла с подматрицей столбцов и индексом начала сортируемых столбцов.
3. **Агрегация результатов**:
- После того как каждый узел завершит свою работу (сортировку столбцов), результаты собираются в одном методе `reduce`.
- Все отсортированные части матрицы объединяются в итоговую отсортированную матрицу.
```java
@Override
public int[][] reduce(List<ComputeJobResult> results) {
int[][] sortedMatrix = new int[results.get(0).getData().length][];
int currentCol = 0;
for (ComputeJobResult result : results) {
int[][] sortedChunk = (int[][]) result.getData();
mergeColumns(sortedMatrix, sortedChunk, currentCol);
currentCol += sortedChunk[0].length;
}
return sortedMatrix;
}
```
- **`reduce`**:
- Объединяет отсортированные столбцы из разных узлов в единую матрицу.
- Метод `mergeColumns` копирует данные из отсортированных частей в итоговую матрицу.
4. **Дополнительные методы**:
- **`extractColumns`**: Извлекает подматрицу столбцов для конкретного узла.
- **`mergeColumns`**: Объединяет отсортированные столбцы в итоговую матрицу.
```java
private int[][] extractColumns(int[][] matrix, int startCol, int endCol) {
// Извлекает столбцы с startCol по endCol
}
private void mergeColumns(int[][] target, int[][] sortedChunk, int startCol) {
// Объединяет отсортированные столбцы с основной матрицей
}
```
---
### **ColumnSortJob** (Задача сортировки столбцов)
Этот класс представляет собой задачу для одного узла кластера, которая выполняет сортировку столбцов подматрицы.
#### **Основные моменты**:
1. **Сортировка столбцов**:
- Каждый узел получает подматрицу и сортирует столбцы этой подматрицы.
- Для сортировки используется массив индексов, который определяет порядок столбцов после сортировки.
```java
@Override
public Object execute() {
Integer[] indices = new Integer[cols];
for (int i = 0; i < cols; i++) indices[i] = i;
// Сортируем столбцы по значениям первого ряда
Arrays.sort(indices, Comparator.comparingInt(a -> matrix[0][a]));
int[][] sortedMatrix = new int[rows][cols];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
sortedMatrix[j][i] = matrix[j][indices[i]];
}
}
return sortedMatrix;
}
```
- **`execute`**:
- Столбцы подматрицы сортируются с использованием `Arrays.sort` по первому элементу каждого столбца.
- Индексы столбцов в массиве `indices` определяют новый порядок столбцов после сортировки.
- Возвращается отсортированная матрица.
2. **Отмена задачи**:
- Метод `cancel` вызывается, если задача была прервана, но в данном примере он просто выводит сообщение.
```java
@Override
public void cancel() {
System.out.println("ColumnSortJob cancelled.");
}
```
3. **Конструктор**:
- При создании объекта передаются подматрица и индекс начала столбцов, чтобы корректно обработать каждый сегмент матрицы.
```java
public ColumnSortJob(int[][] matrix, int startCol) {
this.matrix = matrix;
this.startCol = startCol;
}
```
## Как запустить
Для успешной работы потребуется конфигурация ignite.xml. Она должна выглядеть примерно так
```xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<!-- Включаем или отключаем загрузку классов между узлами -->
<property name="peerClassLoadingEnabled" value="true"/>
<property name="discoverySpi">
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
<property name="ipFinder">
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
<!-- Список адресов для обнаружения узлов -->
<property name="addresses">
<list>
<value>IP_CONTAINER1:port</value>
<value>IP_CONTAINER2:port</value>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
```
# Основные свойства:
1. **peerClassLoadingEnabled** — разрешает или запрещает загрузку классов между узлами кластера.
2. **discoverySpi** — настройка компонента для поиска и подключения других узлов.
3. **TcpDiscoverySpi** — механизм для обнаружения узлов по TCP.
4. **ipFinder** — хранит список адресов, используемых для поиска узлов.
5. **TcpDiscoveryVmIpFinder** — класс, который указывает список IP-адресов для поиска узлов.
6. **addresses** — конкретные адреса, которые используются для подключения узлов к кластеру.
Необходимые зависимости
```xml
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version>2.17.0</version>
</dependency>
<!-- Apache Ignite Spring -->
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-spring</artifactId>
<version>2.17.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!-- Плагин для сборки Fat JAR -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>main.java.com.example.MainTask</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
После сборки в папке target (по умолчанию такая) должен появится .jar файл с необходимыми классами
Его уже и нужно будет запустить на компьютере
У меня этот файл называется savinov_roman_lab_4-1.0-SNAPSHOT.jar
На втором контейнере я установил платформу apache ignite и запустил её.
(предварительно нужно изменить default-config.xml)
```bash
cd workingDirectory
java -jar savinov_roman_lab_4-1.0-SNAPSHOT.jar
```
## Выходное значение для матрицы размером 3000 на 3000
Главная нода
```Главная нода.
[14:54:37] Ignite node started OK (id=4c43f1a7)
[14:54:37] Topology snapshot [ver=8, locNode=4c43f1a7, servers=2, clients=0, state=ACTIVE, CPUs=4, offheap=1.5GB, heap=1.9GB]
[14:54:37] ^-- Baseline [id=0, size=2, online=2, offline=0]
Ignite started successfully.
Random matrix generated.
Original Matrix:
18 39 26 96 45 81 87 91 6 19
5 98 93 68 14 16 41 81 6 46
37 48 30 49 72 21 69 24 38 7
95 41 76 83 94 53 66 99 39 47
95 13 70 92 49 45 75 0 58 81
82 43 50 1 66 40 55 66 94 54
92 95 71 72 23 58 89 18 49 75
77 36 96 49 26 20 0 63 97 24
2 23 50 89 14 46 39 14 57 16
31 75 88 94 28 46 44 40 92 74
Mapping jobs to nodes. Total nodes: 2
Sorting columns starting from index 1500
Sorting complete for columns starting at index 1500
Reducing results from jobs. Number of results: 2
Sorted Matrix:
0 0 0 0 0 0 0 0 0 0
2 62 56 61 71 93 18 39 55 49
48 22 46 38 47 69 27 23 37 87
8 54 90 39 29 0 6 54 94 60
61 7 43 39 37 13 15 85 35 79
71 12 19 12 40 50 74 63 54 31
47 20 68 45 43 32 56 59 50 2
36 15 60 12 67 52 88 57 54 8
52 40 54 17 67 80 5 27 45 18
30 23 18 78 98 49 45 83 5 43
Time taken: 1275 ms
[14:54:38] Ignite node stopped OK [uptime=00:00:01.806]
```
Нода работник
```Нода Работник
[14:54:36] ^-- Baseline [id=0, size=2, online=2, offline=0]
Sorting columns starting from index 0
Sorting complete for columns starting at index 0
[14:54:38] Topology snapshot [ver=9, locNode=8e88275c, servers=1, clients=0, state=ACTIVE, CPUs=2, offheap=0.77GB, heap=0.96GB]
[14:54:38] ^-- Baseline [id=0, size=1, online=1, offline=0]
[14:55:00]
[14:55:00] ^-- sysMemPlc region [type=internal, persistence=false, lazyAlloc=false,
[14:55:00] ... initCfg=40MB, maxCfg=100MB, usedRam=0MB, freeRam=99.21%, allocRam=40MB]
[14:55:00] ^-- default region [type=default, persistence=false, lazyAlloc=true,
[14:55:00] ... initCfg=256MB, maxCfg=783MB, usedRam=0MB, freeRam=100%, allocRam=0MB]
[14:55:00] ^-- volatileDsMemPlc region [type=user, persistence=false, lazyAlloc=true,
[14:55:00] ... initCfg=40MB, maxCfg=100MB, usedRam=0MB, freeRam=100%, allocRam=0MB]
[14:55:00]
[14:55:00]
[14:55:00] Data storage metrics for local node (to disable set 'metricsLogFrequency' to 0)
[14:55:00] ^-- Off-heap memory [used=0MB, free=99.92%, allocated=40MB]
[14:55:00] ^-- Page memory [pages=200]
[14:56:00]
[14:56:00] ^-- sysMemPlc region [type=internal, persistence=false, lazyAlloc=false,
[14:56:00] ... initCfg=40MB, maxCfg=100MB, usedRam=0MB, freeRam=99.21%, allocRam=40MB]
[14:56:00] ^-- default region [type=default, persistence=false, lazyAlloc=true,
[14:56:00] ... initCfg=256MB, maxCfg=783MB, usedRam=0MB, freeRam=100%, allocRam=0MB]
[14:56:00] ^-- volatileDsMemPlc region [type=user, persistence=false, lazyAlloc=true,
[14:56:00] ... initCfg=40MB, maxCfg=100MB, usedRam=0MB, freeRam=100%, allocRam=0MB]
[14:56:00]
```
# Вывод
Контейнеры смогли эффективно взаимодействовать между собой благодаря
использованию
механизма
Peer Class Loader в Apache Ignite, который обеспечивал обмен данными и слаженную
работу распределённой системы. Однако эффективность сортировки данных зависела от
качества сети, поскольку передача данных по сети добавляла издержки, что могло замедлить
процесс обработки, особенно при большом количестве узлов.

View File

@@ -0,0 +1,173 @@
import org.apache.ignite.Ignite;
import org.apache.ignite.Ignition;
import org.apache.ignite.compute.ComputeJob;
import org.apache.ignite.compute.ComputeJobResult;
import org.apache.ignite.compute.ComputeTaskAdapter;
import org.apache.ignite.cluster.ClusterNode;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
public class MainTask {
public static void main(String[] args) {
ClassLoader classLoader = MainTask.class.getClassLoader();
URL resource = classLoader.getResource("ignite.xml");
if (resource == null) {
System.out.println("ignite.xml not found in classpath!");
return;
}
try (InputStream igniteConfigStream = classLoader.getResourceAsStream("ignite.xml")) {
if (igniteConfigStream == null) {
System.out.println("Failed to load ignite.xml from resources.");
return;
}
try (Ignite ignite = Ignition.start(igniteConfigStream)) {
System.out.println("Ignite started successfully.");
int rows = 3000;
int cols = 3000;
int[][] matrix = generateRandomMatrix(rows, cols);
System.out.println("Original Matrix:");
printMatrix(matrix);
long startTime = System.nanoTime();
try {
int[][] sortedMatrix = ignite.compute().execute(new MatrixSortingTask(), matrix);
long endTime = System.nanoTime();
System.out.println("Sorted Matrix:");
printMatrix(sortedMatrix);
System.out.println("Time taken: " + (endTime - startTime) / 1_000_000 + " ms");
} catch (Exception e) {
System.out.println("Error during matrix sorting computation: " + e.getMessage());
e.printStackTrace();
}
}
} catch (Exception e) {
System.out.println("Ignite failed to start: " + e.getMessage());
e.printStackTrace();
}
}
public static int[][] generateRandomMatrix(int rows, int cols) {
Random random = new Random();
int[][] matrix = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = random.nextInt(100);
}
}
System.out.println("Random matrix generated.");
return matrix;
}
public static void printMatrix(int[][] matrix) {
for (int i = 0; i < 10 && i < matrix.length; i++) {
for (int j = 0; j < 10 && j < matrix[0].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
private static class MatrixSortingTask extends ComputeTaskAdapter<int[][], int[][]> {
@Override
public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> nodes, int[][] matrix) {
System.out.println("Mapping jobs to nodes. Total nodes: " + nodes.size());
Map<ComputeJob, ClusterNode> map = new HashMap<>();
int cols = matrix[0].length;
int nodeCount = nodes.size();
int chunkSize = (int) Math.ceil((double) cols / nodeCount);
for (int i = 0; i < nodeCount; i++) {
int startCol = i * chunkSize;
int endCol = Math.min(startCol + chunkSize, cols);
if (startCol >= endCol) break;
int[][] subMatrix = extractColumns(matrix, startCol, endCol);
map.put(new ColumnSortJob(subMatrix, startCol), nodes.get(i));
}
return map;
}
@Override
public int[][] reduce(List<ComputeJobResult> results) {
System.out.println("Reducing results from jobs. Number of results: " + results.size());
if (results.isEmpty()) {
return new int[0][0];
}
int[][] firstChunk = (int[][]) results.get(0).getData();
int rows = firstChunk.length;
int cols = results.stream().mapToInt(r -> ((int[][]) r.getData())[0].length).sum();
int[][] sortedMatrix = new int[rows][cols];
int currentCol = 0;
for (ComputeJobResult result : results) {
int[][] sortedChunk = (int[][]) result.getData();
mergeColumns(sortedMatrix, sortedChunk, currentCol);
currentCol += sortedChunk[0].length;
}
return sortedMatrix;
}
private int[][] extractColumns(int[][] matrix, int startCol, int endCol) {
int rows = matrix.length;
int[][] subMatrix = new int[rows][endCol - startCol];
for (int i = 0; i < rows; i++) {
System.arraycopy(matrix[i], startCol, subMatrix[i], 0, endCol - startCol);
}
return subMatrix;
}
private void mergeColumns(int[][] target, int[][] sortedChunk, int startCol) {
for (int i = 0; i < target.length; i++) {
System.arraycopy(sortedChunk[i], 0, target[i], startCol, sortedChunk[i].length);
}
}
}
private static class ColumnSortJob implements ComputeJob {
private final int[][] matrix;
private final int startCol;
public ColumnSortJob(int[][] matrix, int startCol) {
this.matrix = matrix;
this.startCol = startCol;
}
@Override
public Object execute() {
System.out.println("Sorting columns starting from index " + startCol);
int rows = matrix.length;
int cols = matrix[0].length;
Integer[] indices = new Integer[cols];
for (int i = 0; i < cols; i++) indices[i] = i;
Arrays.sort(indices, Comparator.comparingInt(a -> matrix[0][a]));
int[][] sortedMatrix = new int[rows][cols];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
sortedMatrix[j][i] = matrix[j][indices[i]];
}
}
System.out.println("Sorting complete for columns starting at index " + startCol);
return sortedMatrix;
}
@Override
public void cancel() {
System.out.println("ColumnSortJob cancelled.");
}
}
}

View File

@@ -0,0 +1,123 @@
import java.util.Random;
import java.util.concurrent.*;
public class Main {
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(100) + 1;
}
}
return matrix;
}
public static int findMin(int[][] matrix) {
int min = Integer.MAX_VALUE;
for (int[] row : matrix) {
for (int value : row) {
if (value < min) {
min = value;
}
}
}
return min;
}
public static void singleThreadedDivision(int[][] matrix) {
int min = findMin(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= min;
}
}
}
public static void threadPoolDivision(int[][] matrix, int numThreads) {
int min = findMin(matrix);
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < matrix.length; i++) {
final int row = i;
executor.submit(() -> {
for (int j = 0; j < matrix[row].length; j++) {
matrix[row][j] /= min;
}
});
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void forkJoinDivision(int[][] matrix) {
int min = findMin(matrix);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new DivideTask(matrix, min, 0, matrix.length));
}
private static class DivideTask extends RecursiveAction {
private final int[][] matrix;
private final int min;
private final int start;
private final int end;
DivideTask(int[][] matrix, int min, int start, int end) {
this.matrix = matrix;
this.min = min;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= 10) {
for (int i = start; i < end; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= min;
}
}
} else {
int mid = (start + end) / 2;
invokeAll(new DivideTask(matrix, min, start, mid),
new DivideTask(matrix, min, mid, end));
}
}
}
public static void main(String[] args) {
int rows = 1000;
int cols = 1000;
int[][] matrix = generateMatrix(rows, cols);
int[][] singleThreadedMatrix = copyMatrix(matrix);
long startTime = System.currentTimeMillis();
singleThreadedDivision(singleThreadedMatrix);
long endTime = System.currentTimeMillis();
System.out.println("Single thread alghoritm: " + (endTime - startTime) + " ms");
int[][] threadPoolMatrix = copyMatrix(matrix);
startTime = System.currentTimeMillis();
threadPoolDivision(threadPoolMatrix, 4);
endTime = System.currentTimeMillis();
System.out.println("ThreadPoolExecutor: " + (endTime - startTime) + " ms");
int[][] forkJoinMatrix = copyMatrix(matrix);
startTime = System.currentTimeMillis();
forkJoinDivision(forkJoinMatrix);
endTime = System.currentTimeMillis();
System.out.println("ForkJoinPool: " + (endTime - startTime) + " ms");
}
public static int[][] copyMatrix(int[][] matrix) {
int[][] copy = new int[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; i++) {
System.arraycopy(matrix[i], 0, copy[i], 0, matrix[i].length);
}
return copy;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,281 @@
# Лабораторная работа №1
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
### Необходимо:
- Разработать однопоточный вариант алгоритма и замерить время его работы.
- Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы.
- Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
!!!. Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
Вариант задания 2: Разделить элементы матрицы на наименьший элемент.
### Как запустить лабораторную работу:
`javac Main.java` компилирует исходный код
`java Main` команда запускает скомпилированный класс
### Какие технологии использовали:
- Java 17+
- ExecutorService (ThreadPoolExecutor)
- ForkJoinPool
- System.currentTimeMillis() для замеров времени выполнения
### Как работает программа:
Вот описание кода с пояснениями:
---
## Описание программы
---
### 1. Генерация матрицы
```java
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(100) + 1;
}
}
return matrix;
}
```
Метод `generateMatrix` создает двумерную матрицу размером `rows × cols`, заполняя её случайными числами от 1 до 100.
---
### 2. Поиск минимального элемента
```java
public static int findMin(int[][] matrix) {
int min = Integer.MAX_VALUE;
for (int[] row : matrix) {
for (int value : row) {
if (value < min) {
min = value;
}
}
}
return min;
}
```
Метод `findMin` находит минимальный элемент в матрице, который затем будет использоваться для деления элементов.
---
### 3. Однопоточное деление
```java
public static void singleThreadedDivision(int[][] matrix) {
int min = findMin(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= min;
}
}
}
```
В этом методе вся работа выполняется в одном потоке перебираются элементы матрицы и делятся на минимальное значение.
---
### 4. Деление с использованием `ThreadPoolExecutor`
```java
public static void threadPoolDivision(int[][] matrix, int numThreads) {
int min = findMin(matrix);
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < matrix.length; i++) {
final int row = i;
executor.submit(() -> {
for (int j = 0; j < matrix[row].length; j++) {
matrix[row][j] /= min;
}
});
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
```
Метод `threadPoolDivision` использует `ThreadPoolExecutor` с фиксированным числом потоков (`numThreads`). Каждая строка матрицы обрабатывается отдельным потоком.
**Преимущества:**
- Простота использования.
- Хорошо подходит для обработки строк матрицы в параллельном режиме.
**Недостатки:**
- Ограничен количеством потоков в пуле.
- Может быть менее эффективным, чем ForkJoinPool для больших данных.
---
### 5. Деление с использованием `ForkJoinPool`
```java
public static void forkJoinDivision(int[][] matrix) {
int min = findMin(matrix);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new DivideTask(matrix, min, 0, matrix.length));
}
```
Метод `forkJoinDivision` использует `ForkJoinPool`, который автоматически управляет задачами и распределяет их между потоками.
#### Вспомогательный класс `DivideTask`
```java
private static class DivideTask extends RecursiveAction {
private final int[][] matrix;
private final int min;
private final int start;
private final int end;
DivideTask(int[][] matrix, int min, int start, int end) {
this.matrix = matrix;
this.min = min;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= 10) {
for (int i = start; i < end; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= min;
}
}
} else {
int mid = (start + end) / 2;
invokeAll(new DivideTask(matrix, min, start, mid),
new DivideTask(matrix, min, mid, end));
}
}
}
```
**Как работает ForkJoinPool?**
- Разбивает работу на части (рекурсивное разбиение).
- Запускает задачи параллельно.
- Объединяет результаты.
**Преимущества:**
- Автоматически балансирует нагрузку.
- Эффективнее, чем `ThreadPoolExecutor`, при большом количестве данных.
---
### 6. Основной метод `main`
```java
public static void main(String[] args) {
int rows = 1000;
int cols = 1000;
int[][] matrix = generateMatrix(rows, cols);
int[][] singleThreadedMatrix = copyMatrix(matrix);
long startTime = System.currentTimeMillis();
singleThreadedDivision(singleThreadedMatrix);
long endTime = System.currentTimeMillis();
System.out.println("Single thread algorithm: " + (endTime - startTime) + " ms");
int[][] threadPoolMatrix = copyMatrix(matrix);
startTime = System.currentTimeMillis();
threadPoolDivision(threadPoolMatrix, 4);
endTime = System.currentTimeMillis();
System.out.println("ThreadPoolExecutor: " + (endTime - startTime) + " ms");
int[][] forkJoinMatrix = copyMatrix(matrix);
startTime = System.currentTimeMillis();
forkJoinDivision(forkJoinMatrix);
endTime = System.currentTimeMillis();
System.out.println("ForkJoinPool: " + (endTime - startTime) + " ms");
}
```
В `main`:
1. Создается матрица 1000×1000.
2. Копируется три раза для разных реализаций.
3. Запускается однопоточное вычисление и измеряется время выполнения.
4. Запускается версия с `ThreadPoolExecutor`.
5. Запускается версия с `ForkJoinPool`.
6. Результаты выводятся в консоль.
#### Вспомогательный метод `copyMatrix`
```java
public static int[][] copyMatrix(int[][] matrix) {
int[][] copy = new int[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; i++) {
System.arraycopy(matrix[i], 0, copy[i], 0, matrix[i].length);
}
return copy;
}
```
Этот метод делает копию матрицы перед запуском каждого алгоритма, чтобы тестирование было честным.
### (Размер матрицы 1000х1000)
```bash
Single thread alghoritm: 20 ms
ThreadPoolExecutor: 73 ms
ForkJoinPool: 28 ms
```
### (Размер матрицы 2000х2000)
```bash
Single thread alghoritm: 39 ms
ThreadPoolExecutor: 110 ms
ForkJoinPool: 51 ms.
```
### (Размер матрицы 3000x3000)
```bash
Single thread alghoritm: 69 ms
ThreadPoolExecutor: 182 ms
ForkJoinPool: 106 ms
```
### (Размер матрицы 4000x4000)
```bash
Single thread alghoritm: 108 ms
ThreadPoolExecutor: 237 ms
ForkJoinPool: 134 ms
```
<img src="./diagram.png">
### Анализ результатов
1. Однопоточный алгоритм показывает линейный рост времени выполнения с увеличением размера матрицы. Это ожидаемо, так как вычисления выполняются последовательно.
2. `ThreadPoolExecutor` демонстрирует худшие результаты по сравнению с остальными методами. Это связано с накладными расходами на управление потоками и передачу задач в пул потоков. Он эффективен при большом количестве независимых задач, но в данном случае не даёт прироста.
3. `ForkJoinPool` показывает лучшую производительность среди параллельных методов. Это объясняется тем, что `ForkJoinPool` оптимизирован для рекурсивного разбиения задач и эффективного использования ресурсов CPU.
4. При увеличении размера матрицы разница между `ForkJoinPool` и ThreadPoolExecutor становится более заметной, так как `ForkJoinPool` минимизирует накладные расходы на распределение задач.
### Вывод
Использование `ForkJoinPool` даёт наилучший результат среди многопоточных методов в данном алгоритме. `ThreadPoolExecutor` не оправдывает себя из-за накладных расходов на управление потоками. Однопоточный метод показывает неплохие результаты на небольших данных, но проигрывает `ForkJoinPool` при увеличении объёма вычислений.

View File

@@ -0,0 +1,99 @@
import mpi.*;
public class MPIMatrix {
public static void main(String[] args) {
MPI.Init(args);
int rank = MPI.COMM_WORLD.Rank();
int size = MPI.COMM_WORLD.Size();
if (size != 2) {
if (rank == 0) {
System.out.println("This program requires 2 processes.");
}
MPI.Finalize();
return;
}
int n = 1000;
double[][] matrix = null;
double[] localMaxArr = new double[1];
double[] globalMaxArr = new double[1];
double startTime = System.nanoTime();
int rowsPerProcess = n / 2;
double[][] localMatrix = new double[rowsPerProcess][n];
if (rank == 0) {
matrix = new double[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = Math.random() * 100;
}
}
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(matrix[i], 0, localMatrix[i], 0, n);
}
double[] sendBuffer = new double[rowsPerProcess * n];
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(matrix[i + rowsPerProcess], 0, sendBuffer, i * n, n);
}
MPI.COMM_WORLD.Send(sendBuffer, 0, sendBuffer.length, MPI.DOUBLE, 1, 0);
} else {
double[] recvBuffer = new double[rowsPerProcess * n];
MPI.COMM_WORLD.Recv(recvBuffer, 0, recvBuffer.length, MPI.DOUBLE, 0, 0);
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(recvBuffer, i * n, localMatrix[i], 0, n);
}
}
double localMax = 0;
for (int i = 0; i < rowsPerProcess; i++) {
for (int j = 0; j < n; j++) {
if (localMatrix[i][j] > localMax) {
localMax = localMatrix[i][j];
}
}
}
localMaxArr[0] = localMax;
MPI.COMM_WORLD.Reduce(localMaxArr, 0, globalMaxArr, 0, 1, MPI.DOUBLE, MPI.MAX, 0);
double globalMax = globalMaxArr[0];
double[] globalMaxBroadcast = new double[1];
if (rank == 0) {
globalMaxBroadcast[0] = globalMax;
}
MPI.COMM_WORLD.Bcast(globalMaxBroadcast, 0, 1, MPI.DOUBLE, 0);
globalMax = globalMaxBroadcast[0];
for (int i = 0; i < rowsPerProcess; i++) {
for (int j = 0; j < n; j++) {
localMatrix[i][j] /= globalMax;
}
}
if (rank == 0) {
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(localMatrix[i], 0, matrix[i], 0, n);
}
double[] recvBuffer = new double[rowsPerProcess * n];
MPI.COMM_WORLD.Recv(recvBuffer, 0, recvBuffer.length, MPI.DOUBLE, 1, 1);
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(recvBuffer, i * n, matrix[i + rowsPerProcess], 0, n);
}
double endTime = System.nanoTime();
System.out.printf("Process %d finished at %.1f ms\n", rank, (endTime - startTime) / 1_000_000.0);
} else {
double[] sendBuffer = new double[rowsPerProcess * n];
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(localMatrix[i], 0, sendBuffer, i * n, n);
}
MPI.COMM_WORLD.Send(sendBuffer, 0, sendBuffer.length, MPI.DOUBLE, 0, 1);
double endTime = System.nanoTime();
System.out.printf("Process %d finished at %.1f ms\n", rank, (endTime - startTime) / 1_000_000.0);
}
MPI.Finalize();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,2 @@
CT128
CT129

View File

@@ -0,0 +1,260 @@
# Лабораторная работа №2
Разработка параллельного MPI приложения на языке Java.
### Необходимо:
- Разработать параллельный вариант алгоритма с применением MPI и
замерить время его работы.
<i>В рамках работы программы должно быть две копии приложения, которые соединяются друг с другом по сети. Сообщения о статусе соединения (например, что соединение установлено) должны выводиться в консоль.</i>
<strong>Вариант задания 1: Разделить элементы матрицы на наибольший элемент.</strong>
### Как запустить лабораторную работу:
`javac -cp $MPJ_HOME/lib/mpj.jar MPIMatrix.java` компилирует исходный код (делаем на обоих контейнерах)
`mpjrun.sh -np 2 -dev niodev -machinesfile machines MPIMatrix` команда запускает скомпилированный класс (делаем на контейнере CT128)
### Какие технологии использовали:
- Java - язык программирования.
- MPJ Express - библиотека для реализации параллельных вычислений.
- MPI - интерфейс для передачи сообщений между процессами.
### Как работает программа:
### **Разбор кода с фрагментами**
#### **1. Инициализация MPI**
```java
MPI.Init(args);
int rank = MPI.COMM_WORLD.Rank();
int size = MPI.COMM_WORLD.Size();
```
- Запускается среда MPI.
- Получается **ранг процесса** (`rank`) и общее число процессов (`size`).
#### **2. Проверка количества процессов**
```java
if (size != 2) {
if (rank == 0) {
System.out.println("This program requires 2 processes.");
}
MPI.Finalize();
return;
}
```
- Программа работает **только с двумя процессами**.
- Если процессов больше или меньше, программа завершает выполнение.
#### **3. Создание матрицы (только для `rank == 0`)**
```java
int n = 1000;
double[][] matrix = null;
if (rank == 0) {
matrix = new double[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = Math.random() * 100;
}
}
}
```
- Создается **матрица 1000×1000** и заполняется случайными числами от 0 до 100.
#### **4. Разделение матрицы между процессами**
```java
int rowsPerProcess = n / 2;
double[][] localMatrix = new double[rowsPerProcess][n];
if (rank == 0) {
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(matrix[i], 0, localMatrix[i], 0, n);
}
double[] sendBuffer = new double[rowsPerProcess * n];
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(matrix[i + rowsPerProcess], 0, sendBuffer, i * n, n);
}
MPI.COMM_WORLD.Send(sendBuffer, 0, sendBuffer.length, MPI.DOUBLE, 1, 0);
} else {
double[] recvBuffer = new double[rowsPerProcess * n];
MPI.COMM_WORLD.Recv(recvBuffer, 0, recvBuffer.length, MPI.DOUBLE, 0, 0);
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(recvBuffer, i * n, localMatrix[i], 0, n);
}
}
```
- **Процесс 0** отправляет вторую половину матрицы **процессу 1** с помощью `MPI.COMM_WORLD.Send()`.
- **Процесс 1** принимает данные через `MPI.COMM_WORLD.Recv()`.
#### **5. Поиск локального максимума**
```java
double localMax = 0;
for (int i = 0; i < rowsPerProcess; i++) {
for (int j = 0; j < n; j++) {
if (localMatrix[i][j] > localMax) {
localMax = localMatrix[i][j];
}
}
}
double[] localMaxArr = {localMax};
```
- Каждый процесс находит **максимальное значение** в своей части матрицы.
#### **6. Нахождение глобального максимума**
```java
double[] globalMaxArr = new double[1];
MPI.COMM_WORLD.Reduce(localMaxArr, 0, globalMaxArr, 0, 1, MPI.DOUBLE, MPI.MAX, 0);
double globalMax = globalMaxArr[0];
```
- `MPI.Reduce()` передает **максимальный элемент** от каждого процесса **процессу 0**, который вычисляет **глобальный максимум**.
#### **7. Рассылка глобального максимума**
```java
double[] globalMaxBroadcast = new double[1];
if (rank == 0) {
globalMaxBroadcast[0] = globalMax;
}
MPI.COMM_WORLD.Bcast(globalMaxBroadcast, 0, 1, MPI.DOUBLE, 0);
globalMax = globalMaxBroadcast[0];
```
- **Процесс 0** рассылает глобальный максимум **всем процессам** с помощью `MPI.Bcast()`.
#### **8. Нормализация матрицы**
```java
for (int i = 0; i < rowsPerProcess; i++) {
for (int j = 0; j < n; j++) {
localMatrix[i][j] /= globalMax;
}
}
```
- Каждый элемент **делится на глобальный максимум**, нормализуя значения.
#### **9. Сбор нормализованных данных**
```java
if (rank == 0) {
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(localMatrix[i], 0, matrix[i], 0, n);
}
double[] recvBuffer = new double[rowsPerProcess * n];
MPI.COMM_WORLD.Recv(recvBuffer, 0, recvBuffer.length, MPI.DOUBLE, 1, 1);
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(recvBuffer, i * n, matrix[i + rowsPerProcess], 0, n);
}
} else {
double[] sendBuffer = new double[rowsPerProcess * n];
for (int i = 0; i < rowsPerProcess; i++) {
System.arraycopy(localMatrix[i], 0, sendBuffer, i * n, n);
}
MPI.COMM_WORLD.Send(sendBuffer, 0, sendBuffer.length, MPI.DOUBLE, 0, 1);
}
```
- **Процесс 1** отправляет нормализованные данные **процессу 0**, который собирает матрицу обратно.
#### **10. Вывод времени выполнения**
```java
double endTime = System.nanoTime();
System.out.printf("Process %d finished at %.1f ms\n", rank, (endTime - startTime) / 1_000_000.0);
```
- Каждый процесс выводит **время работы** в миллисекундах.
#### **11. Завершение работы MPI**
```java
MPI.Finalize();
```
- Освобождение ресурсов и завершение работы MPI.
### (Размер матрицы 1000х1000)
```bash
Starting process <0> on <CT128>
Starting process <1> on <CT129>
Maximum: 99.99984736376186
Process 1 finished at 170.9 ms
Process 0 finished at 179.2 ms
Stopping Process <0> on <CT128>
Stopping Process <1> on <CT129>
```
### (Размер матрицы 2000х2000)
```bash
Starting process <1> on <CT129>
Starting process <0> on <CT128>
Maximum: 99.99998909285488
Process 1 finished at 347.8 ms
Process 0 finished at 359.3 ms
Stopping Process <0> on <CT128>
Stopping Process <1> on <CT129>
```
### (Размер матрицы 3000x3000)
```bash
Starting process <0> on <CT128>
Starting process <1> on <CT129>
Maximum: 99.99999045962504
Process 1 finished at 753.4 ms
Process 0 finished at 762.8 ms
Stopping Process <0> on <CT128>
Stopping Process <1> on <CT129>
```
### (Размер матрицы 4000x4000)
```bash
Starting process <1> on <CT129>
Starting process <0> on <CT128>
Maximum: 99.99998218458676
Process 1 finished at 1376.1 ms
Process 0 finished at 1398.0 ms
Stopping Process <0> on <CT128>
Stopping Process <1> on <CT129>
```
<img src="./diagram.png">
### Выводы по временным замерам:
- Время выполнения растет примерно квадратично с увеличением размера матрицы.
- Разница во времени между процессами 0 и 1 минимальна, что говорит о равномерном распределении нагрузки.
- Задержка в работе процесса 0 связана с дополнительными затратами на генерацию матрицы и сбор результатов.
- MPI позволяет эффективно разделить вычисления, но обмен данными (Send/Recv) вносит небольшие накладные расходы.
### **Сравнение времени выполнения MPI и многопоточных алгоритмов**
Для матрицы 1000x1000:
- MPI: **179.2 мс**
- Однопоточный алгоритм: **20 мс**
- ThreadPoolExecutor: **73 мс**
- ForkJoinPool: **28 мс**
Для матрицы 2000x2000:
- MPI: **359.3 мс**
- Однопоточный алгоритм: **39 мс**
- ThreadPoolExecutor: **110 мс**
- ForkJoinPool: **51 мс**
Для матрицы 3000x3000:
- MPI: **762.8 мс**
- Однопоточный алгоритм: **69 мс**
- ThreadPoolExecutor: **182 мс**
- ForkJoinPool: **106 мс**
Для матрицы 4000x4000:
- MPI: **1398.0 мс**
- Однопоточный алгоритм: **108 мс**
- ThreadPoolExecutor: **237 мс**
- ForkJoinPool: **134 мс**
MPI работает **значительно медленнее**, чем даже однопоточное исполнение, а многопоточные решения (особенно ForkJoinPool) выполняют задачу в **2-6 раз быстрее**. Это связано с издержками межпроцессного взаимодействия через сеть. **MPI неэффективен для данной задачи**, лучше использовать многопоточные алгоритмы внутри одного узла.
### Вывод:
MPI позволяет значительно ускорить обработку больших массивов данных чем вариации с использованием однопоточных и многопоточных реализаций из 1й лабораторной работы, особенно при распределении вычислений между несколькими узлами. Однако эффективность зависит от баланса вычислений и передачи данных. В данной работе удалось добиться почти линейного ускорения по сравнению с последовательным исполнением. MPI превосходит как однопоточное исполнение, так и многопоточные подходы (ThreadPoolExecutor, ForkJoinPool) в задачах, требующих интенсивных вычислений на больших объемах данных. В многопоточных реализациях ускорение ограничено мощностью одного процессора, тогда как в MPI оно зависит от количества узлов, что позволяет добиться более высокой производительности. Однако MPI требует сложной настройки распределенной системы, тогда как ThreadPoolExecutor и ForkJoinPool проще в интеграции и подходят для локального многопоточного исполнения.

View File

@@ -0,0 +1,11 @@
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,34 @@
package com.example.matrixservice;
import org.springframework.web.bind.annotation.*;
import java.util.Random;
@RestController
@RequestMapping("/matrix")
public class MatrixProcessorController {
@PostMapping("/process")
public double[][] processMatrix(@RequestBody double[][] matrix) {
System.out.println("Get half matrix for processing");
double maxElement = findMax(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= maxElement;
}
}
System.out.println("Matrix successful proccessed");
return matrix;
}
private double findMax(double[][] matrix) {
double max = Double.MIN_VALUE;
for (double[] row : matrix) {
for (double value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
}

View File

@@ -0,0 +1,11 @@
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,87 @@
package com.example.matrixservice;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.Random;
@RestController
@RequestMapping("/matrix")
public class MatrixProcessorController {
private final String processHalfUrl = "http://192.168.28.128:8080/matrix/process";
@GetMapping("/process")
public String processMatrix(@RequestParam int size) {
RestTemplate restTemplate = new RestTemplate();
System.out.println("Program started...");
// 1. Генерируем матрицу
System.out.println("Generating matrix...");
Random rand = new Random();
double[][] matrix = new double[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = rand.nextDouble() * 100;
}
}
double maxElement = Arrays.stream(matrix)
.flatMapToDouble(Arrays::stream)
.max()
.orElse(1.0);
System.out.println("Matrix are generated! Size: " + size);
long startTime = System.currentTimeMillis();
System.out.println("Start processing...");
// 2. Делим матрицу на две части
double[][] firstHalf = new double[size / 2][size];
double[][] secondHalf = new double[size - size / 2][size];
System.arraycopy(matrix, 0, firstHalf, 0, size / 2);
System.arraycopy(matrix, size / 2, secondHalf, 0, size - size / 2);
// 3. Запускаем обработку локально и на первом сервисе параллельно
CompletableFuture<double[][]> processedFirstHalfFuture = CompletableFuture.supplyAsync(() -> processMatrixLocally(firstHalf));
CompletableFuture<double[][]> processedSecondHalfFuture = CompletableFuture.supplyAsync(() -> restTemplate.postForObject(processHalfUrl, secondHalf, double[][].class));
// 4. Ждем завершения обеих задач
CompletableFuture.allOf(processedFirstHalfFuture, processedSecondHalfFuture).join();
// 5. Получаем обработанные результаты
double[][] processedFirstHalf = processedFirstHalfFuture.join();
double[][] processedSecondHalf = processedSecondHalfFuture.join();
// 6. Объединяем результаты
double[][] resultMatrix = new double[size][size];
System.arraycopy(processedFirstHalf, 0, resultMatrix, 0, size / 2);
System.arraycopy(processedSecondHalf, 0, resultMatrix, size / 2, size - size / 2);
long endTime = System.currentTimeMillis();
System.out.println("Matrix processed success: " + (endTime - startTime) + "ms \n");
return "Matrix processed success: " + (endTime - startTime) + "ms \n";
}
private double[][] processMatrixLocally(double[][] matrix) {
double maxElement = findMax(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= maxElement;
}
}
return matrix;
}
private double findMax(double[][] matrix) {
double max = Double.MIN_VALUE;
for (double[] row : matrix) {
for (double value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
}

View File

@@ -0,0 +1,197 @@
# Лабораторная работа №3
Разработка распределенного приложения с использованием фреймворка Spring Boot.
### Необходимо:
- Разработать параллельный вариант алгоритма с применением сервис-ориентированного подхода и фреймворка Spring Boot, замерить время его работы.
**Вариант задания 1: Разделить элементы матрицы на наибольший элемент.**
### Как запустить лабораторную работу:
```sh
mvn spring-boot:run
```
Запускаем сервисы (делаем на обоих контейнерах).
```sh
curl http://192.168.28.129:8080/matrix/process?size=1000
```
Команда отправляет запрос на генерацию и обработку матрицы размером 1000x1000 (я запускал с виртуальной машины).
### Какие технологии использовали:
- **Spring Boot** — для создания REST API сервиса.
- **RestTemplate** — для отправки HTTP-запросов между сервисами.
- **CompletableFuture** — для асинхронного выполнения задач.
- **Docker + Proxmox** — для контейнеризации и тестирования распределенной системы.
### Как работает программа:
Вот описание работы кода с разбором фрагментов:
---
### **Общий обзор**
Код реализует распределенную обработку матрицы в **два контейнера**.
Один контейнер делит матрицу на две части:
- **Первую часть** он обрабатывает локально.
- **Вторую часть** отправляет другому контейнеру по HTTP-запросу.
После обработки обе половины объединяются в одну итоговую матрицу.
---
## **Контейнер 1 (Основной сервис)**
Файл: **`MatrixProcessorController`**
Этот сервис принимает запрос от клиента на обработку матрицы.
### **1. Генерация матрицы**
```java
System.out.println("Generating matrix...");
Random rand = new Random();
double[][] matrix = new double[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = rand.nextDouble() * 100;
}
}
```
Создается матрица **размера size x size**, заполняется случайными числами от 0 до 100.
---
### **2. Разбиение матрицы**
```java
double[][] firstHalf = new double[size / 2][size];
double[][] secondHalf = new double[size - size / 2][size];
System.arraycopy(matrix, 0, firstHalf, 0, size / 2);
System.arraycopy(matrix, size / 2, secondHalf, 0, size - size / 2);
```
Матрица делится на **две половины**:
- `firstHalf` — обрабатывается локально.
- `secondHalf` — отправляется во второй контейнер.
---
### **3. Обработка двух частей параллельно**
```java
CompletableFuture<double[][]> processedFirstHalfFuture = CompletableFuture.supplyAsync(() -> processMatrixLocally(firstHalf));
CompletableFuture<double[][]> processedSecondHalfFuture = CompletableFuture.supplyAsync(() -> restTemplate.postForObject(processHalfUrl, secondHalf, double[][].class));
```
- **Локальная обработка** запускается в асинхронном потоке.
- **Вторая половина** отправляется во **второй контейнер** по `POST`-запросу.
Контейнер 2 обрабатывает её и возвращает результат.
---
### **4. Ожидание завершения и объединение**
```java
CompletableFuture.allOf(processedFirstHalfFuture, processedSecondHalfFuture).join();
double[][] processedFirstHalf = processedFirstHalfFuture.join();
double[][] processedSecondHalf = processedSecondHalfFuture.join();
double[][] resultMatrix = new double[size][size];
System.arraycopy(processedFirstHalf, 0, resultMatrix, 0, size / 2);
System.arraycopy(processedSecondHalf, 0, resultMatrix, size / 2, size - size / 2);
```
Обе части матрицы собираются обратно.
---
## **Контейнер 2 (Вспомогательный сервис)**
Файл: **`MatrixProcessorController`** (в другом контейнере)
Этот сервис принимает вторую половину матрицы и **выполняет ту же обработку**, что и первый контейнер, но отдельно.
### **1. Прием и обработка**
```java
@PostMapping("/process")
public double[][] processMatrix(@RequestBody double[][] matrix) {
System.out.println("Get half matrix for processing");
double maxElement = findMax(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= maxElement;
}
}
System.out.println("Matrix successful proccessed");
return matrix;
}
```
- Получает массив через **POST-запрос**.
- Находит **максимальный элемент**.
- **Делит все элементы** на этот максимум.
- Отправляет **обратно в контейнер 1**.
### (Размер матрицы 1000х1000)
#### CT129 (На который поступал запрос от клиента)
```
Program started...
Generating matrix...
Matrix are generated! Size: 1000
Start processing...
Matrix processed success: 779ms
```
#### CT128 (Который обрабатывал половину матрицы)
```
Get half matrix for processing
Half matrix processed success: 2ms
```
### (Размер матрицы 2000х2000)
#### CT129 (На который поступал запрос от клиента)
```
Program started...
Generating matrix...
Matrix are generated! Size: 2000
Start processing...
Matrix processed success: 2966ms
```
#### CT128 (Который обрабатывал половину матрицы)
```
Get half matrix for processing
Half matrix processed success: 5ms
```
### Почему так происходит?
1. **Рост времени обработки при увеличении матрицы**:
- Время обработки на сервере CT129 растет быстрее, чем линейно, что может быть связано с увеличением накладных расходов на передачу данных и синхронизацию потоков.
- Время обработки на CT128 остается минимальным, так как он получает уже готовую половину матрицы и выполняет лишь нормализацию значений.
2. **Влияние сети**:
- Передача больших массивов данных через HTTP вызывает дополнительные задержки, что сказывается на общей производительности.
3. **Особенности Spring Boot**:
- Использование `RestTemplate` не является самым эффективным способом передачи данных. Возможен переход на WebFlux для обработки потоков данных без блокировки.
- `CompletableFuture` помогает параллелизировать задачи, но накладные расходы на переключение потоков влияют на общую скорость выполнения.
## Когда стоит использовать сервисный подход?
Использование сервисного подхода (Spring Boot) оправдано, если:
- Требуется **масштабируемость** (можно распределять обработку на несколько узлов).
- Важна **гибкость** (легко добавлять новые сервисы без изменения архитектуры).
- Обрабатываются **разнородные задачи** (например, обработка изображений, финансовые вычисления и пр.).
- Не критичны **задержки** при передаче данных по сети.
Если же задача требует высокой производительности и минимальных накладных расходов, лучше рассмотреть MPI или ForkJoin.
## Вывод
Spring Boot отлично подходит для разработки распределенных систем, но для вычислительно сложных задач, требующих минимальных задержек, стоит рассмотреть альтернативные парадигмы, такие как MPI (Spring Boot и обработка матрицы 1000x1000 составляет около 779 мс, а для 2000x2000 — 2966 мс. В отличие от этого, MPI разделяет матрицу на части и обрабатывает их параллельно на нескольких узлах, например, CT128 и CT129, где обработка половины матрицы происходит за 2 мс для 1000x1000 и 5 мс для 2000x2000, с общим временем завершения процесса для каждого узла в пределах 170-180 мс для 1000x1000 и 347-359 мс для 2000x2000) или ForkJoin (Для матрицы 1000x1000: ForkJoinPool 28 мс, Spring Boot 779 мс.
Для матрицы 2000x2000: ForkJoinPool 51 мс, Spring Boot 2966 мс.). При увеличении размера данных время обработки растет нелинейно из-за сетевых и потоковых накладных расходов. Оптимизация возможна за счет использования асинхронных технологий, таких как WebFlux, или перехода на другой подход к распределению вычислений.

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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

View 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;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More