Shipilov_Nikita_lab_1
This commit is contained in:
123
shipilov_nikita_lab_1/Main.java
Normal file
123
shipilov_nikita_lab_1/Main.java
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static int[][] generateMatrix(int rows, int cols) {
|
||||||
|
int[][] matrix = new int[rows][cols];
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < rows; i++) {
|
||||||
|
for (int j = 0; j < cols; j++) {
|
||||||
|
matrix[i][j] = random.nextInt(100) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int findMin(int[][] matrix) {
|
||||||
|
int min = Integer.MAX_VALUE;
|
||||||
|
for (int[] row : matrix) {
|
||||||
|
for (int value : row) {
|
||||||
|
if (value < min) {
|
||||||
|
min = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void singleThreadedDivision(int[][] matrix) {
|
||||||
|
int min = findMin(matrix);
|
||||||
|
for (int i = 0; i < matrix.length; i++) {
|
||||||
|
for (int j = 0; j < matrix[i].length; j++) {
|
||||||
|
matrix[i][j] /= min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void threadPoolDivision(int[][] matrix, int numThreads) {
|
||||||
|
int min = findMin(matrix);
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||||
|
for (int i = 0; i < matrix.length; i++) {
|
||||||
|
final int row = i;
|
||||||
|
executor.submit(() -> {
|
||||||
|
for (int j = 0; j < matrix[row].length; j++) {
|
||||||
|
matrix[row][j] /= min;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
executor.shutdown();
|
||||||
|
try {
|
||||||
|
executor.awaitTermination(1, TimeUnit.MINUTES);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void forkJoinDivision(int[][] matrix) {
|
||||||
|
int min = findMin(matrix);
|
||||||
|
ForkJoinPool pool = new ForkJoinPool();
|
||||||
|
pool.invoke(new DivideTask(matrix, min, 0, matrix.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DivideTask extends RecursiveAction {
|
||||||
|
private final int[][] matrix;
|
||||||
|
private final int min;
|
||||||
|
private final int start;
|
||||||
|
private final int end;
|
||||||
|
|
||||||
|
DivideTask(int[][] matrix, int min, int start, int end) {
|
||||||
|
this.matrix = matrix;
|
||||||
|
this.min = min;
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void compute() {
|
||||||
|
if (end - start <= 10) {
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
for (int j = 0; j < matrix[i].length; j++) {
|
||||||
|
matrix[i][j] /= min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int mid = (start + end) / 2;
|
||||||
|
invokeAll(new DivideTask(matrix, min, start, mid),
|
||||||
|
new DivideTask(matrix, min, mid, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
int rows = 1000;
|
||||||
|
int cols = 1000;
|
||||||
|
int[][] matrix = generateMatrix(rows, cols);
|
||||||
|
|
||||||
|
int[][] singleThreadedMatrix = copyMatrix(matrix);
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
singleThreadedDivision(singleThreadedMatrix);
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
System.out.println("Single thread alghoritm: " + (endTime - startTime) + " ms");
|
||||||
|
|
||||||
|
int[][] threadPoolMatrix = copyMatrix(matrix);
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
threadPoolDivision(threadPoolMatrix, 4);
|
||||||
|
endTime = System.currentTimeMillis();
|
||||||
|
System.out.println("ThreadPoolExecutor: " + (endTime - startTime) + " ms");
|
||||||
|
|
||||||
|
int[][] forkJoinMatrix = copyMatrix(matrix);
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
forkJoinDivision(forkJoinMatrix);
|
||||||
|
endTime = System.currentTimeMillis();
|
||||||
|
System.out.println("ForkJoinPool: " + (endTime - startTime) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[][] copyMatrix(int[][] matrix) {
|
||||||
|
int[][] copy = new int[matrix.length][matrix[0].length];
|
||||||
|
for (int i = 0; i < matrix.length; i++) {
|
||||||
|
System.arraycopy(matrix[i], 0, copy[i], 0, matrix[i].length);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
shipilov_nikita_lab_1/diagram.png
Normal file
BIN
shipilov_nikita_lab_1/diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
126
shipilov_nikita_lab_1/readme.md
Normal file
126
shipilov_nikita_lab_1/readme.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# Лабораторная работа №1
|
||||||
|
|
||||||
|
Разработка многопоточного приложения с использованием Java Concurrency согласно варианту задания.
|
||||||
|
|
||||||
|
### Необходимо:
|
||||||
|
|
||||||
|
- Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||||
|
- Разработать параллельный вариант алгоритма с использованием ThreadPoolExecutor и замерить время его работы.
|
||||||
|
- Разработать параллельный вариант алгоритма с использованием ForkJoinPoll и замерить время его работы.
|
||||||
|
|
||||||
|
!!!. Массив генерируется до работы всех вариантов алгоритмов. Все три алгоритма обрабатывают три одинаковых массива.
|
||||||
|
|
||||||
|
Вариант задания 2: Разделить элементы матрицы на наименьший элемент.
|
||||||
|
|
||||||
|
### Как запустить лабораторную работу:
|
||||||
|
|
||||||
|
`javac Main.java` компилирует исходный код
|
||||||
|
`java Main` команда запускает скомпилированный класс
|
||||||
|
|
||||||
|
### Какие технологии использовали:
|
||||||
|
|
||||||
|
- Java 17+
|
||||||
|
|
||||||
|
- ExecutorService (ThreadPoolExecutor)
|
||||||
|
|
||||||
|
- ForkJoinPool
|
||||||
|
|
||||||
|
- System.currentTimeMillis() для замеров времени выполнения
|
||||||
|
|
||||||
|
### Как работает программа:
|
||||||
|
|
||||||
|
1. Генерация матрицы:
|
||||||
|
|
||||||
|
- Программа создаёт двумерный массив заданного размера (например, 1000x1000).
|
||||||
|
|
||||||
|
- Заполняет его случайными значениями.
|
||||||
|
|
||||||
|
- Определяет наименьший элемент матрицы.
|
||||||
|
|
||||||
|
2. Однопоточный алгоритм:
|
||||||
|
|
||||||
|
- Проходит по всей матрице в одном потоке.
|
||||||
|
|
||||||
|
- Делит каждый элемент матрицы на наименьший элемент.
|
||||||
|
|
||||||
|
- Записывает результат в новую матрицу.
|
||||||
|
|
||||||
|
- Измеряет время выполнения.
|
||||||
|
|
||||||
|
3. Параллельный алгоритм с ThreadPoolExecutor:
|
||||||
|
|
||||||
|
- Создаёт пул потоков с фиксированным количеством потоков (обычно равное количеству ядер CPU).
|
||||||
|
|
||||||
|
- Делит матрицу на строки, назначая обработку каждой строки отдельному потоку.
|
||||||
|
|
||||||
|
- Потоки выполняют деление элементов и записывают результаты.
|
||||||
|
|
||||||
|
- Ожидает завершения всех потоков.
|
||||||
|
|
||||||
|
- Измеряет время выполнения.
|
||||||
|
|
||||||
|
4. Параллельный алгоритм с ForkJoinPool:
|
||||||
|
|
||||||
|
- Использует принцип рекурсивного разбиения задач (Fork/Join).
|
||||||
|
|
||||||
|
- Разбивает матрицу на более мелкие подзадачи, пока они не станут достаточно малы для обработки в одном потоке.
|
||||||
|
|
||||||
|
- Выполняет деление элементов параллельно.
|
||||||
|
|
||||||
|
- Объединяет результаты.
|
||||||
|
|
||||||
|
- Измеряет время выполнения.
|
||||||
|
|
||||||
|
5. Вывод результатов:
|
||||||
|
|
||||||
|
- Для каждого метода измеряется время выполнения.
|
||||||
|
|
||||||
|
- Результаты выводятся в консоль.
|
||||||
|
|
||||||
|
### (Размер матрицы 1000х1000)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Single thread alghoritm: 20 ms
|
||||||
|
ThreadPoolExecutor: 73 ms
|
||||||
|
ForkJoinPool: 28 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
### (Размер матрицы 2000х2000)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Single thread alghoritm: 39 ms
|
||||||
|
ThreadPoolExecutor: 110 ms
|
||||||
|
ForkJoinPool: 51 ms.
|
||||||
|
```
|
||||||
|
|
||||||
|
### (Размер матрицы 3000x3000)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Single thread alghoritm: 69 ms
|
||||||
|
ThreadPoolExecutor: 182 ms
|
||||||
|
ForkJoinPool: 106 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
### (Размер матрицы 4000x4000)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Single thread alghoritm: 108 ms
|
||||||
|
ThreadPoolExecutor: 237 ms
|
||||||
|
ForkJoinPool: 134 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="./diagram.png">
|
||||||
|
|
||||||
|
### Анализ результатов
|
||||||
|
|
||||||
|
1. Однопоточный алгоритм показывает линейный рост времени выполнения с увеличением размера матрицы. Это ожидаемо, так как вычисления выполняются последовательно.
|
||||||
|
|
||||||
|
2. `ThreadPoolExecutor` демонстрирует худшие результаты по сравнению с остальными методами. Это связано с накладными расходами на управление потоками и передачу задач в пул потоков. Он эффективен при большом количестве независимых задач, но в данном случае не даёт прироста.
|
||||||
|
|
||||||
|
3. `ForkJoinPool` показывает лучшую производительность среди параллельных методов. Это объясняется тем, что `ForkJoinPool` оптимизирован для рекурсивного разбиения задач и эффективного использования ресурсов CPU.
|
||||||
|
|
||||||
|
4. При увеличении размера матрицы разница между `ForkJoinPool` и ThreadPoolExecutor становится более заметной, так как `ForkJoinPool` минимизирует накладные расходы на распределение задач.
|
||||||
|
|
||||||
|
### Вывод
|
||||||
|
|
||||||
|
Использование `ForkJoinPool` даёт наилучший результат среди многопоточных методов в данном алгоритме. `ThreadPoolExecutor` не оправдывает себя из-за накладных расходов на управление потоками. Однопоточный метод показывает неплохие результаты на небольших данных, но проигрывает `ForkJoinPool` при увеличении объёма вычислений.
|
||||||
Reference in New Issue
Block a user