shurygin_dima_lab_1 is ready
This commit is contained in:
249
shurygin_dima_lab_1/DividingByAverage.java
Normal file
249
shurygin_dima_lab_1/DividingByAverage.java
Normal file
@@ -0,0 +1,249 @@
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
|
||||
public class DividingByAverage {
|
||||
public static void main(String[] args) {
|
||||
Random random = new Random();
|
||||
|
||||
// Создание массива
|
||||
double[][] array = new double[8000][8000];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = random.nextInt(101);
|
||||
}
|
||||
}
|
||||
|
||||
// Создание копии исходного массива
|
||||
double[][] arraySave = new double[array.length][array.length];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
arraySave[i][j] = array[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Тест однопоточного алгоритма
|
||||
System.out.println("Однопоточный алгоритм:");
|
||||
long startTime1 = System.nanoTime();
|
||||
singleThreadedAlgorithm(array);
|
||||
long endTime1 = System.nanoTime();
|
||||
long duration1 = endTime1 - startTime1;
|
||||
System.out.println("Время выполнения: " + (duration1 / 1000000) + " мс");
|
||||
System.out.println(" ");
|
||||
|
||||
// Восстановление массива
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = arraySave[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Тест многопоточного алгоритма с ThreadPoolExecutor
|
||||
System.out.println("Алгоритм с ThreadPoolExecutor:");
|
||||
long startTime2 = System.nanoTime();
|
||||
threadPoolExecutorAlgorithm(array);
|
||||
long endTime2 = System.nanoTime();
|
||||
long duration2 = endTime2 - startTime2;
|
||||
System.out.println("Время выполнения: " + (duration2 / 1000000) + " мс");
|
||||
System.out.println(" ");
|
||||
|
||||
// Восстановление массива
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = arraySave[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Тест многопоточного алгоритма с ForkJoinPool
|
||||
System.out.println("Алгоритм с ForkJoinPool:");
|
||||
long startTime3 = System.nanoTime();
|
||||
forkJoinPoolAlgorithm(array);
|
||||
long endTime3 = System.nanoTime();
|
||||
long duration3 = endTime3 - startTime3;
|
||||
System.out.println("Время выполнения: " + (duration3 / 1000000) + " мс");
|
||||
System.out.println(" ");
|
||||
|
||||
// Восстановление массива
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = arraySave[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Однопоточный алгоритм
|
||||
public static void singleThreadedAlgorithm(double[][] array) {
|
||||
// Сумма всех элементов массива
|
||||
double sum = 0;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
sum += array[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Среднее арифметическое
|
||||
double average = (double) sum / (array.length * array.length);
|
||||
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
System.out.println("Среднее арифметическое: " + average);
|
||||
|
||||
// Деление элементов массива на среднее
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
array[i][j] = array[i][j] / average;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
}
|
||||
|
||||
// Тест многопоточного алгоритма с ThreadPoolExecutor
|
||||
public static void threadPoolExecutorAlgorithm(double[][] array) {
|
||||
int size = array.length;
|
||||
// Количество потоков
|
||||
int threads = 4;
|
||||
// Создаем пул потоков
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threads);
|
||||
// Используем AtomicLong для безопасного накопления суммы в потоках
|
||||
AtomicLong totalSum = new AtomicLong(0);
|
||||
|
||||
// Считаем сумму элементов массива с использованием потоков
|
||||
for (int t = 0; t < threads; t++) {
|
||||
// Начало потока
|
||||
final int startRow = t * (size / threads);
|
||||
// Конец потока
|
||||
final int endRow = (t == threads - 1) ? size : (t + 1) * (size / threads);
|
||||
executor.submit(() -> {
|
||||
// Считаем сумму в пределах выделенного потоку диапазона строк
|
||||
double localSum = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
localSum += array[i][j];
|
||||
}
|
||||
}
|
||||
// Добавляем локальную сумму в атомарную переменную.
|
||||
// Преобразуем в long для атомарного сложения
|
||||
totalSum.addAndGet((long) localSum);
|
||||
});
|
||||
}
|
||||
|
||||
// Делим элементы массива на среднее значение
|
||||
for (int t = 0; t < threads; t++) {
|
||||
final int startRow = t * (size / threads);
|
||||
final int endRow = (t == threads - 1) ? size : (t + 1) * (size / threads);
|
||||
executor.submit(() -> {
|
||||
// Разделяем элементы массива на среднее значение
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
array[i][j] /= totalSum.get() / (double) (size * size);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Закрываем пул после того, как все задачи были отправлены
|
||||
executor.shutdown();
|
||||
|
||||
// Ожидаем завершения всех задач
|
||||
while (!executor.isTerminated()) {
|
||||
// Ждем завершения всех задач
|
||||
}
|
||||
|
||||
double average = totalSum.get() / (double) (size * size);
|
||||
System.out.println("Среднее арифметическое: " + average);
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
}
|
||||
|
||||
// Порог разбиения
|
||||
private static final int THRESHOLD = 50000;
|
||||
|
||||
// Многопоточный алгоритм с ForkJoinPool
|
||||
public static void forkJoinPoolAlgorithm(double[][] array) {
|
||||
int size = array.length;
|
||||
ForkJoinPool forkJoinPool = new ForkJoinPool();
|
||||
|
||||
// Подсчет суммы
|
||||
SumTask sumTask = new SumTask(array, 0, size);
|
||||
double totalSum = forkJoinPool.invoke(sumTask);
|
||||
|
||||
// Вычисление среднего
|
||||
double average = totalSum / (size * size);
|
||||
System.out.println("Среднее арифметическое: " + average);
|
||||
|
||||
// Деление массива
|
||||
forkJoinPool.invoke(new NormalizeTask(array, 0, size, average));
|
||||
|
||||
forkJoinPool.shutdown();
|
||||
|
||||
System.out.println("array[100][100] = " + array[100][100]);
|
||||
}
|
||||
|
||||
// Подсчет суммы
|
||||
static class SumTask extends RecursiveTask<Double> {
|
||||
private final double[][] array;
|
||||
private final int startRow, endRow;
|
||||
|
||||
SumTask(double[][] array, int startRow, int endRow) {
|
||||
this.array = array;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double compute() {
|
||||
// Если строк меньше порога, считаем напрямую
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
double sum = 0;
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < array[i].length; j++) {
|
||||
sum += array[i][j];
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
} else {
|
||||
// Разделяем массив на две части
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
|
||||
SumTask top = new SumTask(array, startRow, midRow);
|
||||
SumTask bottom = new SumTask(array, midRow, endRow);
|
||||
|
||||
// Запускаем верхнюю часть в новом потоке
|
||||
top.fork();
|
||||
// Вычисляем нижнюю часть в текущем потоке
|
||||
double bottomResult = bottom.compute();
|
||||
// Ждём завершения верхней части и суммируем
|
||||
return top.join() + bottomResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Деление массива
|
||||
static class NormalizeTask extends RecursiveAction {
|
||||
private final double[][] array;
|
||||
private final int startRow, endRow;
|
||||
private final double average;
|
||||
|
||||
NormalizeTask(double[][] array, int startRow, int endRow, double average) {
|
||||
this.array = array;
|
||||
this.startRow = startRow;
|
||||
this.endRow = endRow;
|
||||
this.average = average;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (endRow - startRow <= THRESHOLD) {
|
||||
for (int i = startRow; i < endRow; i++) {
|
||||
for (int j = 0; j < array[i].length; j++) {
|
||||
array[i][j] /= average;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int midRow = (startRow + endRow) / 2;
|
||||
invokeAll(new NormalizeTask(array, startRow, midRow, average),
|
||||
new NormalizeTask(array, midRow, endRow, average));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
112
shurygin_dima_lab_1/README.md
Normal file
112
shurygin_dima_lab_1/README.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Лабораторная №1
|
||||
Разработка многопоточного приложения с использованием Java Concurrency
|
||||
согласно варианту задания.
|
||||
|
||||
Необходимо:
|
||||
1) Разработать однопоточный вариант алгоритма и замерить время его работы.
|
||||
2) Разработать параллельный вариант алгоритма с использованием
|
||||
ThreadPoolExecutor и замерить время его работы
|
||||
3) Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
|
||||
и замерить время его работы.
|
||||
|
||||
Массив генерируется до работы всех вариантов алгоритмов. Все три
|
||||
алгоритма обрабатывают три одинаковых массива.
|
||||
|
||||
## Вариант и задание
|
||||
3) Разделить элементы матрицы на среднее арифметическое всех ее элементов.
|
||||
|
||||
### Описание
|
||||
Данная программа разработана для сравнения производительности однопоточного и многопоточного подходов к делению элементов матрицы на среднее арифметическое её элементов. Задача состоит из двух основных частей:
|
||||
|
||||
1. Вычисление среднего арифметического
|
||||
2. Деление элементов матрицы на среднее арифметическое
|
||||
|
||||
Эти этапы выполняются с разными вариантами многопоточности, чтобы выявить их влияние на производительность.
|
||||
|
||||
### Как запустить
|
||||
```sh
|
||||
javac DividingByAverage.java
|
||||
java DividingByAverage
|
||||
```
|
||||
|
||||
### Используемые технологии
|
||||
Программа использует Java Concurrency, включая классы и интерфейсы:
|
||||
1) `ExecutorService`
|
||||
2) `ThreadPoolExecutor`
|
||||
3) `ForkJoinPool`
|
||||
4) `RecursiveAction`
|
||||
|
||||
### Что делает программа
|
||||
1. Генерирует матрицу заданного размера, заполненную случайными значениями.
|
||||
2. Вычисляет среднее арифметическое элементов матрицы.
|
||||
3. Делит все элементы матрицы на среднее арифметическое.
|
||||
4. Замеряет время выполнения для однопоточного и двух многопоточных методов.
|
||||
|
||||
## Результаты тестов
|
||||
|
||||
### Для матрцы 5000*5000:
|
||||
|
||||
```
|
||||
Однопоточный алгоритм:
|
||||
array[100][100] = 15.0
|
||||
Среднее арифметическое: 50.00305776
|
||||
array[100][100] = 0.29998165456191894
|
||||
Время выполнения: 59 мс
|
||||
|
||||
Алгоритм с ThreadPoolExecutor:
|
||||
Среднее арифметическое: 50.00305776
|
||||
array[100][100] = 0.29998165456191894
|
||||
Время выполнения: 56 мс
|
||||
|
||||
Алгоритм с ForkJoinPool:
|
||||
Среднее арифметическое: 50.00305776
|
||||
array[100][100] = 0.29998165456191894
|
||||
Время выполнения: 66 мс
|
||||
```
|
||||
|
||||
### Для матрцы 10000*10000:
|
||||
|
||||
```
|
||||
Однопоточный алгоритм:
|
||||
array[100][100] = 38.0
|
||||
Среднее арифметическое: 50.00220663
|
||||
array[100][100] = 0.7599664607041763
|
||||
Время выполнения: 186 мс
|
||||
|
||||
Алгоритм с ThreadPoolExecutor:
|
||||
Среднее арифметическое: 50.00220663
|
||||
array[100][100] = 0.7599664607041763
|
||||
Время выполнения: 140 мс
|
||||
|
||||
Алгоритм с ForkJoinPool:
|
||||
Среднее арифметическое: 50.00220663
|
||||
array[100][100] = 0.7599664607041763
|
||||
Время выполнения: 174 мс
|
||||
```
|
||||
|
||||
### Для матрцы 15000*15000:
|
||||
|
||||
```
|
||||
Однопоточный алгоритм:
|
||||
array[100][100] = 4.0
|
||||
Среднее арифметическое: 49.99820225777778
|
||||
array[100][100] = 0.08000287649097934
|
||||
Время выполнения: 388 мс
|
||||
|
||||
Алгоритм с ThreadPoolExecutor:
|
||||
Среднее арифметическое: 49.99820225777778
|
||||
array[100][100] = 0.08000287649097934
|
||||
Время выполнения: 323 мс
|
||||
|
||||
Алгоритм с ForkJoinPool:
|
||||
Среднее арифметическое: 49.99820225777778
|
||||
array[100][100] = 0.08000287649097934
|
||||
Время выполнения: 379 мс
|
||||
```
|
||||
|
||||
## Вывод ##
|
||||
Пока матрицы небольших размеров, можно заметить, что многопоточные алгоритмы могут не иметь преимущества по времени выполнения перед однопоточным алгоритмом или даже быть хуже.
|
||||
Это происходит из-за того, что ThreadPool и ForkJoin затрачивают дополнительные ресурсы на создание и поддержание потоков.
|
||||
|
||||
По мере роста размеров матрицы, многопоточные алгоритмы показывают большую эффективность, чем однопоточный. Однопоточный алгоритм начинает работать медленнее из-за большого количества элементов.
|
||||
ThreadPool и ForkJoin начинают работать быстрее, так как дополнительные траты на поддержание потоков с лихвой компенсируются эффективностью многопоточного метода решения задачи.
|
||||
Reference in New Issue
Block a user