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:
4
lyakhov_timofey_lab_1/.dockerfile
Normal file
4
lyakhov_timofey_lab_1/.dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM openjdk:21-jdk-slim
|
||||
COPY target/sspr-1.0-SNAPSHOT.jar app.jar
|
||||
|
||||
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
||||
186
lyakhov_timofey_lab_1/README.md
Normal file
186
lyakhov_timofey_lab_1/README.md
Normal 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.
|
||||
|
||||
Были закреплены навыки работы с потоками, параллельными вычислениями и измерением производительности различных подходов.
|
||||
|
||||
31
lyakhov_timofey_lab_1/pom.xml
Normal file
31
lyakhov_timofey_lab_1/pom.xml
Normal 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>
|
||||
121
lyakhov_timofey_lab_1/src/main/java/org/urlshort/App.java
Normal file
121
lyakhov_timofey_lab_1/src/main/java/org/urlshort/App.java
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user