From dd0d4e53c406c900af622f8dcd1714d5e80f6d6f Mon Sep 17 00:00:00 2001 From: Zyzf Date: Fri, 8 Nov 2024 21:25:22 +0400 Subject: [PATCH] done --- kalyshev_yan_lab_5/.gitignore | 1 + kalyshev_yan_lab_5/Dockerfile | 6 ++ .../MatrixMultiplicationBenchmark.java | 96 ++++++++++++++++++ kalyshev_yan_lab_5/Readme.md | 22 ++++ kalyshev_yan_lab_5/docker-compose.yml | 3 + kalyshev_yan_lab_5/images/benchmark.png | Bin 0 -> 20572 bytes 6 files changed, 128 insertions(+) create mode 100644 kalyshev_yan_lab_5/.gitignore create mode 100644 kalyshev_yan_lab_5/Dockerfile create mode 100644 kalyshev_yan_lab_5/MatrixMultiplicationBenchmark.java create mode 100644 kalyshev_yan_lab_5/Readme.md create mode 100644 kalyshev_yan_lab_5/docker-compose.yml create mode 100644 kalyshev_yan_lab_5/images/benchmark.png diff --git a/kalyshev_yan_lab_5/.gitignore b/kalyshev_yan_lab_5/.gitignore new file mode 100644 index 0000000..6b468b6 --- /dev/null +++ b/kalyshev_yan_lab_5/.gitignore @@ -0,0 +1 @@ +*.class diff --git a/kalyshev_yan_lab_5/Dockerfile b/kalyshev_yan_lab_5/Dockerfile new file mode 100644 index 0000000..825b5af --- /dev/null +++ b/kalyshev_yan_lab_5/Dockerfile @@ -0,0 +1,6 @@ +FROM eclipse-temurin:21 +RUN mkdir /opt/app +WORKDIR /opt/app +COPY MatrixMultiplicationBenchmark.java /opt/app +RUN ["javac", "/opt/app/MatrixMultiplicationBenchmark.java"] +CMD ["java", "MatrixMultiplicationBenchmark"] diff --git a/kalyshev_yan_lab_5/MatrixMultiplicationBenchmark.java b/kalyshev_yan_lab_5/MatrixMultiplicationBenchmark.java new file mode 100644 index 0000000..50936fc --- /dev/null +++ b/kalyshev_yan_lab_5/MatrixMultiplicationBenchmark.java @@ -0,0 +1,96 @@ +import java.util.concurrent.*; +import java.util.Random; +import java.util.List; +import java.util.ArrayList; + +public class MatrixMultiplicationBenchmark { + + // Обычное умножение матриц + public static int[][] multiplyMatrices(int[][] A, int[][] B) { + int n = A.length; + int[][] C = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + C[i][j] += A[i][k] * B[k][j]; + } + } + } + + return C; + } + + // Параллельное умножение матриц + public static int[][] multiplyMatricesParallel(int[][] A, int[][] B, int numThreads) + throws InterruptedException, ExecutionException { + int n = A.length; + int[][] C = new int[n][n]; + + ExecutorService executor = Executors.newFixedThreadPool(numThreads); + List> futures = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + final int row = i; + futures.add(executor.submit(() -> { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + C[row][j] += A[row][k] * B[k][j]; + } + } + })); + } + + // Ожидание завершения всех задач + for (Future future : futures) { + future.get(); + } + + executor.shutdown(); + + return C; + } + + // Генерация случайной матрицы + public static int[][] generateRandomMatrix(int size) { + Random random = new Random(); + int[][] matrix = new int[size][size]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + matrix[i][j] = random.nextInt(10); // случайные числа от 0 до 9 + } + } + return matrix; + } + + // Бенчмарк методов умножения матриц + public static void benchmarkMatrixMultiplication(int size) throws InterruptedException, ExecutionException { + int[][] A = generateRandomMatrix(size); + int[][] B = generateRandomMatrix(size); + + System.out.println("Матрица " + size + "x" + size); + + // Обычное умножение + long start = System.nanoTime(); + multiplyMatrices(A, B); + long end = System.nanoTime(); + System.out.println("Время обычного: " + (end - start) / 1_000_000 + " ms"); + + // Параллельное умножение + int numThreads = Runtime.getRuntime().availableProcessors(); + start = System.nanoTime(); + multiplyMatricesParallel(A, B, numThreads); + end = System.nanoTime(); + System.out.println("Время параллельного: " + (end - start) / 1_000_000 + " ms"); + + System.out.println(); + } + + public static void main(String[] args) throws InterruptedException, ExecutionException { + int[] sizes = { 100, 300, 500 }; + + for (int size : sizes) { + benchmarkMatrixMultiplication(size); + } + } +} diff --git a/kalyshev_yan_lab_5/Readme.md b/kalyshev_yan_lab_5/Readme.md new file mode 100644 index 0000000..d2c8c67 --- /dev/null +++ b/kalyshev_yan_lab_5/Readme.md @@ -0,0 +1,22 @@ +# Отчет. Лабораторная работа 5 + +## Описание + +В рамках лабораторной работы была создана программа для умножения матриц, используя как последовательный, так и параллельный алгоритмы. Эти алгоритмы реализованы независимыми. Для указания количества потоков в параллельном алгоритме нужно указать это число в качестве аргумента функции. По заданию требовалось измерить скорость выполнения алгоритмов на квадратных матрицах размером 100x100, 300x300 и 500x500. Результаты представлены на следующих изображениях: +![Результаты](./images/benchmark.png) + +## Как запустить + +Для того, чтобы запустить сервисы, необходимо выполнить следующие действия: + +1. Установить и запустить Docker Engine или Docker Desktop +2. Через консоль перейти в папку, в которой расположен файл docker-compose.yml +3. Выполнить команду для запуска контейнера: + +``` +docker compose up -d +``` + +## Видео-отчет + +Работоспособность лабораторной работы можно оценить в следующем [видео](). diff --git a/kalyshev_yan_lab_5/docker-compose.yml b/kalyshev_yan_lab_5/docker-compose.yml new file mode 100644 index 0000000..4772619 --- /dev/null +++ b/kalyshev_yan_lab_5/docker-compose.yml @@ -0,0 +1,3 @@ +services: + matrixbench: + build: ./ diff --git a/kalyshev_yan_lab_5/images/benchmark.png b/kalyshev_yan_lab_5/images/benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..8a5d8dd3d0940b6e8921afb01241c370ca786335 GIT binary patch literal 20572 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