anisin_ruslan_lab_6 is ready

This commit is contained in:
russell
2025-12-15 13:27:20 +04:00
parent de37e35815
commit a598ec9cfe
5 changed files with 230 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
FROM golang:alpine3.22
WORKDIR /app
COPY main.go .
RUN go build -o matrix-det main.go
ENTRYPOINT ["./matrix-det"]

View File

@@ -0,0 +1,62 @@
# Лабораторная работа №6
## Использованные технологии
- Go
## Что делает
Нахождит детерминант квадратной матрицы (последовательно или параллельно)
## Запуск
Программа работает в двух режимах: **Normal** (обычный) и **Benchmark** (тестирование производительности). Режим переключается флагом `-mode`.
### 1. Обычный режим (по умолчанию)
Позволяет вручную задать размер матрицы и количество потоков.
**Флаги:**
- `-size`: Размер квадратной матрицы (N x N). По умолчанию: 300.
- `-threads`: Количество потоков (горутин). По умолчанию: 4.
### 2. Режим бенчмарка
Автоматически запускает серию тестов для матриц размером 100x100, 300x300 и 500x500 с различным количеством потоков (1, 2, 4, 6, 8, 12).
1. Установите Docker.
2. Клонируйте репозиторий.
3. В терминале перейдите в папку anisin_ruslan_lab_5 и выполните команду:
```bash
docker build -t matrix-det .
```
4. Примеры возможных комманды для запуска представлены ниже:
**Запуск с параметрами по умолчанию:**
```bash
docker run --rm matrix-det
```
**Обычный режим (свои параметры):**
```bash
docker run --rm matrix-det -size=500 -threads=8
```
**Режим бенчмарка:**
```bash
docker run --rm matrix-det -mode=benchmark
```
### Результат запуска в режиме бенчмарка
![benchmark_result](benchmark_result.png)
## Анализ результатов
**100x100:** Результаты крайне нестабильны. Использование 12 потоков сделало работу в 2 раза медленнее (1.45ms), чем в 1 поток (0.6ms). Накладные расходы на синхронизацию здесь вредны.
**300x300:** Пик эффективности достигнут на 4 потоках (4.9ms). Дальнейшее увеличение числа потоков только ухудшает результат, возвращая время к показателям однопоточного режима.
**500x500:** Лучший результат сместился на 6 потоков (18.8ms, ускорение более чем в 2 раза), однако 8 и 12 потоков снова показывают падение производительности.
**Вывод:** В отличие от умножения матриц, алгоритм требует остановки и синхронизации всех потоков после обработки каждого столбца. Слишком большое количество потоков создает высокие накладные расходы на ожидание. Оптимально использовать 4-6 потоков.
## Видео
[Ссылка](https://drive.google.com/file/d/1F7ZW6GayRJe_n7auAdlZw_MzUDwPrjP5/view)

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -0,0 +1,3 @@
module anisin_ruslan_lab_6
go 1.24.4

156
anisin_ruslan_lab_6/main.go Normal file
View File

@@ -0,0 +1,156 @@
package main
import (
"flag"
"fmt"
"math"
"math/rand"
"runtime"
"sync"
"time"
)
type Matrix [][]int
func GenerateMatrix(n int) Matrix {
m := make(Matrix, n)
for i := 0; i < n; i++ {
m[i] = make([]int, n)
for j := 0; j < n; j++ {
m[i][j] = rand.Intn(10)
}
}
return m
}
func CalculateDeterminant(m Matrix, workers int) float64 {
n := len(m)
temp := make([][]float64, n)
for i := 0; i < n; i++ {
temp[i] = make([]float64, n)
for j := 0; j < n; j++ {
temp[i][j] = float64(m[i][j])
}
}
detSign := 1.0
for k := 0; k < n; k++ {
pivotIndex := k
maxVal := math.Abs(temp[k][k])
for i := k + 1; i < n; i++ {
if math.Abs(temp[i][k]) > maxVal {
maxVal = math.Abs(temp[i][k])
pivotIndex = i
}
}
if math.Abs(temp[pivotIndex][k]) < 1e-9 {
return 0
}
if pivotIndex != k {
temp[k], temp[pivotIndex] = temp[pivotIndex], temp[k]
detSign = -detSign
}
rowsToProcess := n - (k + 1)
if rowsToProcess <= 0 {
continue
}
if workers == 1 || rowsToProcess < workers {
for i := k + 1; i < n; i++ {
processRow(temp, n, k, i)
}
} else {
var wg sync.WaitGroup
rowsPerWorker := (rowsToProcess + workers - 1) / workers
startIndex := k + 1
for w := 0; w < workers; w++ {
start := startIndex + w*rowsPerWorker
end := start + rowsPerWorker
if start >= n {
break
}
if end > n {
end = n
}
wg.Add(1)
go func(s, e int) {
defer wg.Done()
for i := s; i < e; i++ {
processRow(temp, n, k, i)
}
}(start, end)
}
wg.Wait()
}
}
det := 1.0
for i := 0; i < n; i++ {
det *= temp[i][i]
}
return det * detSign
}
func processRow(m [][]float64, n, pivotIdx, rowIdx int) {
factor := m[rowIdx][pivotIdx] / m[pivotIdx][pivotIdx]
for j := pivotIdx; j < n; j++ {
m[rowIdx][j] -= factor * m[pivotIdx][j]
}
}
func runOnce(size, workers int) time.Duration {
A := GenerateMatrix(size)
runtime.GC()
start := time.Now()
_ = CalculateDeterminant(A, workers)
return time.Since(start)
}
func runBenchmarkMode() {
sizes := []int{100, 300, 500}
threadCounts := []int{1, 2, 4, 6, 8, 12}
for i, size := range sizes {
for _, workers := range threadCounts {
duration := runOnce(size, workers)
fmt.Printf("Size: %d Threads: %d Time: %v\n", size, workers, duration)
}
if i < len(sizes)-1 {
fmt.Println("---")
}
}
}
func runNormalMode(size, workers int) {
fmt.Printf("Generating matrix %dx%d...\n", size, size)
A := GenerateMatrix(size)
fmt.Printf("Calculating determinant using %d threads...\n", workers)
start := time.Now()
det := CalculateDeterminant(A, workers)
duration := time.Since(start)
fmt.Printf("Determinant: %.4g\n", det)
fmt.Printf("Done. Time elapsed: %v\n", duration)
}
func main() {
mode := flag.String("mode", "normal", "Mode: 'normal' or 'benchmark'")
size := flag.Int("size", 300, "Matrix size (for normal mode)")
threads := flag.Int("threads", 4, "Number of threads (for normal mode)")
flag.Parse()
if *mode == "benchmark" {
runBenchmarkMode()
} else {
runNormalMode(*size, *threads)
}
}