This commit is contained in:
2025-10-22 20:03:52 +04:00
parent 8d3a5c4fbc
commit 48afaceb98
2 changed files with 79 additions and 90 deletions

View File

@@ -31,54 +31,31 @@ python matrix.py
``` ```
matrix.py # Основной файл с реализацией алгоритмов matrix.py # Основной файл с реализацией алгоритмов
README.md # Документация проекта README.md # Документация проекта
``` ```
## 4. Результаты тестирования ## 4. Результаты тестирования
### Матрицы 100×100 На всех тестах одинаковый результат:
Детерминант при использовании множества потоков(процессов) высчитывается дольше чем при использовании строго 1 потока (главного). Это происходит из-за необходимости тратить время на выделение потоков(процессов), а также необходимости объединять результаты
#### Результат: # Сводная таблица результатов
| Алгоритм | Время (сек) | Ускорение | | Размер | Потоки | Время (сек) | Ускорение |
|----------|-------------|-----------| |--------|---------|-------------|-----------|
| Последовательный (LU) | 0.01-0.03 | 1.00x | | 100 | 1 | 0.0100 | 1.00 |
| Параллельный (2 процесса) | 2.5-3.5 | 0.01x | | 100 | 2 | 0.3408 | 0.03 |
| Параллельный (4 процесса) | 1.8-2.8 | 0.01x | | 100 | 4 | 0.3861 | 0.03 |
| Параллельный (8 процессов) | 1.5-2.5 | 0.01x | | 100 | 12 | 0.5920 | 0.02 |
| 300 | 1 | 0.0783 | 1.00 |
| 300 | 2 | 1.0641 | 0.07 |
| 300 | 4 | 1.2565 | 0.06 |
| 300 | 12 | 2.1755 | 0.04 |
| 500 | 1 | 0.2195 | 1.00 |
| 500 | 2 | 3.0588 | 0.07 |
| 500 | 4 | 3.0725 | 0.07 |
| 500 | 12 | 4.7439 | 0.05 |
#### Вывод: ## 5. Видео
На матрицах 100×100 параллелизм **крайне неэффективен**. Overhead от создания процессов (2-3 сек) во много раз превышает время самих вычислений (0.01-0.03 сек). Последовательный LU-алгоритм работает мгновенно и является оптимальным выбором.
### Матрицы 300×300 ВидеоСсылка на видео работы алгоритма: [Rutube](https://rutube.ru/video/private/b655cb0028629067f79920162bf3b199/?p=l5uLlZQUmoEgv6RSAm4cVA)
#### Результат:
| Алгоритм | Время (сек) | Ускорение |
|----------|-------------|-----------|
| Последовательный (LU) | 0.15-0.25 | 1.00x |
| Параллельный (2 процесса) | 45-55 | 0.004x ❌ |
| Параллельный (4 процесса) | 25-35 | 0.007x ❌ |
| Параллельный (8 процессов) | 15-20 | 0.01x ❌ |
#### Вывод:
Даже на матрицах 300×300 параллелизм через разложение по строке остается **неэффективным**. LU-разложение работает за доли секунды, в то время как параллельное разложение требует десятков секунд на вычисление 300 миноров размером 299×299.
### Матрицы 500×500
#### Результат:
| Алгоритм | Время (сек) | Ускорение | Детерминант |
|----------|-------------|-----------|-------------|
| Последовательный (LU) | 0.5-0.8 | 1.00x | ~10¹⁰⁰⁰ |
| Параллельный (2 процесса) | 180-220 | 0.003x ❌ | ~10¹⁰⁰⁰ |
| Параллельный (4 процесса) | 100-130 | 0.006x ❌ | ~10¹⁰⁰⁰ |
| Параллельный (8 процессов) | 60-80 | 0.009x ❌ | ~10¹⁰⁰⁰ |
#### Вывод:
На больших матрицах 500×500 ситуация **не улучшается**. LU-разложение остается самым эффективным (менее 1 секунды), в то время как параллельное разложение по строке требует минут работы.
## 13. Видео
ВидеоСсылка на видео работы алгоритма: [Rutube](https://rutube.ru/video/private/d4dc5613005afbbe601862d54b248d36/?p=De8RTMImQZQtab3avfH2Zg)

View File

@@ -1,9 +1,6 @@
import numpy as np import numpy as np
import multiprocessing as mp import multiprocessing as mp
import time import time
import warnings
warnings.filterwarnings('ignore')
def sequential_determinant(matrix): def sequential_determinant(matrix):
n = matrix.shape[0] n = matrix.shape[0]
@@ -79,27 +76,70 @@ def parallel_determinant(matrix, num_processes = None):
num_processes = mp.cpu_count() num_processes = mp.cpu_count()
n = matrix.shape[0] n = matrix.shape[0]
if n < 100 or num_processes == 1: if n < 100 or num_processes == 1:
return sequential_determinant(matrix) return sequential_determinant(matrix)
A = matrix.astype(np.float64).copy()
sign = 1
with mp.Pool(num_processes) as pool:
for i in range(n):
max_row = i
max_val = abs(A[i, i])
for k in range(i + 1, n):
if abs(A[k, i]) > max_val:
max_row = k
max_val = abs(A[k, i])
if max_row != i:
A[[i, max_row]] = A[[max_row, i]]
sign *= -1
if abs(A[i, i]) < 1e-15:
return 0.0, -np.inf
rows_to_update = list(range(i + 1, n))
if len(rows_to_update) >= num_processes * 2:
tasks = []
for k in rows_to_update:
tasks.append((A[k].copy(), A[i].copy(), A[k, i], A[i, i], i))
updated_rows = pool.map(_update_row_worker, tasks)
for idx, k in enumerate(rows_to_update):
A[k] = updated_rows[idx]
else:
for k in rows_to_update:
factor = A[k, i] / A[i, i]
A[k, i:] -= factor * A[i, i:]
log_abs_det = 0.0
for i in range(n):
if A[i, i] < 0:
sign *= -1
log_abs_det += np.log(abs(A[i, i]))
return sign, log_abs_det
def _update_row_worker(args):
row_k, row_i, a_ki, a_ii, i = args
import os import os
old_threads = os.environ.get('OMP_NUM_THREADS', None) os.environ['OMP_NUM_THREADS'] = '1'
os.environ['OMP_NUM_THREADS'] = str(num_processes) os.environ['MKL_NUM_THREADS'] = '1'
os.environ['MKL_NUM_THREADS'] = str(num_processes)
os.environ['NUMEXPR_NUM_THREADS'] = str(num_processes)
result = sequential_determinant(matrix) factor = a_ki / a_ii
if old_threads:
os.environ['OMP_NUM_THREADS'] = old_threads
else:
os.environ.pop('OMP_NUM_THREADS', None)
return result row_k[i:] -= factor * row_i[i:]
return row_k
def generate_stable_matrix(size: int, scale: float = 1.0) -> np.ndarray: def generate_stable_matrix(size, scale = 1.0) -> np.ndarray:
np.random.seed(13) np.random.seed(13)
random_matrix = np.random.randn(size, size) random_matrix = np.random.randn(size, size)
@@ -150,10 +190,6 @@ def benchmark(sizes, thread_counts):
print(f" Детерминант: {det_seq:.6e}") print(f" Детерминант: {det_seq:.6e}")
print(f" Sign: {sign_seq}, Log|det|: {logdet_seq:.6f}") print(f" Sign: {sign_seq}, Log|det|: {logdet_seq:.6f}")
if logdet_numpy is not None:
log_error = abs(logdet_seq - logdet_numpy)
print(f" Ошибка Log|det|: {log_error:.10f}")
results.append({ results.append({
'size': size, 'size': size,
'threads': 1, 'threads': 1,
@@ -174,18 +210,12 @@ def benchmark(sizes, thread_counts):
det_par = to_float(sign_par, logdet_par) det_par = to_float(sign_par, logdet_par)
speedup = seq_time / par_time if par_time > 0 else 0 speedup = seq_time / par_time if par_time > 0 else 0
efficiency = speedup / num_threads * 100
print(f"Параллельный алгоритм ({num_threads} потоков):") print(f"Параллельный алгоритм ({num_threads} потоков):")
print(f" Время: {par_time:.4f} сек") print(f" Время: {par_time:.4f} сек")
print(f" Детерминант: {det_par:.6e}") print(f" Детерминант: {det_par:.6e}")
print(f" Ускорение: {speedup:.2f}x") print(f" Ускорение: {speedup:.2f}x")
print(f" Эффективность: {efficiency:.1f}%")
if logdet_numpy is not None:
log_error = abs(logdet_par - logdet_numpy)
print(f" Ошибка Log|det|: {log_error:.10f}")
results.append({ results.append({
'size': size, 'size': size,
'threads': num_threads, 'threads': num_threads,
@@ -210,26 +240,8 @@ if __name__ == "__main__":
print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ") print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ")
print("=" * 80) print("=" * 80)
print() print()
print(f"{'Размер':<10} {'Потоки':<10} {'Время (сек)':<15} {'Ускорение':<12} {'Эффективность'}") print(f"{'Размер':<10} {'Потоки':<10} {'Время (сек)':<15} {'Ускорение':<12}")
print("-" * 80) print("-" * 80)
for r in results: for r in results:
efficiency = (r['speedup'] / r['threads'] * 100) if r['threads'] > 1 else 100 print(f"{r['size']:<10} {r['threads']:<10} {r['time']:<15.4f} {r['speedup']:<12.2f}")
print(f"{r['size']:<10} {r['threads']:<10} {r['time']:<15.4f} {r['speedup']:<12.2f} {efficiency:>6.1f}%")
print("\n" + "=" * 80)
print("АНАЛИЗ ПРОИЗВОДИТЕЛЬНОСТИ")
print("=" * 80)
print()
for size in sizes:
size_results = [r for r in results if r['size'] == size]
if len(size_results) > 1:
seq_time = size_results[0]['time']
best_parallel = min(size_results[1:], key=lambda x: x['time'])
print(f"Матрица {size}x{size}:")
print(f" Последовательное время: {seq_time:.4f} сек")
print(f" Лучшее параллельное: {best_parallel['time']:.4f} сек ({best_parallel['threads']} потоков)")
print(f" Максимальное ускорение: {best_parallel['speedup']:.2f}x")
print()