Merge pull request 'zhimolostnova_anna_lab_6' (#95) from zhimolostnova_anna_lab_6 into main

Reviewed-on: #95
This commit is contained in:
Alexey 2024-10-26 12:56:04 +04:00
commit 62290fc43d
7 changed files with 252 additions and 0 deletions

View File

@ -0,0 +1,87 @@
# Отчет по лабораторной работе №6
## Описание задачи
Данная работа нацелена на изучение эффективности параллельных вычислений при нахождении детерминанта
квадратной матрицы. Были реализованы два алгоритма:
1. **Последовательный алгоритм**: рекурсивное вычисление детерминанта методом разложения по строкам.
2. **Параллельный алгоритм**: вычисление детерминанта с использованием многопоточности,
где различные миноры матрицы вычисляются в отдельных потоках.
Целью эксперимента было сравнение времени выполнения последовательного и параллельного алгоритмов для
матриц разного размера и с разным количеством потоков.
## Структура проекта
Проект состоит из двух файлов с реализацией алгоритмов:
- regular.go (в папке alg) — рекурсивное вычисление детерминанта методом разложения по строкам.
- parallel.go (в папке alg) — вычисление детерминанта с использованием многопоточности,
где различные миноры матрицы вычисляются в отдельных потоках.
- matrix.go (в папке util) — вспомогательные функции для матриц.
- run.go - запуск бенчмарков.
## Результаты
### Последовательный алгоритм
![img.png](images%2Fimg.png)
| Размер матрицы | Время выполнения |
|----------------|------------------|
| 7x7 | 525.1µs |
| 8x8 | 5.8494ms |
| 9x9 | 35.3115ms |
### Параллельный алгоритм
![img_1.png](images%2Fimg_1.png)
| Размер матрицы | Количество потоков | Время выполнения |
|----------------|--------------------|------------------|
| 7x7 | 2 | 2ms |
| 7x7 | 4 | 2.0009ms |
| 7x7 | 6 | 1.0002ms |
| 7x7 | 8 | 1.9989ms |
| 8x8 | 2 | 5.0014ms |
| 8x8 | 4 | 21.5145ms |
| 8x8 | 6 | 16.3851ms |
| 8x8 | 8 | 17.9676ms |
| 9x9 | 2 | 65.099ms |
| 9x9 | 4 | 115.9553ms |
| 9x9 | 6 | 161.408ms |
| 9x9 | 8 | 117.4747ms |
## Анализ полученных данных
1. **Последовательный алгоритм**:
Последовательный алгоритм показал ожидаемую тенденцию: с увеличением размера матрицы, время выполнения
увеличивается экспоненциально. Это связано с тем, что сложность рекурсивного алгоритма вычисления
детерминанта составляет O(n!), где n — размер матрицы. Даже небольшое увеличение размера матрицы
приводит к значительному росту времени вычислений.
2. **Параллельный алгоритм**:
**Матрицы 7x7**:
- Время параллельного алгоритма оказалось больше, чем последовательного.
Это связано с тем, что накладные расходы на создание потоков, синхронизацию данных и распределение
задач превышают выгоду от параллельного выполнения для небольших задач. Матрица 7x7 слишком мала,
чтобы эффективно распределить вычисления между потоками, поэтому многопоточность не дает прироста в
производительности.
**Матрицы 8x8**:
- Параллельное вычисление снова оказалось медленнее последовательного. Время выполнения с увеличением
количества потоков увеличивалось вплоть до 4 потоков, что демонстрирует отрицательный эффект от
создания избыточного числа потоков для задач такого размера. Наибольшее ускорение удалось достичь с 2 и 6 потоками,
но даже в этом случае результат (5.0014 и 16.3851 ms) был хуже и менее стабильным последовательного (5.8494 ms).
**Матрицы 9x9**:
- Здесь также наблюдается значительное замедление при увеличении количества потоков. Время с 8 потоками
составило 117.4747 ms, что все равно больше времени последовательного алгоритма (35.3115 ms). Это связано с тем,
что при увеличении числа потоков на задаче, где каждая операция по вычислению минора требует больших
вычислительных затрат, накладные расходы на управление потоками могут стать слишком велики.
## Демонстрационное видео
Видеозапись доступна по адресу: [https://vk.com/video193898050_456240874](https://vk.com/video193898050_456240874)

View File

@ -0,0 +1,47 @@
package alg
import (
"sync"
"zhimolostnova_anna_lab_6/util"
)
func DeterminantParallel(matrix [][]float64, workers int) float64 {
size := len(matrix)
if size == 1 {
return matrix[0][0]
}
if size == 2 {
return matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]
}
det := 0.0
wg := sync.WaitGroup{}
mu := sync.Mutex{}
chunks := size / workers
if chunks == 0 {
chunks = 1
}
for j := 0; j < size; j += chunks {
wg.Add(1)
go func(jStart int) {
defer wg.Done()
localDet := 0.0
for jj := jStart; jj < jStart+chunks && jj < size; jj++ {
subMatrix := util.GetMinor(matrix, jj)
sign := 1
if jj%2 != 0 {
sign = -1
}
localDet += float64(sign) * matrix[0][jj] * DeterminantParallel(subMatrix, workers)
}
mu.Lock()
det += localDet
mu.Unlock()
}(j)
}
wg.Wait()
return det
}

View File

@ -0,0 +1,30 @@
package alg
import (
"zhimolostnova_anna_lab_6/util"
)
// Determinant Функция для вычисления детерминанта матрицы
func Determinant(matrix [][]float64) float64 {
size := len(matrix)
if size == 1 {
return matrix[0][0]
}
if size == 2 {
return matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]
}
// Рекурсивное вычисление детерминанта по разложению по первой строке
det := 0.0
for j := 0; j < size; j++ {
subMatrix := util.GetMinor(matrix, j)
sign := 1
if j%2 != 0 {
sign = -1
}
det += float64(sign) * matrix[0][j] * Determinant(subMatrix)
}
return det
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,52 @@
package main
import (
"fmt"
"strconv"
"time"
"zhimolostnova_anna_lab_6/alg"
"zhimolostnova_anna_lab_6/util"
)
// Функция для бенчмарка последовательного вычисления детерминанта
func benchmarkDeterminantSequential(sizes []int) {
for _, size := range sizes {
matrix := util.GenerateMatrix(size)
start := time.Now()
_ = alg.Determinant(matrix)
elapsed := time.Since(start)
fmt.Printf("Sequential determinant of matrix %sx%s took %s\n", strconv.Itoa(size), strconv.Itoa(size), elapsed)
}
}
// Функция для бенчмарка параллельного вычисления детерминанта
func benchmarkDeterminantParallel(sizes []int, threadsList []int) {
for _, size := range sizes {
for _, threads := range threadsList {
matrix := util.GenerateMatrix(size)
start := time.Now()
_ = alg.DeterminantParallel(matrix, threads)
elapsed := time.Since(start)
fmt.Printf("Parallel determinant of matrix %sx%s with %d threads took %s\n", strconv.Itoa(size), strconv.Itoa(size), threads, elapsed)
}
}
}
func main() {
// Список размерностей матриц
sizes := []int{7, 8, 9}
// Список количества потоков для тестирования
threadsList := []int{2, 4, 6, 8}
// Запуск бенчмарков
fmt.Println("Sequential Benchmark:")
benchmarkDeterminantSequential(sizes)
fmt.Println("\nParallel Benchmark:")
benchmarkDeterminantParallel(sizes, threadsList)
}

View File

@ -0,0 +1,36 @@
package util
import "math/rand"
// GetMinor Получение минора матрицы
func GetMinor(matrix [][]float64, col int) [][]float64 {
size := len(matrix)
minor := make([][]float64, size-1)
for i := range minor {
minor[i] = make([]float64, size-1)
}
for i := 1; i < size; i++ {
subCol := 0
for j := 0; j < size; j++ {
if j == col {
continue
}
minor[i-1][subCol] = matrix[i][j]
subCol++
}
}
return minor
}
// GenerateMatrix Генерация случайной матрицы размера size x size
func GenerateMatrix(size int) [][]float64 {
matrix := make([][]float64, size)
for i := range matrix {
matrix[i] = make([]float64, size)
for j := range matrix[i] {
matrix[i][j] = float64(rand.Intn(6))
}
}
return matrix
}