Merge pull request 'PIBD-22-lyakhov_timofey_lab_1_ready' (#42) from funa4i/SSPR_25:lyakhov_timofey_lab_1 into main

Reviewed-on: #42
This commit was merged in pull request #42.
This commit is contained in:
2025-03-28 21:51:40 +04:00
4 changed files with 342 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
FROM openjdk:21-jdk-slim
COPY target/sspr-1.0-SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

View File

@@ -0,0 +1,186 @@
# Лабораторная работа №1
## Описание
Программа вычисляет сумму произведений строк квадратной матрицы с использованием трех подходов:
- Однопоточная реализация
- Многопоточная реализация с использованием ThreadPool
- Реализация с использованием ForkJoinPool
Цель — сравнить эффективность различных способов многопоточной обработки данных и замерить время выполнения для каждого подхода.
## Как запустить лабораторную работу
1. Соберите проект и получите `.jar` файл:
```bash
mvn package
```
Либо перейдите сразу к шагу 2, если `.jar` уже собран.
2. Соберите Docker-образ:
```bash
docker build -t app -f .dockerfile .
```
3. Запустите контейнер:
```bash
docker run -i app
```
## Используемые технологии
- **Java**
- **Maven** — для сборки проекта
- **Docker** — для упаковки и запуска приложения в контейнере
- **Параллельное программирование**:
- `ExecutorService` (ThreadPool)
- `ForkJoinPool` (пул задач с рекурсивным делением данных)
## Что делает программа
- Заполняет матрицу случайными числами от 1 до 100.
- Вычисляет сумму произведений элементов в каждой строке матрицы тремя способами:
1. **Однопоточная реализация** — классический линейный перебор строк.
2. **ThreadPool** — распределение строк по пулу потоков.
3. **ForkJoinPool** — разбиение задачи на подзадачи с использованием `RecursiveTask`.
- Для каждого способа выводит время выполнения в наносекундах.
```java
public static void calculateSumOfRow() { // Фнукция подсчета в одном потоке
LocalDateTime startTime = LocalDateTime.now();
long s = System.nanoTime();
int sum = 0;
for (int[] ints : matrix) {
int product = 1;
for (int anInt : ints) {
product *= anInt;
}
sum += product;
}
long st = System.nanoTime() - s;
System.out.println("Время выполнения в одном потоке: \t" + st);
}
public static void calculateSumOfRowWithThreadPool() { // Фнукция подсчета с помощью ThreadPool
int sum = 0;
int numThreads = Runtime.getRuntime().availableProcessors(); // Определение количества потоков на основании процессора
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future<Integer>> futures = new ArrayList<>();
long s = System.nanoTime();
for (int i = 0; i < matrix.length; i++) { // Создание задач выполнения
final int row = i;
futures.add(executor.submit(() -> {
int product = 1;
for (int j = 0; j < matrix[row].length; j++) {
product *= matrix[row][j];
}
return product;
}));
}
for (Future<Integer> future : futures) { // Подсчет всех значений
try {
sum += future.get();
} catch (InterruptedException | ExecutionException e) {
System.out.println(e.getMessage());
}
}
executor.shutdown();
long st = System.nanoTime() - s;
System.out.println("Время выполнения с ThreadPool: \t\t" + st);
}
public static void calculateSumOfRowWithForkPool(){ // ПОдсчет через ForkPool
PartTask pt = new PartTask(matrix, 0, size + 1); // Создание сущности парта
ForkJoinPool forkJoinPool = new ForkJoinPool();
long s = System.nanoTime();
forkJoinPool.invoke(pt);
long st = System.nanoTime() - s;
System.out.println("Время выполнения с ForkJoinPool: \t" + st);
}
}
class PartTask extends RecursiveTask<Integer> { // Класс задачи
private final int[][] matrix;
private final int start, stop;
public PartTask(int[][] matrix, int start, int stop) {
this.matrix = matrix;
this.start = start;
this.stop = stop;
}
@Override
protected Integer compute() { // Вычисление части парта(Использование рекурсий)
if (start + 1 == stop) {
return Arrays.stream(matrix[start]).sum();
}
PartTask p1 = new PartTask(matrix, start, (start + stop) / 2);
PartTask p2 = new PartTask(matrix, (start + stop) / 2, stop);
p1.fork();
p2.fork();
return p1.join() + p2.join();
}
}
```
### Особенности кода
- Используется замер времени выполнения с помощью `System.nanoTime()`.
- ThreadPool автоматически подстраивается под количество доступных ядер CPU.
- Реализация с ForkJoinPool демонстрирует работу паттерна divide-and-conquer (разделяй и властвуй).
- Для ForkJoinPool реализован собственный класс `PartTask`, унаследованный от `RecursiveTask<Integer>`.
## Тесты (Примеры ввода и вывода)
### Тест 1:
**Ввод:**
```
Введите размер матрицы (от 2 до 1000): 10
```
**Вывод:**
```
Время выполнения в одном потоке: 2800
Время выполнения с ThreadPool: 1793455
Время выполнения с ForkJoinPool: 599719
```
### Тест 2:
**Ввод:**
```
Введите размер матрицы (от 2 до 1000): 100
```
**Вывод:**
```
Время выполнения в одном потоке: 66801
Время выполнения с ThreadPool: 5646255
Время выполнения с ForkJoinPool: 568506
```
### Тест 3:
**Ввод:**
```
Введите размер матрицы (от 2 до 1000): 1000
```
**Вывод:**
```
Время выполнения в одном потоке: 2352923
Время выполнения с ThreadPool: 14560843
Время выполнения с ForkJoinPool: 917609
```
## Вывод
В рамках лабораторной работы были реализованы и протестированы три подхода к параллельной обработке данных. ForkJoinPool показал лучшее соотношение скорости и масштабируемости при увеличении размера матрицы, особенно по сравнению с классическим ThreadPool.
Были закреплены навыки работы с потоками, параллельными вычислениями и измерением производительности различных подходов.

View File

@@ -0,0 +1,31 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.urlshort</groupId>
<artifactId>sspr</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sspr</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>org.urlshort.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,121 @@
package org.urlshort;
import java.time.LocalDateTime;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.*;
public class App
{
private static int[][] matrix;
private static int size;
public static void main( String[] args ) {
do {
Scanner scanner = new java.util.Scanner(System.in);
Random random = new java.util.Random();
System.out.print("Введите размер матрицы (от 2 до 1000): ");
int size = scanner.nextInt();
if (size < 2 || size > 10000) {
System.out.println("Размер матрицы должен быть от 2 до 1000.");
return;
}
matrix = new int[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(100) + 1;
}
}
calculateSumOfRow();
calculateSumOfRowWithThreadPool();
calculateSumOfRowWithForkPool();
} while (true);
}
public static void calculateSumOfRow() {
LocalDateTime startTime = LocalDateTime.now();
long s = System.nanoTime();
int sum = 0;
for (int[] ints : matrix) {
int product = 1;
for (int anInt : ints) {
product *= anInt;
}
sum += product;
}
long st = System.nanoTime() - s;
System.out.println("Время выполнения в одном потоке: \t" + st);
}
public static void calculateSumOfRowWithThreadPool() {
int sum = 0;
int numThreads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future<Integer>> futures = new ArrayList<>();
long s = System.nanoTime();
for (int i = 0; i < matrix.length; i++) {
final int row = i;
futures.add(executor.submit(() -> {
int product = 1;
for (int j = 0; j < matrix[row].length; j++) {
product *= matrix[row][j];
}
return product;
}));
}
for (Future<Integer> future : futures) {
try {
sum += future.get();
} catch (InterruptedException | ExecutionException e) {
System.out.println(e.getMessage());
}
}
executor.shutdown();
long st = System.nanoTime() - s;
System.out.println("Время выполнения с ThreadPool: \t\t" + st);
}
public static void calculateSumOfRowWithForkPool(){
PartTask pt = new PartTask(matrix, 0, size + 1);
ForkJoinPool forkJoinPool = new ForkJoinPool();
long s = System.nanoTime();
forkJoinPool.invoke(pt);
long st = System.nanoTime() - s;
System.out.println("Время выполнения с ForkJoinPool: \t" + st);
}
}
class PartTask extends RecursiveTask<Integer> {
private final int[][] matrix;
private final int start, stop;
public PartTask (int[][] matrix, int start, int stop){
this.matrix = matrix;
this.start = start;
this.stop = stop;
}
@Override
protected Integer compute() {
if (start + 1 == stop){
return Arrays.stream(matrix[start]).sum();
}
PartTask p1 = new PartTask(matrix, start, (start + stop) / 2);
PartTask p2 = new PartTask(matrix, (start + stop) / 2, stop);
p1.fork();
p2.fork();
return p1.join() + p2.join();
}
}