Merge pull request 'ПИбд-22_Хасянов_А.Н._Лаб_1' (#30) from Aidar/SSPR_25:khasyanov_aidar_lab_1 into main
Неплохо бы добавить описание кода, в остальном хорошо Reviewed-on: #30
This commit was merged in pull request #30.
This commit is contained in:
227
khasyanov_aidar_lab_1/MinDivisionTask.java
Normal file
227
khasyanov_aidar_lab_1/MinDivisionTask.java
Normal file
@@ -0,0 +1,227 @@
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class MinDivisionTask {
|
||||
static double[][] mainArray = new double[4000][4000];
|
||||
static double[][] bufferArray = new double[mainArray.length][mainArray.length];
|
||||
static final int FRONTIER = 5000; // Порог разбиения
|
||||
static final int THREADS = 2; // Кол-во потоков
|
||||
static int countOftests = 20; // Кол-во тестов
|
||||
|
||||
public static void main(String[] args) {
|
||||
DoTests();
|
||||
}
|
||||
public static void DoTests() {
|
||||
long SingleThreadedTimeSum = 0;
|
||||
long ThreadPoolExecutorTimeSum = 0;
|
||||
long ForkJoinPoolTimeSum = 0;
|
||||
int count = 0;
|
||||
Random random = new Random();
|
||||
while (count != countOftests) {
|
||||
count += 1;
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) {
|
||||
mainArray[i][j] = random.nextInt(-100,100);
|
||||
bufferArray[i][j] = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
//PrintLogMatrix(mainArray); System.out.println();
|
||||
|
||||
// Однопоточный алгоритм
|
||||
long SingleThreadedTime = ComputeAndPrintDuration("SingleThreadedAlgorithm");
|
||||
SingleThreadedTimeSum += SingleThreadedTime;
|
||||
// Многопоточный алгоритм
|
||||
long ThreadPoolExecutorTime = ComputeAndPrintDuration("ThreadPoolExecutorAlgorithm");
|
||||
ThreadPoolExecutorTimeSum += ThreadPoolExecutorTime;
|
||||
// ForkJoinPool-алгоритм
|
||||
long ForkJoinPoolTime = ComputeAndPrintDuration("ForkJoinPoolAlgorithm");
|
||||
ForkJoinPoolTimeSum += ForkJoinPoolTime;
|
||||
|
||||
System.out.println("{Тест "+ count + "} " + "SingleThreadedTime: " + SingleThreadedTime +
|
||||
" ThreadPoolExecutorTime: " + ThreadPoolExecutorTime + " ForkJoinPoolTime: " + ForkJoinPoolTime);
|
||||
}
|
||||
System.out.println("SingleThreadedTime: " + SingleThreadedTimeSum/countOftests);
|
||||
System.out.println("ThreadPoolExecutorTime: " + ThreadPoolExecutorTimeSum/countOftests);
|
||||
System.out.println("ForkJoinPoolTime: " + ForkJoinPoolTimeSum/countOftests);
|
||||
}
|
||||
public static void CopyArray() {
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) mainArray[i][j] = bufferArray[i][j];
|
||||
}
|
||||
}
|
||||
public static void PrintLogMatrix(double[][] mainArray) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
System.out.print(mainArray[i][j] + " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
public static long ComputeAndPrintDuration(String typeOfAlgoritm) {
|
||||
long startTime = 0, endTime = 0, duration;
|
||||
switch (typeOfAlgoritm) {
|
||||
case "SingleThreadedAlgorithm":
|
||||
startTime = System.nanoTime();
|
||||
SingleThreadedAlgorithm(mainArray);
|
||||
endTime = System.nanoTime();
|
||||
break;
|
||||
case "ThreadPoolExecutorAlgorithm":
|
||||
startTime = System.nanoTime();
|
||||
ThreadPoolExecutorAlgorithm(mainArray);
|
||||
endTime = System.nanoTime();
|
||||
break;
|
||||
case "ForkJoinPoolAlgorithm":
|
||||
startTime = System.nanoTime();
|
||||
ForkJoinPoolAlgorithm(mainArray);
|
||||
endTime = System.nanoTime();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
duration = (endTime - startTime) / 1000000;
|
||||
//System.out.println("Время выполнения: " + (duration) + " мс\n");
|
||||
CopyArray();
|
||||
return duration;
|
||||
}
|
||||
|
||||
public static void SingleThreadedAlgorithm(double[][] mainArray) {
|
||||
//System.out.println("Однопоточный алгоритм");
|
||||
double min = Double.MAX_VALUE;
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) {
|
||||
if(min > mainArray[i][j]) min = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
//System.out.println("Наименьший элемент: " + min);
|
||||
|
||||
for (int i = 0; i < mainArray.length; i++) {
|
||||
for (int j = 0; j < mainArray.length; j++) {
|
||||
mainArray[i][j] = mainArray[i][j] / min;
|
||||
}
|
||||
}
|
||||
|
||||
//PrintLogMatrix(mainArray);
|
||||
}
|
||||
public static void ThreadPoolExecutorAlgorithm(double[][] mainArray) {
|
||||
//System.out.println("ThreadPoolExecutorAlgorithm");
|
||||
int size = mainArray.length;
|
||||
ExecutorService executor = Executors.newFixedThreadPool(THREADS); // Создаем пул потоков
|
||||
|
||||
AtomicLong globalMin = new AtomicLong(Long.MAX_VALUE);
|
||||
|
||||
// Вычисление суммы элементов массива
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
final int startRow = t * (size / THREADS); // Начало данного потока
|
||||
final int endRow = (t == THREADS - 1) ? size : (t + 1) * (size / THREADS); // Конец данного потока
|
||||
|
||||
executor.submit(() -> {
|
||||
double localMin = Double.MAX_VALUE;
|
||||
// Вычисление суммы в рамках выделенного потоку диапазона строк
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
if(localMin > mainArray[i][j]) localMin = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
// Вычисление наименьшего элемента глобально
|
||||
long finalLocalMin = (long)localMin;
|
||||
globalMin.updateAndGet(v -> Math.min(v, finalLocalMin));
|
||||
});
|
||||
}
|
||||
|
||||
// Деление элементов массива на наименьшее значение
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
final int startRow = t * (size / THREADS); // Начало данного потока
|
||||
final int endRow = (t == THREADS - 1) ? size : (t + 1) * (size / THREADS); // Конец данного потока
|
||||
|
||||
executor.submit(() -> {
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
mainArray[i][j] /= globalMin.get();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
executor.shutdown();
|
||||
while (!executor.isTerminated()) { /* Ожидание завершения всех задач */ }
|
||||
|
||||
double min = globalMin.get();
|
||||
//System.out.println("Наименьший элемент: " + min);
|
||||
//PrintLogMatrix(mainArray);
|
||||
}
|
||||
public static void ForkJoinPoolAlgorithm(double[][] mainArray) {
|
||||
//System.out.println("ForkJoinPoolAlgorithm");
|
||||
int size = mainArray.length;
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
|
||||
// Вычисление наименьшего элемента
|
||||
MinTask minTask = new MinTask(mainArray, 0, size);
|
||||
double min = forkJoinPool.invoke(minTask);
|
||||
//System.out.println("Наименьший элемент: " + min);
|
||||
// Деление массива на наименьший элемент
|
||||
forkJoinPool.invoke(new DivisionTask(mainArray, 0, size, min));
|
||||
|
||||
forkJoinPool.shutdown();
|
||||
//PrintLogMatrix(mainArray);
|
||||
}
|
||||
|
||||
static class MinTask extends RecursiveTask<Double> {
|
||||
private final double[][] mainArray;
|
||||
private final int startRow, endRow;
|
||||
|
||||
MinTask(double[][] mainArray, int startRow, int endRow) {
|
||||
this.mainArray = mainArray;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double compute() {
|
||||
if (endRow - startRow <= FRONTIER) {
|
||||
double min = Double.MAX_VALUE;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < mainArray[i].length; j++) {
|
||||
if(min > mainArray[i][j]) min = mainArray[i][j];
|
||||
}
|
||||
}
|
||||
return (Double) min;
|
||||
} else {
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
|
||||
MinTask top = new MinTask(mainArray, startRow, midRow);
|
||||
MinTask bottom = new MinTask(mainArray, midRow, endRow);
|
||||
|
||||
top.fork();
|
||||
double bottomResult = bottom.compute();
|
||||
return (Double) Math.min(top.join(), (long)bottomResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
static class DivisionTask extends RecursiveAction {
|
||||
private final double[][] mainArray;
|
||||
private final int startRow, endRow;
|
||||
private final double min;
|
||||
|
||||
DivisionTask(double[][] mainArray, int startRow, int endRow, double min) {
|
||||
this.mainArray = mainArray;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (endRow - startRow <= FRONTIER) {
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < mainArray[i].length; j++) {
|
||||
mainArray[i][j] /= min;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
invokeAll(new DivisionTask(mainArray, startRow, midRow, min),
|
||||
new DivisionTask(mainArray, midRow, endRow, min));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
khasyanov_aidar_lab_1/README.md
Normal file
107
khasyanov_aidar_lab_1/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Лабораторная №1
|
||||
Разработка многопоточного приложения с использованием Java Concurrency
|
||||
согласно варианту задания.
|
||||
|
||||
Необходимо:
|
||||
1) Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2) Разработать параллельный вариант алгоритма с использованием
|
||||
ThreadPoolExecutor и замерить время его работы
|
||||
3) Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
|
||||
и замерить время его работы.
|
||||
|
||||
Массив генерируется до работы всех вариантов алгоритмов. Все три
|
||||
алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
## Вариант и задание
|
||||
2. Разделить элементы матрицы на наименьший элемент.
|
||||
|
||||
### Описание
|
||||
Данная программа разработана для сравнения производительности однопоточного и многопоточного подходов к делению
|
||||
элементов матрицы на среднее арифметическое её элементов. Задача состоит из двух основных частей:
|
||||
1. Нахождение наименьшего элемента в матрице
|
||||
2. Деление элементов матрицы на наименьший элемент в матрице
|
||||
|
||||
Эти этапы выполняются с разными вариантами многопоточности, чтобы выявить их влияние на производительность.
|
||||
|
||||
### Как запустить
|
||||
java MinDivisionTask
|
||||
|
||||
### Используемые технологии
|
||||
Программа использует Java Concurrency, включая классы и интерфейсы:
|
||||
1) ExecutorService
|
||||
2) RecursiveAction
|
||||
3) ForkJoinPool
|
||||
ThreadPoolExecutor был реализован вручную
|
||||
|
||||
### Что делает программа
|
||||
1. Генерирует матрицу заданного размера, заполненную случайными значениями.
|
||||
2. Находит наименьший элемент в матрице.
|
||||
3. Делит все элементы матрицы на наименьший элемент в матрице.
|
||||
4. Замеряет время выполнения для однопоточного и двух многопоточных (ThreadPoolExecutor, ForkJoinPool) алгоритмов.
|
||||
|
||||
## Результаты тестов
|
||||
(SingleThreadedTime - Однопоточный алгоритм)
|
||||
|
||||
### Для матрцы 4000*4000:
|
||||
{Тест 1} SingleThreadedTime: 80 ThreadPoolExecutorTime: 78 ForkJoinPoolTime: 64
|
||||
{Тест 2} SingleThreadedTime: 45 ThreadPoolExecutorTime: 39 ForkJoinPoolTime: 64
|
||||
{Тест 3} SingleThreadedTime: 37 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 32
|
||||
{Тест 4} SingleThreadedTime: 32 ThreadPoolExecutorTime: 40 ForkJoinPoolTime: 46
|
||||
{Тест 5} SingleThreadedTime: 43 ThreadPoolExecutorTime: 32 ForkJoinPoolTime: 50
|
||||
{Тест 6} SingleThreadedTime: 44 ThreadPoolExecutorTime: 29 ForkJoinPoolTime: 45
|
||||
{Тест 7} SingleThreadedTime: 45 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 45
|
||||
{Тест 8} SingleThreadedTime: 44 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 47
|
||||
{Тест 9} SingleThreadedTime: 43 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 45
|
||||
{Тест 10} SingleThreadedTime: 43 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 11} SingleThreadedTime: 34 ThreadPoolExecutorTime: 31 ForkJoinPoolTime: 32
|
||||
{Тест 12} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 13} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
{Тест 14} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 15} SingleThreadedTime: 32 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 16} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 17} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 32
|
||||
{Тест 18} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
{Тест 19} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
{Тест 20} SingleThreadedTime: 31 ThreadPoolExecutorTime: 30 ForkJoinPoolTime: 31
|
||||
Среднее время:
|
||||
SingleThreadedTime: 38
|
||||
ThreadPoolExecutorTime: 33
|
||||
ForkJoinPoolTime: 39
|
||||
|
||||
### Для матрцы 11000*11000:
|
||||
{Тест 1} SingleThreadedTime: 402 ThreadPoolExecutorTime: 259 ForkJoinPoolTime: 271
|
||||
{Тест 2} SingleThreadedTime: 406 ThreadPoolExecutorTime: 273 ForkJoinPoolTime: 236
|
||||
{Тест 3} SingleThreadedTime: 388 ThreadPoolExecutorTime: 245 ForkJoinPoolTime: 226
|
||||
{Тест 4} SingleThreadedTime: 237 ThreadPoolExecutorTime: 329 ForkJoinPoolTime: 339
|
||||
{Тест 5} SingleThreadedTime: 333 ThreadPoolExecutorTime: 251 ForkJoinPoolTime: 227
|
||||
{Тест 6} SingleThreadedTime: 330 ThreadPoolExecutorTime: 231 ForkJoinPoolTime: 220
|
||||
{Тест 7} SingleThreadedTime: 344 ThreadPoolExecutorTime: 234 ForkJoinPoolTime: 224
|
||||
{Тест 8} SingleThreadedTime: 330 ThreadPoolExecutorTime: 224 ForkJoinPoolTime: 224
|
||||
{Тест 9} SingleThreadedTime: 329 ThreadPoolExecutorTime: 222 ForkJoinPoolTime: 220
|
||||
{Тест 10} SingleThreadedTime: 336 ThreadPoolExecutorTime: 230 ForkJoinPoolTime: 239
|
||||
{Тест 11} SingleThreadedTime: 238 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 222
|
||||
{Тест 12} SingleThreadedTime: 238 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 234
|
||||
{Тест 13} SingleThreadedTime: 243 ThreadPoolExecutorTime: 241 ForkJoinPoolTime: 223
|
||||
{Тест 14} SingleThreadedTime: 253 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 224
|
||||
{Тест 15} SingleThreadedTime: 235 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 223
|
||||
{Тест 16} SingleThreadedTime: 243 ThreadPoolExecutorTime: 222 ForkJoinPoolTime: 223
|
||||
{Тест 17} SingleThreadedTime: 236 ThreadPoolExecutorTime: 221 ForkJoinPoolTime: 221
|
||||
{Тест 18} SingleThreadedTime: 234 ThreadPoolExecutorTime: 225 ForkJoinPoolTime: 233
|
||||
{Тест 19} SingleThreadedTime: 238 ThreadPoolExecutorTime: 221 ForkJoinPoolTime: 221
|
||||
{Тест 20} SingleThreadedTime: 241 ThreadPoolExecutorTime: 223 ForkJoinPoolTime: 225
|
||||
Среднее время:
|
||||
SingleThreadedTime: 291
|
||||
ThreadPoolExecutorTime: 237
|
||||
ForkJoinPoolTime: 233
|
||||
|
||||
## Вывод
|
||||
В ходе анализа производительности алгоритмов обработки матриц разного размера выяснилось, что многопоточные алгоритмы
|
||||
не всегда оказываются быстрее однопоточных, особенно при работе с небольшими матрицами. В таких случаях многопоточные
|
||||
алгоритмы, использующие ThreadPoolExecutor и ForkJoinPool, показывают более плохие результаты из-за дополнительных
|
||||
затрат на создание потоков и управление ими.
|
||||
Но когда размеры матриц увеличиваются, многопоточные алгоритмы начинают показывать гораздо лучшую производительность
|
||||
по сравнению с однопоточным. Однопоточный алгоритм начинает замедляться из-за большого объема данных, тогда как
|
||||
многопоточные алгоритмы, начинают работать быстрее. Это происходит потому, что затраты на управление потоками
|
||||
оправдываются их параллельной работой, что позволяет значительно ускорить обработку данных.
|
||||
Вообщем, многопоточность действительно может значительно повысить производительность при обработке больших объемов
|
||||
данных.
|
||||
Reference in New Issue
Block a user