chernyshov_nikita_lab_6 #304

Closed
kitossy wants to merge 5 commits from chernyshov_nikita_lab_6 into main
50 changed files with 823 additions and 0 deletions

View File

@ -0,0 +1,22 @@
## Лабораторная работа №2
**App 1: Программа 4 - Количество символов в именах файлов из каталога /var/data**
- Формирует файл /var/result/data.txt так, что каждая строка файла - количество символов в именах файлов из каталога /var/data.
**App 2: Программа 3 - Количество чисел в последовательности**
- Ищет набольшее число из файла /var/result/data.txt и сохраняет количество таких чисел из последовательности в /var/result/result.txt.
**Структура проекта:**
1. В папках app_1, app_2 лежат выполняемые файлы .py и Dockerfile с нужным кодом.
2. В папке generator_of_data лежат выполняемые файлы для создания данных.
3. В папке data лежат файлы, длину имен которых нужно посчитать.
4. В папке result лежат файлы с результатами выполнения программ. data.txt - результат выполнения main.py (app_1), result.txt - результат выполнения main.py (app_2). Данные в result рассчитываются из данных data.
5. docker-compose.yml - для определения и управления контейнерами Docker.
**Команда для запуска** - docker-compose up
**Ссылка на видео:** https://vkvideo.ru/video286865610_456239225?list=ln-Xqg7SupM1CohXsGRZK

View File

@ -0,0 +1,20 @@
# Используем образ Python 3.9-slim как основу для нашего контейнера.
# slim-версия образа более компактная, что делает контейнер меньше.
FROM python:3.9-slim
# Устанавливаем рабочую директорию в контейнере как /app.
# Все последующие команды будут выполняться в этой директории.
WORKDIR /app
# Копируем файл main.py из текущей директории в директорию /app в контейнере.
COPY . /app
# Определяем команду, которая будет выполняться при запуске контейнера.
# В данном случае запускается Python-скрипт main.py.
CMD ["python", "main.py"]

View File

@ -0,0 +1,38 @@
import os
def get_file_with_most_lines(directory):
max_lines = 0
target_file = ""
for filename in os.listdir(directory):
filepath = os.path.join(directory, filename)
if os.path.isfile(filepath):
with open(filepath, 'r') as f:
line_count = sum(1 for _ in f)
if line_count > max_lines:
max_lines = line_count
target_file = filename
return target_file
def copy_file(src_directory, dest_directory, filename):
src_filepath = os.path.join(src_directory, filename)
dest_filepath = os.path.join(dest_directory, 'data.txt')
os.makedirs(dest_directory, exist_ok=True)
with open(src_filepath, 'r') as src_file:
with open(dest_filepath, 'w') as dest_file:
dest_file.write(src_file.read())
def main():
src_directory = '/var/data'
dest_directory = '/var/result'
target_file = get_file_with_most_lines(src_directory)
if target_file:
copy_file(src_directory, dest_directory, target_file)
print(f"File {target_file} copied to {dest_directory}/data.txt")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,7 @@
FROM python:3.9-slim
WORKDIR /app
COPY . /app
CMD ["python", "main.py"]

View File

@ -0,0 +1,25 @@
import os
def get_largest_number_from_file(filepath):
with open(filepath, 'r') as f:
numbers = [int(line.strip()) for line in f.readlines()]
return max(numbers)
def save_square_of_number(number, output_filepath):
result = number ** 2
with open(output_filepath, 'w') as f:
f.write(str(result))
def main():
input_filepath = '/var/result/data.txt'
output_filepath = '/var/result/result.txt'
if os.path.exists(input_filepath):
largest_number = get_largest_number_from_file(input_filepath)
save_square_of_number(largest_number, output_filepath)
print(f"Largest number squared: {largest_number}^2 saved to {output_filepath}")
else:
print(f"Input file {input_filepath} not found!")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,22 @@
version: '3'
services:
generator_of_data:
build:
context: ./generator_of_data
volumes:
- ./data:/var/data
app_1:
build:
context: ./app_1
volumes:
- ./data:/var/data
- ./result:/var/result
depends_on:
- generator_of_data
app_2:
build:
context: ./app_2
volumes:
- ./result:/var/result
depends_on:
- app_1

View File

@ -0,0 +1,7 @@
FROM python:3.9-slim
WORKDIR /app
COPY . /app
CMD ["python", "generate_data.py"]

View File

@ -0,0 +1,27 @@
import os
import random
def generate_random_files(directory, num_files, num_lines_per_file, min_value, max_value):
os.makedirs(directory, exist_ok=True)
for i in range(num_files):
file_path = os.path.join(directory, f"file_{i + 1}.txt")
with open(file_path, 'w') as f:
for _ in range(num_lines_per_file):
random_number = random.randint(min_value, max_value)
f.write(f"{random_number}\n")
print(f"Generated file: {file_path}")
def main():
data_directory = '/var/data'
num_files = 10
num_lines_per_file = 12
min_value = 1
max_value = 100
generate_random_files(data_directory, num_files, num_lines_per_file, min_value, max_value)
print(f"Generated {num_files} files in {data_directory}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,36 @@
## Лабораторная работа №3
### Цель:
* Реализовать два микросервиса, которые взаимодействуют друг с другом через синхронный обмен сообщениями (HTTP-запросы). Для доступа к микросервисам используется шлюз Nginx, реализованный с помощью Docker Compose.
### Технологии:
* Python: Язык программирования для реализации микросервисов.
* Flask: Фреймворк Python для создания веб-приложений, использован для создания REST API микросервисов.
* requests: Библиотека Python для отправки HTTP-запросов, использован для синхронного обмена сообщениями между микросервисами.
* flask_cors: Расширение Flask, которое позволяет микросервисам получать доступ к данным из других доменов.
* Docker: Технология контейнеризации для упаковки и запуска микросервисов.
* Docker Compose: Инструмент для определения и управления многоконтейнерными приложениями, использован для запуска микросервисов и шлюза Nginx.
* Nginx: Сетевой прокси-сервер, использован как шлюз для доступа к микросервисам.
### Функциональность:
#### Микросервис games-service:
* Реализует CRUD операции для игр (GET, POST, PUT, DELETE).
* Сохраняет данные о играх в памяти (в словаре games).
* Получает информацию о жанре из микросервиса genres-service через HTTP-запрос.
* Включает информацию о жанре в ответ JSON для игры.
#### Микросервис genres-service:
* Реализует CRUD операции для жанров (GET, POST, PUT, DELETE).
* Сохраняет данные о жанре в памяти (в словаре genres).
#### Шлюз Nginx:
* Перенаправляет HTTP-запросы на соответствующие микросервисы.
* Предоставляет единую точку входа для доступа к микросервисам.
### Запуск программы:
* Запуск команды docker-compose up --build
### Ссылка на видео:
https://vkvideo.ru/video286865610_456239226?list=ln-U577n85GB3sBxUtF34

View File

@ -0,0 +1,26 @@
version: '3.8'
services:
games_service:
build:
context: ./games_service
dockerfile: Dockerfile
ports:
- "5000:5000"
genres_service:
build:
context: ./genres_service
dockerfile: Dockerfile
ports:
- "5001:5001"
nginx:
image: nginx:latest
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "80:80"
depends_on:
- games_service
- genres_service

View File

@ -0,0 +1,10 @@
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

View File

@ -0,0 +1,53 @@
from flask import Flask, jsonify, request
import uuid
app = Flask(__name__)
games = {}
@app.route('/games', methods=['GET'])
def get_games():
return jsonify(list(games.values()))
@app.route('/games/<uuid:game_uuid>', methods=['GET'])
def get_game(game_uuid):
game = games.get(str(game_uuid))
if game:
return jsonify(game)
return jsonify({'error': 'Not found'}), 404
@app.route('/games', methods=['POST'])
def create_game():
data = request.get_json()
game_uuid = str(uuid.uuid4())
game = {
'uuid': game_uuid,
'name': data['name'],
'developer': data['developer'],
'genres': data.get('genres', []) # List of genre UUIDs
}
games[game_uuid] = game
return jsonify(game), 201
@app.route('/games/<uuid:game_uuid>', methods=['PUT'])
def update_game(game_uuid):
game = games.get(str(game_uuid))
if not game:
return jsonify({'error': 'Not found'}), 404
data = request.get_json()
game['name'] = data['name']
game['developer'] = data['developer']
game['genres'] = data.get('genres', [])
return jsonify(game)
@app.route('/games/<uuid:game_uuid>', methods=['DELETE'])
def delete_game(game_uuid):
if str(game_uuid) in games:
del games[str(game_uuid)]
return '', 204
return jsonify({'error': 'Not found'}), 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -0,0 +1 @@
Flask

View File

@ -0,0 +1,10 @@
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

View File

@ -0,0 +1,51 @@
from flask import Flask, jsonify, request
import uuid
app = Flask(__name__)
genres = {}
@app.route('/genres', methods=['GET'])
def get_genres():
return jsonify(list(genres.values()))
@app.route('/genres/<uuid:genre_uuid>', methods=['GET'])
def get_genre(genre_uuid):
genre = genres.get(str(genre_uuid))
if genre:
return jsonify(genre)
return jsonify({'error': 'Not found'}), 404
@app.route('/genres', methods=['POST'])
def create_genre():
data = request.get_json()
genre_uuid = str(uuid.uuid4())
genre = {
'uuid': genre_uuid,
'name': data['name'],
'description': data['description']
}
genres[genre_uuid] = genre
return jsonify(genre), 201
@app.route('/genres/<uuid:genre_uuid>', methods=['PUT'])
def update_genre(genre_uuid):
genre = genres.get(str(genre_uuid))
if not genre:
return jsonify({'error': 'Not found'}), 404
data = request.get_json()
genre['name'] = data['name']
genre['description'] = data['description']
return jsonify(genre)
@app.route('/genres/<uuid:genre_uuid>', methods=['DELETE'])
def delete_genre(genre_uuid):
if str(genre_uuid) in genres:
del genres[str(genre_uuid)]
return '', 204
return jsonify({'error': 'Not found'}), 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)

View File

@ -0,0 +1 @@
Flask

View File

@ -0,0 +1,11 @@
server {
listen 80;
location /games {
proxy_pass http://games_service:5000;
}
location /genres {
proxy_pass http://genres_service:5001;
}
}

View File

@ -0,0 +1,30 @@
import pika
import time
def callback(ch, method, properties, body):
print(f'Consumer 1 получил сообщение: {body.decode()}')
# Время задержки по условию
time.sleep(2)
print('Consumer 1 закончил обработку')
def consume_events_1():
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# Создание очереди
channel.queue_declare(queue='consumer1_queue')
# Привязка очереди
channel.queue_bind(exchange='beauty_salon_events', queue='consumer1_queue')
channel.basic_consume(queue='consumer1_queue', on_message_callback=callback, auto_ack=True)
print('Consumer 1 начал ожидать сообщения...')
channel.start_consuming()
if __name__ == "__main__":
consume_events_1()

View File

@ -0,0 +1,28 @@
import pika
def callback(ch, method, properties, body):
print(f'Consumer 2 получил сообщение: {body.decode()}')
# Обработка "нон-стопом"
print('Consumer 2 закончил обработку')
def consume_events_2():
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# Создание очереди
channel.queue_declare(queue='consumer2_queue')
# Привязка очереди
channel.queue_bind(exchange='beauty_salon_events', queue='consumer2_queue')
channel.basic_consume(queue='consumer2_queue', on_message_callback=callback, auto_ack=True)
print('Consumer 2 начал ожидать сообщения...')
channel.start_consuming()
if __name__ == "__main__":
consume_events_2()

View File

@ -0,0 +1,28 @@
import pika
import time
def publish_events():
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# Создание exchange типа fanout
channel.exchange_declare(exchange='beauty_salon_events', exchange_type='fanout')
events = [
"Test1",
"Test2",
"Test3",
"Test4",
"Test5"
]
while True:
event = events[int(time.time()) % len(events)]
channel.basic_publish(exchange='beauty_salon_events', routing_key='', body=event)
print(f'Отправлено: {event}')
time.sleep(1)
if __name__ == "__main__":
publish_events()

View File

@ -0,0 +1,56 @@
### Лабораторная работа №4
#### Задание
1. Установить брокер сообщений RabbitMQ.
2. Пройти уроки 1, 2 и 3 из RabbitMQ Tutorials на любом языке программирования.
3. Продемонстрировать работу брокера сообщений.
#### Описание работы программы:
- **Класс Publisher** успешно осуществляет отправку сообщений своим клиентам.
- **Класс Consumer1** принимает и обрабатывает сообщения с задержкой в 3 секунды, что можно заметить на видео.
- **Класс Consumer2** мгновенно принимает и обрабатывает сообщения.
#### Уроки
1. lesson1
![lesson1.png](lesson1.png)
2. lesson2
![lesson2.png](lesson2.png)
3. lesson3
![lesson3.png](lesson3.png)
## Работа с RabbitMQ Management UI
![img1.png](img1.png)
## Показания очереди queue_1 при одном запущенном экземпляре Consumer_1
![img2.png](img2.png)
## Показания очереди queue_2
![img3.png](img3.png)
## Показания очереди queue_1 при двух запущенных экземплярах Consumer_1
![img4.png](img4.png)
## Показания очереди queue_1 при трех запущенных экземплярах Consumer_1
![img5.png](img5.png)
## Диспетчер задач
![img6.png](img6.png)
## Видео
https://vkvideo.ru/video286865610_456239227?list=ln-EUw7H8FIzc8ZwX8rG7

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -0,0 +1,25 @@
import pika, sys, os
def main():
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
def callback(ch, method, properties, body):
print(f" [x] Received {body}")
channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('Interrupted')
try:
sys.exit(0)
except SystemExit:
os._exit(0)

View File

@ -0,0 +1,11 @@
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
channel.basic_publish(exchange='', routing_key='hello', body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View File

@ -0,0 +1,19 @@
import pika
import sys
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='task_queue', durable=True)
message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(
exchange='',
routing_key='task_queue',
body=message,
properties=pika.BasicProperties(
delivery_mode=pika.DeliveryMode.Persistent
))
print(f" [x] Sent {message}")
connection.close()

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python
import pika
import time
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='task_queue', durable=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(f" [x] Received {body.decode()}")
time.sleep(body.count(b'.'))
print(" [x] Done")
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue='task_queue', on_message_callback=callback)
channel.start_consuming()

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -0,0 +1,13 @@
import pika
import sys
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', exchange_type='fanout')
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='logs', routing_key='', body=message)
print(f" [x] Sent {message}")
connection.close()

View File

@ -0,0 +1,22 @@
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', exchange_type='fanout')
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange='logs', queue=queue_name)
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(f" [x] {body}")
channel.basic_consume(
queue=queue_name, on_message_callback=callback, auto_ack=True)
channel.start_consuming()

View File

@ -0,0 +1,58 @@
# Лабораторная работа 5: Умножение матриц
## Описание
**Цель работы** реализовать и сравнить производительность последовательного и параллельного алгоритмов умножения матриц на больших массивах данных.
### Задачи:
1. Разработать последовательный алгоритм умножения матриц.
2. Реализовать параллельный алгоритм с возможностью настройки числа потоков.
3. Провести тестирование обоих подходов на матрицах размером 100x100, 300x300 и 500x500.
4. Выполнить анализ производительности, изучив зависимость времени выполнения от размера матриц и количества потоков, и сформулировать выводы.
## Теоретическое обоснование
Операция умножения матриц широко применяется в задачах вычислительного характера, таких как обработка изображений, машинное обучение и физическое моделирование. Сложность умножения матриц размером
N×N составляет O(N^3), что приводит к резкому росту времени вычислений с увеличением размера матриц. Для ускорения работы можно использовать параллельные алгоритмы, распределяющие вычисления между несколькими потоками.
## Реализация
1. **Последовательный алгоритм:** Реализован в модуле sequential.py. Алгоритм вычисляет каждый элемент результирующей матрицы поэлементно, складывая произведения соответствующих элементов строк и столбцов исходных матриц.
2. **Параллельный алгоритм:** Представлен в модуле parallel.py. Он использует многопоточность, разделяя вычисления на несколько потоков, где каждый поток обрабатывает определённый блок строк результирующей матрицы. Пользователь может задавать количество потоков для оптимизации производительности с учётом размера матриц и доступных ресурсов.
## Результаты тестирования
Тестирование проводилось на матрицах размером 100x100, 300x300 и 500x500 с различным числом потоков. Было проанализировано влияние количества потоков на производительность алгоритма.
## Скриншоты результатов
### Результат бенчамарка при 1 потоке:
![img1.png](img1.png)
### Результат бенчамарка при 2 потоках:
![img2.png](img2.png)
### Результат бенчамарка при 4 потоках:
![img3.png](img3.png)
### Результат бенчамарка при 8 потоках:
![img4.png](img4.png)
## Выводы
1. **Эффективность параллельного алгоритма**: Использование многопоточности в параллельном алгоритме показало значительное ускорение по сравнению с последовательным методом, особенно на матрицах больших размеров. Например, для матриц размером 500x500 параллельный алгоритм с 4 потоками оказался более чем вдвое быстрее последовательного.
2. **Влияние количества потоков**: Увеличение числа потоков сокращает время выполнения, но этот эффект ограничен. Для небольших матриц (100x100) использование более 2 потоков практически не улучшает производительность. На матрицах среднего и большого размера (300x300 и 500x500) оптимальный результат достигался при использовании 4 потоков, так как это позволяло равномерно распределить вычислительную нагрузку.
3. **Закономерности и ограничения**: Эффективность параллельного умножения матриц ограничена накладными расходами на управление потоками. Для небольших задач эти расходы могут нивелировать преимущества параллелизации. Однако на больших данных задача лучше масштабируется, что делает параллельный подход значительно более выгодным.
4. **Рекомендации по использованию**: Для работы с большими матрицами в реальных задачах рекомендуется использовать параллельные алгоритмы, подбирая оптимальное число потоков в зависимости от объёма задачи и доступных вычислительных ресурсов.
## Заключение
Выполнение лабораторной работы показало, что параллельные алгоритмы значительно ускоряют умножение матриц на больших данных. Однако для достижения максимальной эффективности важно учитывать размер задачи и оптимально выбирать количество потоков. Полученные результаты подтверждают, что параллельный подход предпочтителен для работы с крупными матрицами, тогда как для небольших задач затраты на управление потоками могут свести его преимущества на нет.
## Видео
https://vkvideo.ru/video286865610_456239228?list=ln-RciNpMoyWby0uMIZoa

View File

@ -0,0 +1,27 @@
import time
import random
from matrix_multiplication.sequential import matrix_multiply_sequential
from matrix_multiplication.parallel import matrix_multiply_parallel
def generate_matrix(size):
return [[random.randint(0, 10) for _ in range(size)] for _ in range(size)]
def benchmark(matrix_size, num_threads):
A = generate_matrix(matrix_size)
B = generate_matrix(matrix_size)
start = time.time()
matrix_multiply_sequential(A, B)
sequential_time = time.time() - start
start = time.time()
matrix_multiply_parallel(A, B, num_threads)
parallel_time = time.time() - start
print(f"Размер матрицы: {matrix_size}x{matrix_size}")
print(f"Последовательное время: {sequential_time:.5f} сек")
print(f"Параллельное время ({num_threads} потоков): {parallel_time:.5f} сек")
if __name__ == "__main__":
for size in [100, 300, 500]:
benchmark(size, num_threads=8)

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,21 @@
from concurrent.futures import ThreadPoolExecutor
def matrix_multiply_parallel(A, B, num_threads=1):
n = len(A)
result = [[0] * n for _ in range(n)]
def worker(start, end):
for i in range(start, end):
for j in range(n):
result[i][j] = sum(A[i][k] * B[k][j] for k in range(n))
chunk_size = n // num_threads
with ThreadPoolExecutor(max_workers=num_threads) as executor:
futures = [
executor.submit(worker, i * chunk_size, (i + 1) * chunk_size)
for i in range(num_threads)
]
for future in futures:
future.result()
return result

View File

@ -0,0 +1,9 @@
def matrix_multiply_sequential(A, B):
n = len(A)
result = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
result[i][j] = sum(A[i][k] * B[k][j] for k in range(n))
return result

View File

@ -0,0 +1,36 @@
# Лабораторная работа 6: Определение детерминанта матрицы с помощью параллельных вычислений
## Описание
В лабраторной работе программа вычисляет определитель матрицы двумя способами: параллельным методом с использованием нескольких процессов и последовательным методом, а также сравнивает их производительность. Функция determinant_block вычисляет детерминант переданного блока матрицы с помощью функции np.linalg.det из библиотеки NumPy. Функция determinant_parallel разделяет исходную матрицу на блоки (строго по строкам и столбцам) в зависимости от количества процессов, заданного аргументом num_processes. После этого каждый блок обрабатывается отдельным процессом в пуле процессов (Pool), и вычисленные детерминанты блоков перемножаются, чтобы получить общий детерминант матрицы. Функция benchmark генерирует случайную квадратную матрицу заданного размера, измеряет время выполнения параллельного и последовательного вычисления детерминанта, а затем выводит результаты. Основная часть программы запускается, если файл выполняется напрямую. Используется модуль argparse для получения аргумента командной строки --processes, который задает количество процессов для параллельного вычисления (по умолчанию 4). В цикле for функция benchmark выполняется для матриц размеров 100x100, 300x300 и 500x500, сравнивая производительность последовательного и параллельного подходов.
## Результаты
В процессе тестирования были получены следующие результаты:
### Скриншоты результатов
#### Результат бенчамарка при 1 процессе:
![img1.png](img1.png)
#### Результат бенчамарка при 2 процессах:
![img2.png](img2.png)
#### Результат бенчамарка при 3 процессах:
![img3.png](img3.png)
#### Результат бенчамарка при 4 процессах:
![img4.png](img4.png)
## Выводы
1. **Производительность**: Для небольших матриц (например, 100x100) параллельное вычисление демонстрирует замедление по сравнению с последовательным методом из-за накладных расходов на создание и управление процессами.
2. **Эффективность**: С увеличением размера матриц (300x300 и 500x500) время выполнения параллельного алгоритма также растёт, что свидетельствует о снижении его эффективности при использовании большего числа процессов. В некоторых тестах, например, для матрицы 500x500 при использовании 3 и 4 процессов, параллельный алгоритм оказался медленнее последовательного.
## Ссылка на видео
https://vkvideo.ru/video286865610_456239229?list=ln-59QwPscwM3KZRLxg1E

View File

@ -0,0 +1,50 @@
import numpy as np
from multiprocessing import Pool
import time
import argparse
def determinant_block(matrix_block):
return np.linalg.det(matrix_block)
def determinant_parallel(matrix, num_processes):
size = matrix.shape[0]
step = size // num_processes
blocks = []
for i in range(num_processes):
start_row = i * step
end_row = start_row + step if i < num_processes - 1 else size
blocks.append(matrix[start_row:end_row, start_row:end_row])
pool = Pool(processes=num_processes)
dets = pool.map(determinant_block, blocks)
pool.close()
pool.join()
return np.prod(dets)
def benchmark(size, num_processes):
matrix = np.random.rand(size, size)
start = time.time()
det_parallel = determinant_parallel(matrix, num_processes)
end = time.time()
print(f"Матрица {size}x{size} с {num_processes} процессами заняла {end - start:.5f} сек (Параллельно)")
start = time.time()
det_seq = determinant_block(matrix)
end = time.time()
print(f"Матрица {size}x{size} последовательный вычисление заняло {end - start:.5f} сек (Последовательно)")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Вычисление детерминанта с параллельной обработкой")
parser.add_argument("--processes", type=int, default=4)
args = parser.parse_args()
sizes = [100, 300, 500]
for size in sizes:
benchmark(size, args.processes)

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB