merge upstream

This commit is contained in:
zum
2025-03-15 21:14:45 +04:00
77 changed files with 7680 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
import java.math.BigInteger;
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
public class MatrixSumOfProducts {
// Генерация матрицы
public static int[][] generateMatrix(int rows, int cols, int minValue, int maxValue) {
int[][] matrix = new int[rows][cols];
Random random = new Random();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = minValue + random.nextInt(maxValue - minValue + 1);
}
}
return matrix;
}
// Однопоточный алгоритм
public static BigInteger singleThreadedSum(int[][] matrix) {
BigInteger totalSum = BigInteger.ZERO;
for (int[] row : matrix) {
BigInteger rowProduct = BigInteger.ONE;
for (int value : row) {
rowProduct = rowProduct.multiply(BigInteger.valueOf(value));
}
totalSum = totalSum.add(rowProduct);
}
return totalSum;
}
// Параллельный алгоритм с ThreadPoolExecutor
public static BigInteger multiThreadedSumWithThreadPool(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
AtomicReference<BigInteger> totalSum = new AtomicReference<>(BigInteger.ZERO);
int rowsPerThread = matrix.length / numThreads;
for (int i = 0; i < numThreads; i++) {
int startRow = i * rowsPerThread;
int endRow = (i == numThreads - 1) ? matrix.length : startRow + rowsPerThread;
executor.submit(() -> {
BigInteger localSum = BigInteger.ZERO;
for (int row = startRow; row < endRow; row++) {
BigInteger rowProduct = BigInteger.ONE;
for (int value : matrix[row]) {
rowProduct = rowProduct.multiply(BigInteger.valueOf(value));
}
localSum = localSum.add(rowProduct);
}
// Используем локальную копию переменной localSum
BigInteger finalLocalSum = localSum;
totalSum.updateAndGet(current -> current.add(finalLocalSum));
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
return totalSum.get();
}
// Параллельный алгоритм с ForkJoinPool
public static BigInteger multiThreadedSumWithForkJoin(int[][] matrix) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
return forkJoinPool.invoke(new MatrixTask(matrix, 0, matrix.length));
}
private static class MatrixTask extends RecursiveTask<BigInteger> {
private final int[][] matrix;
private final int startRow;
private final int endRow;
private static final int THRESHOLD = 100; // Порог для разделения задачи
public MatrixTask(int[][] matrix, int startRow, int endRow) {
this.matrix = matrix;
this.startRow = startRow;
this.endRow = endRow;
}
@Override
protected BigInteger compute() {
if (endRow - startRow <= THRESHOLD) {
BigInteger localSum = BigInteger.ZERO;
for (int row = startRow; row < endRow; row++) {
BigInteger rowProduct = BigInteger.ONE;
for (int value : matrix[row]) {
rowProduct = rowProduct.multiply(BigInteger.valueOf(value));
}
localSum = localSum.add(rowProduct);
}
return localSum;
} else {
int mid = (startRow + endRow) / 2;
MatrixTask leftTask = new MatrixTask(matrix, startRow, mid);
MatrixTask rightTask = new MatrixTask(matrix, mid, endRow);
leftTask.fork();
BigInteger rightResult = rightTask.compute();
BigInteger leftResult = leftTask.join();
return leftResult.add(rightResult);
}
}
}
// Основной метод
public static void main(String[] args) throws InterruptedException, ExecutionException {
int rows = 100;
int cols = 100;
int minValue = 1;
int maxValue = 100;
int numThreads = Runtime.getRuntime().availableProcessors(); // Используем все доступные ядра
// Генерация матрицы
int[][] matrix = generateMatrix(rows, cols, minValue, maxValue);
// Однопоточный алгоритм
long startTime = System.nanoTime();
BigInteger singleThreadedResult = singleThreadedSum(matrix);
long endTime = System.nanoTime();
System.out.println("Single-threaded result: " + singleThreadedResult);
System.out.println("Execution time (single-threaded): " + (endTime - startTime) / 1_000_000 + " ms");
// Параллельный алгоритм с ThreadPoolExecutor
startTime = System.nanoTime();
BigInteger multiThreadedResultThreadPool = multiThreadedSumWithThreadPool(matrix, numThreads);
endTime = System.nanoTime();
System.out.println("Multi-threaded result (ThreadPoolExecutor): " + multiThreadedResultThreadPool);
System.out.println("Execution time (ThreadPoolExecutor): " + (endTime - startTime) / 1_000_000 + " ms");
// Параллельный алгоритм с ForkJoinPool
startTime = System.nanoTime();
BigInteger multiThreadedResultForkJoin = multiThreadedSumWithForkJoin(matrix);
endTime = System.nanoTime();
System.out.println("Multi-threaded result (ForkJoinPool): " + multiThreadedResultForkJoin);
System.out.println("Execution time (ForkJoinPool): " + (endTime - startTime) / 1_000_000 + " ms");
}
}

View File

@@ -0,0 +1,62 @@
## Лабораторная работа №1.
Разработка многопоточного приложения с использованием Java Concurrency
согласно варианту задания.
Необходимо:
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
2. Разработать параллельный вариант алгоритма с использованием
ThreadPoolExecutor и замерить время его работы
3. Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
и замерить время его работы.
ВАЖНО: Массив генерируется до работы всех вариантов алгоритмов. Все три
алгоритма обрабатывают три одинаковых массива.
**Вариант 13**
Определить сумму из произведений элементов каждой строки матрицы.
**Запуск программы**
javac MatrixSumOfProducts.java
java MatrixSumOfProducts
**Используемые технологии**
*java.util.concurrent — библиотека для разработки многопоточных программ.
*BigInteger — для работы с большими числами, чтобы избежать переполнения при умножении.
**Что делает программа**
1. Генерирует матрицу заданного размера.
2. Вычисляет сумму произведений элементов каждой строки матрицы:
2.1 Однопоточным алгоритмом.
2.2 Параллельным алгоритмом с использованием ThreadPoolExecutor.
2.3 Параллельным алгоритмом с использованием ForkJoinPool.
3. Замеряет время выполнения каждого из алгоритмов.
**Тесты**
```
Размерность матрицы 100x100
Single-threaded time: 17 ms
ThreadPoolExecutor time: 73 ms
ForkJoinPool time: 36 ms
```
```
Размерность матрицы 1000x1000
Single-threaded time: 1639 ms
ThreadPoolExecutor time: 1440 ms
ForkJoinPool time: 464 ms
```
### Вывод
Для небольших задач реализация многопоточности может приводить к более худшему результату, так как ее реализация требует дополнительных затрат на создание и управление потоками. Однако при увеличении размера задачи многопоточные алгоритмы начинают показывать свою эффективность за счет параллельной обработки данных.
**Наблюдения:**
1. **Однопоточный алгоритм** работает быстрее на небольших матрицах (например, 100x100), так как отсутствуют накладные расходы на создание и синхронизацию потоков.
2. **ThreadPoolExecutor** показывает хорошие результаты на больших матрицах (например, 1000x1000), но его производительность может быть ниже, чем у `ForkJoinPool`, из-за менее эффективного распределения задач между потоками.
3. **ForkJoinPool** демонстрирует наилучшую производительность на больших задачах, так как он оптимизирован для рекурсивного разделения задач и эффективного использования ресурсов процессора.

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

View File

@@ -0,0 +1,249 @@
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class DividingByAverage {
public static void main(String[] args) {
Random random = new Random();
// Создание массива
double[][] array = new double[8000][8000];
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
array[i][j] = random.nextInt(101);
}
}
// Создание копии исходного массива
double[][] arraySave = new double[array.length][array.length];
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
arraySave[i][j] = array[i][j];
}
}
// Тест однопоточного алгоритма
System.out.println("Однопоточный алгоритм:");
long startTime1 = System.nanoTime();
singleThreadedAlgorithm(array);
long endTime1 = System.nanoTime();
long duration1 = endTime1 - startTime1;
System.out.println("Время выполнения: " + (duration1 / 1000000) + " мс");
System.out.println(" ");
// Восстановление массива
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
array[i][j] = arraySave[i][j];
}
}
// Тест многопоточного алгоритма с ThreadPoolExecutor
System.out.println("Алгоритм с ThreadPoolExecutor:");
long startTime2 = System.nanoTime();
threadPoolExecutorAlgorithm(array);
long endTime2 = System.nanoTime();
long duration2 = endTime2 - startTime2;
System.out.println("Время выполнения: " + (duration2 / 1000000) + " мс");
System.out.println(" ");
// Восстановление массива
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
array[i][j] = arraySave[i][j];
}
}
// Тест многопоточного алгоритма с ForkJoinPool
System.out.println("Алгоритм с ForkJoinPool:");
long startTime3 = System.nanoTime();
forkJoinPoolAlgorithm(array);
long endTime3 = System.nanoTime();
long duration3 = endTime3 - startTime3;
System.out.println("Время выполнения: " + (duration3 / 1000000) + " мс");
System.out.println(" ");
// Восстановление массива
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
array[i][j] = arraySave[i][j];
}
}
}
// Однопоточный алгоритм
public static void singleThreadedAlgorithm(double[][] array) {
// Сумма всех элементов массива
double sum = 0;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
sum += array[i][j];
}
}
// Среднее арифметическое
double average = (double) sum / (array.length * array.length);
System.out.println("array[100][100] = " + array[100][100]);
System.out.println("Среднее арифметическое: " + average);
// Деление элементов массива на среднее
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
array[i][j] = array[i][j] / average;
}
}
System.out.println("array[100][100] = " + array[100][100]);
}
// Тест многопоточного алгоритма с ThreadPoolExecutor
public static void threadPoolExecutorAlgorithm(double[][] array) {
int size = array.length;
// Количество потоков
int threads = 4;
// Создаем пул потоков
ExecutorService executor = Executors.newFixedThreadPool(threads);
// Используем AtomicLong для безопасного накопления суммы в потоках
AtomicLong totalSum = new AtomicLong(0);
// Считаем сумму элементов массива с использованием потоков
for (int t = 0; t < threads; t++) {
// Начало потока
final int startRow = t * (size / threads);
// Конец потока
final int endRow = (t == threads - 1) ? size : (t + 1) * (size / threads);
executor.submit(() -> {
// Считаем сумму в пределах выделенного потоку диапазона строк
double localSum = 0;
for (int i = startRow; i < endRow; i++) {
for (int j = 0; j < size; j++) {
localSum += array[i][j];
}
}
// Добавляем локальную сумму в атомарную переменную.
// Преобразуем в long для атомарного сложения
totalSum.addAndGet((long) localSum);
});
}
// Делим элементы массива на среднее значение
for (int t = 0; t < threads; t++) {
final int startRow = t * (size / threads);
final int endRow = (t == threads - 1) ? size : (t + 1) * (size / threads);
executor.submit(() -> {
// Разделяем элементы массива на среднее значение
for (int i = startRow; i < endRow; i++) {
for (int j = 0; j < size; j++) {
array[i][j] /= totalSum.get() / (double) (size * size);
}
}
});
}
// Закрываем пул после того, как все задачи были отправлены
executor.shutdown();
// Ожидаем завершения всех задач
while (!executor.isTerminated()) {
// Ждем завершения всех задач
}
double average = totalSum.get() / (double) (size * size);
System.out.println("Среднее арифметическое: " + average);
System.out.println("array[100][100] = " + array[100][100]);
}
// Порог разбиения
private static final int THRESHOLD = 50000;
// Многопоточный алгоритм с ForkJoinPool
public static void forkJoinPoolAlgorithm(double[][] array) {
int size = array.length;
ForkJoinPool forkJoinPool = new ForkJoinPool();
// Подсчет суммы
SumTask sumTask = new SumTask(array, 0, size);
double totalSum = forkJoinPool.invoke(sumTask);
// Вычисление среднего
double average = totalSum / (size * size);
System.out.println("Среднее арифметическое: " + average);
// Деление массива
forkJoinPool.invoke(new NormalizeTask(array, 0, size, average));
forkJoinPool.shutdown();
System.out.println("array[100][100] = " + array[100][100]);
}
// Подсчет суммы
static class SumTask extends RecursiveTask<Double> {
private final double[][] array;
private final int startRow, endRow;
SumTask(double[][] array, int startRow, int endRow) {
this.array = array;
this.startRow = startRow;
this.endRow = endRow;
}
@Override
protected Double compute() {
// Если строк меньше порога, считаем напрямую
if (endRow - startRow <= THRESHOLD) {
double sum = 0;
for (int i = startRow; i < endRow; i++) {
for (int j = 0; j < array[i].length; j++) {
sum += array[i][j];
}
}
return sum;
} else {
// Разделяем массив на две части
int midRow = (startRow + endRow) / 2;
SumTask top = new SumTask(array, startRow, midRow);
SumTask bottom = new SumTask(array, midRow, endRow);
// Запускаем верхнюю часть в новом потоке
top.fork();
// Вычисляем нижнюю часть в текущем потоке
double bottomResult = bottom.compute();
// Ждём завершения верхней части и суммируем
return top.join() + bottomResult;
}
}
}
// Деление массива
static class NormalizeTask extends RecursiveAction {
private final double[][] array;
private final int startRow, endRow;
private final double average;
NormalizeTask(double[][] array, int startRow, int endRow, double average) {
this.array = array;
this.startRow = startRow;
this.endRow = endRow;
this.average = average;
}
@Override
protected void compute() {
if (endRow - startRow <= THRESHOLD) {
for (int i = startRow; i < endRow; i++) {
for (int j = 0; j < array[i].length; j++) {
array[i][j] /= average;
}
}
} else {
int midRow = (startRow + endRow) / 2;
invokeAll(new NormalizeTask(array, startRow, midRow, average),
new NormalizeTask(array, midRow, endRow, average));
}
}
}
}

View File

@@ -0,0 +1,112 @@
# Лабораторная №1
Разработка многопоточного приложения с использованием Java Concurrency
согласно варианту задания.
Необходимо:
1) Разработать однопоточный вариант алгоритма и замерить время его работы.
2) Разработать параллельный вариант алгоритма с использованием
ThreadPoolExecutor и замерить время его работы
3) Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
и замерить время его работы.
Массив генерируется до работы всех вариантов алгоритмов. Все три
алгоритма обрабатывают три одинаковых массива.
## Вариант и задание
3) Разделить элементы матрицы на среднее арифметическое всех ее элементов.
### Описание
Данная программа разработана для сравнения производительности однопоточного и многопоточного подходов к делению элементов матрицы на среднее арифметическое её элементов. Задача состоит из двух основных частей:
1. Вычисление среднего арифметического
2. Деление элементов матрицы на среднее арифметическое
Эти этапы выполняются с разными вариантами многопоточности, чтобы выявить их влияние на производительность.
### Как запустить
```sh
javac DividingByAverage.java
java DividingByAverage
```
### Используемые технологии
Программа использует Java Concurrency, включая классы и интерфейсы:
1) `ExecutorService`
2) `ThreadPoolExecutor`
3) `ForkJoinPool`
4) `RecursiveAction`
### Что делает программа
1. Генерирует матрицу заданного размера, заполненную случайными значениями.
2. Вычисляет среднее арифметическое элементов матрицы.
3. Делит все элементы матрицы на среднее арифметическое.
4. Замеряет время выполнения для однопоточного и двух многопоточных методов.
## Результаты тестов
### Для матрцы 5000*5000:
```
Однопоточный алгоритм:
array[100][100] = 15.0
Среднее арифметическое: 50.00305776
array[100][100] = 0.29998165456191894
Время выполнения: 59 мс
Алгоритм с ThreadPoolExecutor:
Среднее арифметическое: 50.00305776
array[100][100] = 0.29998165456191894
Время выполнения: 56 мс
Алгоритм с ForkJoinPool:
Среднее арифметическое: 50.00305776
array[100][100] = 0.29998165456191894
Время выполнения: 66 мс
```
### Для матрцы 10000*10000:
```
Однопоточный алгоритм:
array[100][100] = 38.0
Среднее арифметическое: 50.00220663
array[100][100] = 0.7599664607041763
Время выполнения: 186 мс
Алгоритм с ThreadPoolExecutor:
Среднее арифметическое: 50.00220663
array[100][100] = 0.7599664607041763
Время выполнения: 140 мс
Алгоритм с ForkJoinPool:
Среднее арифметическое: 50.00220663
array[100][100] = 0.7599664607041763
Время выполнения: 174 мс
```
### Для матрцы 15000*15000:
```
Однопоточный алгоритм:
array[100][100] = 4.0
Среднее арифметическое: 49.99820225777778
array[100][100] = 0.08000287649097934
Время выполнения: 388 мс
Алгоритм с ThreadPoolExecutor:
Среднее арифметическое: 49.99820225777778
array[100][100] = 0.08000287649097934
Время выполнения: 323 мс
Алгоритм с ForkJoinPool:
Среднее арифметическое: 49.99820225777778
array[100][100] = 0.08000287649097934
Время выполнения: 379 мс
```
## Вывод ##
Пока матрицы небольших размеров, можно заметить, что многопоточные алгоритмы могут не иметь преимущества по времени выполнения перед однопоточным алгоритмом или даже быть хуже.
Это происходит из-за того, что ThreadPool и ForkJoin затрачивают дополнительные ресурсы на создание и поддержание потоков.
По мере роста размеров матрицы, многопоточные алгоритмы показывают большую эффективность, чем однопоточный. Однопоточный алгоритм начинает работать медленнее из-за большого количества элементов.
ThreadPool и ForkJoin начинают работать быстрее, так как дополнительные траты на поддержание потоков с лихвой компенсируются эффективностью многопоточного метода решения задачи.

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
import mpi.MPI;
public class Main {
public static void main(String[] args) {
MPI.Init(args);
int rank = MPI.COMM_WORLD.Rank();
int size = MPI.COMM_WORLD.Size();
int[][] matrix = null;
int rows = 1000;
int cols = 1000;
long starttime = 0;
long endtime = 0;
if (rank == 0) {
matrix = MatrixGenerator.generateMatrix(rows, cols);
starttime = System.nanoTime();
}
int halfRows = rows / 2;
int[] localMatrixFlat = new int[halfRows * cols];
if (rank == 0) {
int index = 0;
for (int i = halfRows; i < rows; i++) {
for (int j = 0; j < cols; j++) {
localMatrixFlat[index++] = matrix[i][j];
}
}
MPI.COMM_WORLD.Send(localMatrixFlat, 0, localMatrixFlat.length, MPI.INT, 1, 0);
index = 0;
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
localMatrixFlat[index++] = matrix[i][j];
}
}
} else if (rank == 1) {
MPI.COMM_WORLD.Recv(localMatrixFlat, 0, localMatrixFlat.length, MPI.INT, 0, 0);
}
int[][] localMatrix = new int[halfRows][cols];
int index = 0;
for (int i = 0; i < halfRows; i++) {
for (int j = 0; j < cols; j++) {
localMatrix[i][j] = localMatrixFlat[index++];
}
}
int localMax = findMaxInMatrix(localMatrix, rank);
System.out.println("Process " + rank + " local max: " + localMax);
int[] globalMax = new int[1];
MPI.COMM_WORLD.Reduce(new int[]{localMax}, 0, globalMax, 0, 1, MPI.INT, MPI.MAX, 0);
if (rank == 0) {
System.out.println("Global max element: " + globalMax[0]);
endtime = System.nanoTime();
long duration = (endtime - starttime);
System.out.println("Time: " + duration);
}
MPI.Finalize();
}
private static int findMaxInMatrix(int[][] matrix, int rank) {
int max = Integer.MIN_VALUE;
for (int[] row : matrix) {
for (int value : row) {
if (value > max) {
max = value;
System.out.println("New max "+max+" in container "+rank);
}
}
}
return max;
}
}

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] = random.nextInt(100000);
}
}
return matrix;
}
}

View File

@@ -0,0 +1,66 @@
## Лабораторная работа №1.
Разработка параллельного MPI приложения на языке Java.
Вариант 5: Нужно определить максимальный элемент матрицы
**Необходимо:**
1. Разработать параллельный вариант алгоритма с применением MPI, где должны быть две копии приложения,
которые соединяются между собой по сети.
2. Замерить время работы алгоритма
**Используемые технологии**
1. MPJ Express - библиотека, реализующая MPI для Java.
**Запуск лабораторной работы**
1. `mpjdaemon -boot 192.168.5.107` - включение демона mpj-express на первом контейнере
2. `mpjdaemon -boot 192.168.5.106` - включение демона mpj-express на втором контейнере
3. `mpjrun.sh -np 2 -dev niodev Main` - запуск приложения
**Что делает программа**
1. Генерирация матрицы
2. Матрица разделяется на две половины. Половина матрицы отправляется во второй контейнер
3. Обработка матрицы и поиск локального максимального элемента в каждой половине
4. Определение глобального максимума
**Тесты**
```
mpjrun.sh -np 2 -dev niodev Main
MPJ Express (0.44) is started in the cluster configuration with niodev
Starting process <0> on <CT107>
Starting process <1> on <secondcontainer>
New max 36703 in container 0
New max 40049 in container 0
New max 93370 in container 1
New max 95505 in container 0
New max 97047 in container 0
New max 99536 in container 0
New max 99823 in container 0
New max 99875 in container 0
New max 99896 in container 0
New max 99901 in container 0
New max 99988 in container 0
New max 94820 in container 1
New max 98237 in container 1
New max 99825 in container 1
New max 99929 in container 1
New max 99965 in container 1
New max 99966 in container 1
New max 99975 in container 1
New max 99981 in container 1
New max 99996 in container 1
New max 99998 in container 1
New max 99999 in container 1
New max 99996 in container 0
New max 99998 in container 0
New max 99999 in container 0
Process 1 local max: 99999
Process 0 local max: 99999
Global max element: 99999
Time: 485045144
Stopping Process <0> on <CT107>
Stopping Process <1> on <secondcontainer>
```
**Вывод**
С помощью MPI можно реализировать паралельный алгоритм некоторой программы. Если сравнивать время поиска максимального элемента с алгоритмами, реализованными в прошлой лабораторной работе(ForkJoinPool и ThreadPoolExecutor), то на одних и тех же значения алгоритм с MPI работает быстрее.

View File

@@ -0,0 +1,2 @@
192.168.5.107
192.168.5.106

View File

@@ -0,0 +1,93 @@
# Лабораторная работа №1
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
**Необходимо:**
1. Разработать однопоточный вариант алгоритма и замерить время его работы.
2. Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы
3. Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
## Вариант 12.
Определить среднее арифметическое элементов матрицы ниже главной
диагонали.
### Запуск лаборатороной работы:
1. Компиляция программы осуществляется с помощью команды:
> javac Main.java
2. Запуск скомпилированного класса:
> java Main
### Использованные технологии:
- Пакет `java.util.concurrent` предоставляет мощные инструменты для работы с многопоточностью и параллельными вычислениями
- `ExecutorService` и `Executors` используются для управления пулами потоков и выполнения задач
- `ThreadPoolExecutor` - в этом подходе используется пул потоков для параллельного вычисления суммы элементов матрицы. Матрица разбивается на части, и каждая часть обрабатывается отдельным потоком.
- `ForkJoinPool` — специализированный пул потоков, предназначенный для выполнения задач, которые могут быть рекурсивно разбиты на более мелкие подзадачи (fork), а затем объединены (join).
### Как работает программа:
1. Происходит генерация массива размером = size в классе `Main.java`
```
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int size = 1000; // size matrix
int[][] matrix = generateMatrix(size);
```
Метод для генерации массива:
```
private static int[][] generateMatrix(int size) {
int[][] matrix = new int[size][size];
Random random = new Random();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(1000);
}
}
return matrix;
}
```
2. Создание трех копий исходного массива.
3. Происходит обработка массива с помощью однопоточного варианта алгоритма ( класс SingleThreadedAverage).
4. Класс ThreadPoolAverage обрабатывает матрицу с использованием пула потоков создается пул потоков (задается количество потоков = 4), а каждая строка матрицы обрабатывается в отдельном потоке.
5. Класс ForkJoinAverage обрабатывает матрицу с использованием ForkJoinPool и рекурсивного разделения задачи: если блок большой, он разделяется на две части, и каждая часть обрабатывается рекурсивно.
6. Происходит замер времени работы всех алгоритмов в классе `Main.java`:
```
// SingleThreaded algorithm
long startTime = System.nanoTime();
double singleThreadedResult = SingleThreadedAverage.calculate(matrix);
long endTime = System.nanoTime();
System.out.println("Result: " + singleThreadedResult);
System.out.println("Single-threaded time: " + (endTime - startTime) / 1_000_000 + " ms");
// ThreadPoolExecutor algorithm
startTime = System.nanoTime();
double threadPoolResult = ThreadPoolAverage.calculate(matrix, 4);
endTime = System.nanoTime();
System.out.println("ThreadPoolExecutor time: " + (endTime - startTime) / 1_000_000 + " ms");
// ForkJoinPool algorithm
startTime = System.nanoTime();
double forkJoinResult = ForkJoinAverage.calculate(matrix);
endTime = System.nanoTime();
System.out.println("ForkJoinPool time: " + (endTime - startTime) / 1_000_000 + " ms");
```
### Тесты:
- при size = 1000:
Result: 498.8887907907908
Single-threaded time: 4 ms
ThreadPoolExecutor time: 104 ms
ForkJoinPool time: 42 ms
Result: 499.8382882882883
Single-threaded time: 1 ms
ThreadPoolExecutor time: 35 ms
ForkJoinPool time: 15 ms
- при size = 10000:
Result: 499.5273448344835
Single-threaded time: 26 ms
ThreadPoolExecutor time: 42 ms
ForkJoinPool time: 18 ms
## Вывод:
При небольших размерах матрицы лучше использовать однопоточный вариант алгоритма, так как он дает лучший показатель времени. Однако, если размер генерируемой матрицы большой, то нужно использовать ForkJoinPool. А ThreadPoolExecutor - самый медленный.

View File

@@ -0,0 +1,43 @@
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
public class ForkJoinAverage {
private static class AverageTask extends RecursiveTask<Long> {
private final int[][] matrix;
private final int startRow;
private final int endRow;
AverageTask(int[][] matrix, int startRow, int endRow) {
this.matrix = matrix;
this.startRow = startRow;
this.endRow = endRow;
}
@Override
protected Long compute() {
if (endRow - startRow <= 10) {
long sum = 0;
for (int i = startRow; i < endRow; i++) {
for (int j = 0; j < i; j++) {
sum += matrix[i][j];
}
}
return sum;
} else {
int mid = (startRow + endRow) / 2;
AverageTask left = new AverageTask(matrix, startRow, mid);
AverageTask right = new AverageTask(matrix, mid, endRow);
left.fork();
long rightResult = right.compute();
long leftResult = left.join();
return leftResult + rightResult;
}
}
}
public static double calculate(int[][] matrix) {
ForkJoinPool pool = new ForkJoinPool();
long totalSum = pool.invoke(new AverageTask(matrix, 0, matrix.length));
int totalCount = matrix.length * (matrix.length - 1) / 2;
return (double) totalSum / totalCount;
}
}

View File

@@ -0,0 +1,41 @@
import java.util.concurrent.ExecutionException;
import java.util.Random;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int size = 10000; // size matrix
int[][] matrix = generateMatrix(size);
// SingleThreaded algorithm
long startTime = System.nanoTime();
double singleThreadedResult = SingleThreadedAverage.calculate(matrix);
long endTime = System.nanoTime();
System.out.println("Single-threaded result: " + singleThreadedResult);
System.out.println("Single-threaded time: " + (endTime - startTime) / 1_000_000 + " ms");
// ThreadPoolExecutor algorithm
startTime = System.nanoTime();
double threadPoolResult = ThreadPoolAverage.calculate(matrix, 4);
endTime = System.nanoTime();
//System.out.println("ThreadPool result: " + threadPoolResult);
System.out.println("ThreadPoolExecutor time: " + (endTime - startTime) / 1_000_000 + " ms");
// ForkJoinPool algorithm
startTime = System.nanoTime();
double forkJoinResult = ForkJoinAverage.calculate(matrix);
endTime = System.nanoTime();
//System.out.println("ForkJoin result: " + forkJoinResult);
System.out.println("ForkJoinPool time: " + (endTime - startTime) / 1_000_000 + " ms");
}
private static int[][] generateMatrix(int size) {
int[][] matrix = new int[size][size];
Random random = new Random();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(1000);
}
}
return matrix;
}
}

View File

@@ -0,0 +1,15 @@
public class SingleThreadedAverage {
public static double calculate(int[][] matrix) {
long sum = 0;
int count = 0;
int size = matrix.length;
for (int i = 0; i < size; i++) {
for (int j = 0; j < i; j++) {
sum += matrix[i][j];
count++;
}
}
return (double) sum / count;
}

View File

@@ -0,0 +1,38 @@
import java.util.concurrent.*;
public class ThreadPoolAverage {
public static double calculate(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
int size = matrix.length;
int chunkSize = size / numThreads;
Future<Long>[] futures = new Future[numThreads];
int[] counts = new int[numThreads];
for (int t = 0; t < numThreads; t++) {
final int startRow = t * chunkSize;
final int endRow = (t == numThreads - 1) ? size : startRow + chunkSize;
final int threadIndex = t;
futures[t] = executor.submit(() -> {
long localSum = 0;
int localCount = 0;
for (int i = startRow; i < endRow; i++) {
for (int j = 0; j < i; j++) {
localSum += matrix[i][j];
localCount++;
}
}
counts[threadIndex] = localCount;
return localSum;
});
}
long totalSum = 0;
int totalCount = 0;
for (int t = 0; t < numThreads; t++) {
totalSum += futures[t].get();
totalCount += counts[t];
}
executor.shutdown();
return (double) totalSum / totalCount;
}
}