Merge pull request 'ПИбд-22 Покладов Никита Николаевич Лабораторная №1' (#6) from krutonik/SSPR_25:pokladov_nikita_lab_1 into main

Reviewed-on: sevastyan_b/SSPR_25#6
Сейчас приму, но в отчете нужны конкретные данные о эффективности
This commit is contained in:
sevastyan_b 2025-02-27 22:30:51 +04:00
commit 2e06257c82
3 changed files with 494 additions and 0 deletions

View File

@ -0,0 +1,364 @@
import java.util.*;
import java.util.concurrent.*;
public class MatrixSorter {
public static char[][] generateMatrix(int rows, int cols) {
char[][] matrix = new char[rows][cols];
Random random = new Random();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = (char) random.nextInt(100);
}
}
return matrix;
}
public static char[][] copyMatrix(char[][] matrix) {
char[][] copy = new char[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;
}
public static void printMatrix(char[][] matrix) {
for (char[] row : matrix) {
for (char value : row) {
System.out.print((int) value + " ");
}
System.out.println();
}
}
public interface RowSumCalculator {
int[] calculateRowSums(char[][] matrix) throws Exception;
}
public interface RowSorter {
Integer[] sortIndices(int[] rowSums) throws Exception;
}
public static class SingleThreadRowSumCalculator implements RowSumCalculator {
@Override
public int[] calculateRowSums(char[][] matrix) {
int rows = matrix.length;
int[] sums = new int[rows];
for (int i = 0; i < rows; i++) {
int sum = 0;
for (char value : matrix[i]) {
sum += value;
}
sums[i] = sum;
}
return sums;
}
}
public static class ThreadPoolRowSumCalculator implements RowSumCalculator {
private ExecutorService executor;
public ThreadPoolRowSumCalculator(ExecutorService executor) {
this.executor = executor;
}
@Override
public int[] calculateRowSums(char[][] matrix) throws Exception {
int rows = matrix.length;
int[] sums = new int[rows];
@SuppressWarnings("unchecked")
Future<Integer>[] futures = new Future[rows];
for (int i = 0; i < rows; i++) {
final int row = i;
futures[i] = executor.submit(() -> {
int sum = 0;
for (char value : matrix[row]) {
sum += value;
}
return sum;
});
}
for (int i = 0; i < rows; i++) {
sums[i] = futures[i].get();
}
return sums;
}
}
public static class ForkJoinRowSumCalculator implements RowSumCalculator {
private static final int THRESHOLD = 100;
private ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
@Override
public int[] calculateRowSums(char[][] matrix) {
int rows = matrix.length;
int[] sums = new int[rows];
pool.invoke(new RowSumTask(matrix, 0, rows, sums));
return sums;
}
private static class RowSumTask extends RecursiveAction {
private char[][] matrix;
private int start, end;
private int[] sums;
public RowSumTask(char[][] matrix, int start, int end, int[] sums) {
this.matrix = matrix;
this.start = start;
this.end = end;
this.sums = sums;
}
@Override
protected void compute() {
if (end - start <= THRESHOLD) {
for (int i = start; i < end; i++) {
int sum = 0;
for (char value : matrix[i]) {
sum += value;
}
sums[i] = sum;
}
} else {
int mid = (start + end) / 2;
RowSumTask leftTask = new RowSumTask(matrix, start, mid, sums);
RowSumTask rightTask = new RowSumTask(matrix, mid, end, sums);
invokeAll(leftTask, rightTask);
}
}
}
}
public static class SingleThreadRowSorter implements RowSorter {
@Override
public Integer[] sortIndices(int[] rowSums) {
int n = rowSums.length;
Integer[] indices = new Integer[n];
for (int i = 0; i < n; i++) {
indices[i] = i;
}
mergeSort(indices, rowSums, 0, n - 1);
return indices;
}
private void mergeSort(Integer[] indices, int[] rowSums, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(indices, rowSums, left, mid);
mergeSort(indices, rowSums, mid + 1, right);
merge(indices, rowSums, left, mid, right);
}
}
private void merge(Integer[] indices, int[] rowSums, int left, int mid, int right) {
int leftSize = mid - left + 1;
int rightSize = right - mid;
Integer[] leftArr = new Integer[leftSize];
Integer[] rightArr = new Integer[rightSize];
System.arraycopy(indices, left, leftArr, 0, leftSize);
System.arraycopy(indices, mid + 1, rightArr, 0, rightSize);
int i = 0, j = 0, k = left;
while (i < leftSize && j < rightSize) {
if (rowSums[leftArr[i]] >= rowSums[rightArr[j]]) {
indices[k++] = leftArr[i++];
} else {
indices[k++] = rightArr[j++];
}
}
while (i < leftSize) {
indices[k++] = leftArr[i++];
}
while (j < rightSize) {
indices[k++] = rightArr[j++];
}
}
}
public static class ForkJoinRowSorter implements RowSorter {
private static final int THRESHOLD = 100;
private ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
@Override
public Integer[] sortIndices(int[] rowSums) {
int n = rowSums.length;
Integer[] indices = new Integer[n];
for (int i = 0; i < n; i++) {
indices[i] = i;
}
pool.invoke(new MergeSortTask(indices, rowSums, 0, n - 1));
return indices;
}
private static class MergeSortTask extends RecursiveAction {
private Integer[] indices;
private int[] rowSums;
private int left, right;
public MergeSortTask(Integer[] indices, int[] rowSums, int left, int right) {
this.indices = indices;
this.rowSums = rowSums;
this.left = left;
this.right = right;
}
@Override
protected void compute() {
if (right - left <= THRESHOLD) {
Arrays.sort(indices, left, right + 1,
Comparator.comparingInt((Integer i) -> rowSums[i]).reversed());
} else {
int mid = left + (right - left) / 2;
MergeSortTask leftTask = new MergeSortTask(indices, rowSums, left, mid);
MergeSortTask rightTask = new MergeSortTask(indices, rowSums, mid + 1, right);
invokeAll(leftTask, rightTask);
merge(indices, rowSums, left, mid, right);
}
}
private void merge(Integer[] indices, int[] rowSums, int left, int mid, int right) {
Integer[] temp = new Integer[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (rowSums[indices[i]] >= rowSums[indices[j]]) {
temp[k++] = indices[i++];
} else {
temp[k++] = indices[j++];
}
}
while (i <= mid) {
temp[k++] = indices[i++];
}
while (j <= right) {
temp[k++] = indices[j++];
}
System.arraycopy(temp, 0, indices, left, temp.length);
}
}
}
public static class ThreadPoolRowSorter implements RowSorter {
private ExecutorService executor;
private int THRESHOLD;
public ThreadPoolRowSorter(ExecutorService executor, int threshold) {
this.executor = executor;
this.THRESHOLD = threshold;
}
@Override
public Integer[] sortIndices(int[] rowSums) throws Exception {
int n = rowSums.length;
Integer[] indices = new Integer[n];
for (int i = 0; i < n; i++) {
indices[i] = i;
}
sort(indices, rowSums, 0, n - 1).get();
return indices;
}
private CompletableFuture<Void> sort(Integer[] indices, int[] rowSums, int left, int right) {
if (right - left <= THRESHOLD) {
Arrays.sort(indices, left, right + 1,
Comparator.comparingInt((Integer i) -> rowSums[i]).reversed());
return CompletableFuture.completedFuture(null);
} else {
int mid = left + (right - left) / 2;
CompletableFuture<Void> leftFuture = sort(indices, rowSums, left, mid);
CompletableFuture<Void> rightFuture = sort(indices, rowSums, mid + 1, right);
return CompletableFuture.allOf(leftFuture, rightFuture)
.thenRunAsync(() -> merge(indices, rowSums, left, mid, right), executor);
}
}
private void merge(Integer[] indices, int[] rowSums, int left, int mid, int right) {
Integer[] temp = new Integer[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (rowSums[indices[i]] >= rowSums[indices[j]]) {
temp[k++] = indices[i++];
} else {
temp[k++] = indices[j++];
}
}
while (i <= mid) {
temp[k++] = indices[i++];
}
while (j <= right) {
temp[k++] = indices[j++];
}
System.arraycopy(temp, 0, indices, left, temp.length);
}
}
public static class CombinedMatrixSorter {
private RowSumCalculator sumCalculator;
private RowSorter rowSorter;
public CombinedMatrixSorter(RowSumCalculator sumCalculator, RowSorter rowSorter) {
this.sumCalculator = sumCalculator;
this.rowSorter = rowSorter;
}
public long sort(char[][] matrix) throws Exception {
long startTime = System.currentTimeMillis();
int[] rowSums = sumCalculator.calculateRowSums(matrix);
Integer[] sortedIndices = rowSorter.sortIndices(rowSums);
long endTime = System.currentTimeMillis();
char[][] sortedMatrix = reorderMatrix(matrix, sortedIndices);
return endTime - startTime;
}
private char[][] reorderMatrix(char[][] matrix, Integer[] indices) {
char[][] sortedMatrix = new char[matrix.length][];
for (int i = 0; i < indices.length; i++) {
sortedMatrix[i] = matrix[indices[i]];
}
return sortedMatrix;
}
}
public static void main(String[] args) throws Exception {
int[] rowCounts = {10, 100, 1000, 10000, 100000, 1000000};
int[] colCounts = {1000000, 100000, 10000, 1000, 100, 10};
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Map<String, RowSumCalculator> sumCalculators = new LinkedHashMap<>();
sumCalculators.put("Single", new SingleThreadRowSumCalculator());
sumCalculators.put("ThreadPool", new ThreadPoolRowSumCalculator(executor));
sumCalculators.put("ForkJoin", new ForkJoinRowSumCalculator());
Map<String, RowSorter> rowSorters = new LinkedHashMap<>();
rowSorters.put("Single", new SingleThreadRowSorter());
rowSorters.put("ThreadPool", new ThreadPoolRowSorter(executor, 100));
rowSorters.put("ForkJoin", new ForkJoinRowSorter());
int trials = 10;
for (int sizeIndex = 0; sizeIndex < rowCounts.length; sizeIndex++) {
int rows = rowCounts[sizeIndex];
int cols = colCounts[sizeIndex];
System.out.printf("Matrix size: %d x %d%n", rows, cols);
for (Map.Entry<String, RowSumCalculator> sumEntry : sumCalculators.entrySet()) {
for (Map.Entry<String, RowSorter> sortEntry : rowSorters.entrySet()) {
long totalTime = 0;
for (int t = 0; t < trials; t++) {
char[][] matrix = generateMatrix(rows, cols);
CombinedMatrixSorter sorter = new CombinedMatrixSorter(sumEntry.getValue(), sortEntry.getValue());
long time = sorter.sort(matrix);
totalTime += time;
}
System.out.printf("Summation: %-10s | Sorting: %-10s -> Average Time: %d ms%n",
sumEntry.getKey(), sortEntry.getKey(), totalTime / trials);
}
}
System.out.println();
}
executor.shutdown();
}
}

View File

@ -0,0 +1,65 @@
# Лабораторная №1
Разработка многопоточного приложения с использованием Java Concurrency
согласно варианту задания.
Необходимо:
1 Разработать однопоточный вариант алгоритма и замерить время его работы.
2 Разработать параллельный вариант алгоритма с использованием
ThreadPoolExecutor и замерить время его работы
3 Разработать параллельный вариант алгоритма с использованием ForkJoinPoll
и замерить время его работы.
ВАЖНО: Массив генерируется до работы всех вариантов алгоритмов. Все три
алгоритма обрабатывают три одинаковых массива.
## Вариант и задание
17 Упорядочить строки матрицы по убыванию суммы элементов.
### Описание
Данная программа разработана для сравнения производительности однопоточного и многопоточного подходов к сортировке строк матрицы по убыванию суммы их элементов. Задача состоит из двух основных частей:
1. **Подсчёт сумм строк**: Для каждой строки матрицы вычисляется сумма её элементов.
2. **Сортировка строк**: Строки матрицы сортируются по убыванию суммы их элементов.
Эти этапы выполняются с разными вариантами многопоточности, чтобы выявить их влияние на производительность при различных размерах матрицы.
### Как запустить
```sh
javac MatrixSorter.java
java MatrixSorter
```
### Используемые технологии
Программа использует **Java Concurrency**, включая классы и интерфейсы:
- `ExecutorService`, `ThreadPoolExecutor`, `ForkJoinPool`, `Future`, `CompletableFuture`, `RecursiveAction` — для организации многопоточности.
### Что делает программа
1. Генерирует матрицу заданного размера, заполненную случайными значениями.
2. Вычисляет сумму элементов каждой строки.
3. Сортирует строки по убыванию их суммы.
4. Замеряет среднее время выполнения разных комбинаций подсчёта и сортировки.
---
## Анализ результатов
### Результаты в файле res.txt
### Маленькие матрицы (10 x 1000000, 100 x 100000)
- Использование многопоточности не даёт значительного прироста, так как оверхед на создание потоков превышает выгоду от распараллеливания.
- `ThreadPool` или `ForkJoin` для сортировки могут даже слегка ухудшать производительность из-за накладных расходов.
### Средние матрицы (1000 x 10000, 10000 x 1000)
- На этом размере многопоточное суммирование (`ThreadPool`, `ForkJoin`) начинает давать выгоду.
- `ForkJoin` работает стабильно, а `ThreadPool` может давать как ускорение, так и замедление.
- На сортировке многопоточность помогает, но эффект зависит от формы матрицы.
### Большие матрицы (100000 x 100, 1000000 x 10)
- Однопоточное выполнение становится крайне неэффективным.
- `ForkJoin` даёт лучшие результаты как при подсчёте, так и при сортировке.
- `ThreadPool` работает лучше однопоточного подхода, но хуже `ForkJoin`.
### Выводы
1. **Для маленьких матриц**: Однопоточное выполнение предпочтительнее, так как накладные расходы на многопоточность не окупаются.
2. **Для средних матриц**: Подсчёт можно распараллеливать (`ForkJoin` предпочтителен), сортировка зависит от формы матрицы.
3. **Для больших матриц**: Использование `ForkJoin` даёт наилучший результат как для подсчёта, так и для сортировки.
4. **Использование `ThreadPool`** может быть полезно, но его эффективность зависит от конкретных размеров матрицы.

View File

@ -0,0 +1,65 @@
Matrix size: 10 x 1000000
Summation: Single | Sorting: Single -> Average Time: 9 ms
Summation: Single | Sorting: ThreadPool -> Average Time: 11 ms
Summation: Single | Sorting: ForkJoin -> Average Time: 9 ms
Summation: ThreadPool | Sorting: Single -> Average Time: 19 ms
Summation: ThreadPool | Sorting: ThreadPool -> Average Time: 6 ms
Summation: ThreadPool | Sorting: ForkJoin -> Average Time: 7 ms
Summation: ForkJoin | Sorting: Single -> Average Time: 9 ms
Summation: ForkJoin | Sorting: ThreadPool -> Average Time: 7 ms
Summation: ForkJoin | Sorting: ForkJoin -> Average Time: 8 ms
Matrix size: 100 x 100000
Summation: Single | Sorting: Single -> Average Time: 7 ms
Summation: Single | Sorting: ThreadPool -> Average Time: 7 ms
Summation: Single | Sorting: ForkJoin -> Average Time: 7 ms
Summation: ThreadPool | Sorting: Single -> Average Time: 7 ms
Summation: ThreadPool | Sorting: ThreadPool -> Average Time: 7 ms
Summation: ThreadPool | Sorting: ForkJoin -> Average Time: 6 ms
Summation: ForkJoin | Sorting: Single -> Average Time: 7 ms
Summation: ForkJoin | Sorting: ThreadPool -> Average Time: 7 ms
Summation: ForkJoin | Sorting: ForkJoin -> Average Time: 8 ms
Matrix size: 1000 x 10000
Summation: Single | Sorting: Single -> Average Time: 7 ms
Summation: Single | Sorting: ThreadPool -> Average Time: 12 ms
Summation: Single | Sorting: ForkJoin -> Average Time: 10 ms
Summation: ThreadPool | Sorting: Single -> Average Time: 10 ms
Summation: ThreadPool | Sorting: ThreadPool -> Average Time: 9 ms
Summation: ThreadPool | Sorting: ForkJoin -> Average Time: 9 ms
Summation: ForkJoin | Sorting: Single -> Average Time: 7 ms
Summation: ForkJoin | Sorting: ThreadPool -> Average Time: 9 ms
Summation: ForkJoin | Sorting: ForkJoin -> Average Time: 7 ms
Matrix size: 10000 x 1000
Summation: Single | Sorting: Single -> Average Time: 11 ms
Summation: Single | Sorting: ThreadPool -> Average Time: 17 ms
Summation: Single | Sorting: ForkJoin -> Average Time: 13 ms
Summation: ThreadPool | Sorting: Single -> Average Time: 26 ms
Summation: ThreadPool | Sorting: ThreadPool -> Average Time: 19 ms
Summation: ThreadPool | Sorting: ForkJoin -> Average Time: 23 ms
Summation: ForkJoin | Sorting: Single -> Average Time: 8 ms
Summation: ForkJoin | Sorting: ThreadPool -> Average Time: 7 ms
Summation: ForkJoin | Sorting: ForkJoin -> Average Time: 7 ms
Matrix size: 100000 x 100
Summation: Single | Sorting: Single -> Average Time: 72 ms
Summation: Single | Sorting: ThreadPool -> Average Time: 69 ms
Summation: Single | Sorting: ForkJoin -> Average Time: 47 ms
Summation: ThreadPool | Sorting: Single -> Average Time: 146 ms
Summation: ThreadPool | Sorting: ThreadPool -> Average Time: 127 ms
Summation: ThreadPool | Sorting: ForkJoin -> Average Time: 109 ms
Summation: ForkJoin | Sorting: Single -> Average Time: 74 ms
Summation: ForkJoin | Sorting: ThreadPool -> Average Time: 59 ms
Summation: ForkJoin | Sorting: ForkJoin -> Average Time: 47 ms
Matrix size: 1000000 x 10
Summation: Single | Sorting: Single -> Average Time: 2088 ms
Summation: Single | Sorting: ThreadPool -> Average Time: 1305 ms
Summation: Single | Sorting: ForkJoin -> Average Time: 1203 ms
Summation: ThreadPool | Sorting: Single -> Average Time: 5506 ms
Summation: ThreadPool | Sorting: ThreadPool -> Average Time: 4021 ms
Summation: ThreadPool | Sorting: ForkJoin -> Average Time: 4943 ms
Summation: ForkJoin | Sorting: Single -> Average Time: 2497 ms
Summation: ForkJoin | Sorting: ThreadPool -> Average Time: 1739 ms
Summation: ForkJoin | Sorting: ForkJoin -> Average Time: 1435 ms