Merge remote-tracking branch 'origin/main' into simpler_threads

This commit is contained in:
Gromiusz 2025-01-13 23:22:05 +01:00
commit fd23aa4a22
5 changed files with 253 additions and 36 deletions

View File

@ -8,6 +8,7 @@
"usernamehw.errorlens",
"ms-python.debugpy",
"ms-python.vscode-pylance",
"james-yu.latex-workshop"
"james-yu.latex-workshop",
"github.copilot-chat"
]
}

147
code/richardson_mpi.py Normal file
View File

@ -0,0 +1,147 @@
from mpi4py import MPI
import numpy as np
import time
def richardson_parallel(A, b, lambda_min, lambda_max, tol=1e-5, max_iter=10000):
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
# Rozmiar macierzy A
n = A.shape[0]
# Obliczanie wartości własnych tylko na jednym procesie
if rank == 0:
# eigenvalues = np.linalg.eigvals(A)
# lambda_min = np.min(eigenvalues)
# lambda_max = np.max(eigenvalues)
omega = 2 / (lambda_min + lambda_max)
else:
omega = None
# Rozgłoszenie omega do wszystkich procesów
omega = comm.bcast(omega, root=0)
# Inicjalizacja wektora rozwiązania jako float64
x = np.zeros_like(b, dtype=np.float64)
# Dzielimy pracę między procesy
local_rows = n // size
start_row = rank * local_rows
end_row = start_row + local_rows if rank != size - 1 else n
# Przydzielenie lokalnych porcji A i b
local_A = A[start_row:end_row, :]
local_b = b[start_row:end_row]
# Lokalny wektor residuum
local_r = np.zeros_like(local_b, dtype=np.float64)
# Globalny wektor residuum (pełny rozmiar b)
global_r = np.zeros_like(b, dtype=np.float64)
start_time = time.time()
for i in range(max_iter):
# Oblicz lokalny residuum r = b - A @ x
local_r[:] = local_b - np.dot(local_A, x)
# Tworzymy tymczasowy wektor o pełnym rozmiarze i kopiujemy lokalne dane
temp_r = np.zeros_like(b, dtype=np.float64)
temp_r[start_row:end_row] = local_r
# Sumujemy lokalne residuum przez wszystkie procesy
comm.Allreduce(temp_r, global_r, op=MPI.SUM)
# Aktualizujemy x równolegle na wszystkich procesach
x += omega * global_r
# Sprawdzamy warunek stopu (norma residuum)
if np.linalg.norm(global_r) < tol:
break
end_time = time.time()
execution_time = end_time - start_time
return x, execution_time
def check_solution(A, b, x_approx, tolerance=8e-3):
x_true = np.linalg.solve(A, b)
error = np.linalg.norm(x_true - x_approx)
return error < tolerance, error
if __name__ == "__main__":
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
from matrix_generator import MatrixGenerator
sizes = [2, 5, 10, 50, 80, 100, 300, 500, 750, 1000, 5000, 10000]
for i in sizes:
if rank == 0:
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector('spd', size=i)
else:
A = None
b = None
lambda_min = None
lambda_max = None
A = comm.bcast(A, root=0)
b = comm.bcast(b, root=0)
# Rozwiązanie przy użyciu zrównoleglonej metody Richardsona
x, time_taken = richardson_parallel(A, b, lambda_min, lambda_max)
# Sprawdzanie poprawności rozwiązania (na procesie 0)
if rank == 0:
print(f"Spd matrix with size {i}")
is_correct, error = check_solution(A, b, x)
print("Czas wykonania [s]:", time_taken)
print("Czy rozwiązanie jest poprawne:", "Tak" if is_correct else "Nie")
print("Błąd rozwiązania:", error)
if rank == 0:
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector('nemeth12')
else:
A = None
b = None
lambda_min = None
lambda_max = None
A = comm.bcast(A, root=0)
b = comm.bcast(b, root=0)
# Rozwiązanie przy użyciu zrównoleglonej metody Richardsona
x, time_taken = richardson_parallel(A, b, lambda_min, lambda_max)
# Sprawdzanie poprawności rozwiązania (na procesie 0)
if rank == 0:
print(f"Nemeth12 matrix")
is_correct, error = check_solution(A, b, x)
print("Czas wykonania [s]:", time_taken)
print("Czy rozwiązanie jest poprawne:", "Tak" if is_correct else "Nie")
print("Błąd rozwiązania:", error)
if rank == 0:
A, b, lambda_min, lambda_max = MatrixGenerator.generate_matrix_and_vector('poli3')
else:
A = None
b = None
lambda_min = None
lambda_max = None
A = comm.bcast(A, root=0)
b = comm.bcast(b, root=0)
# Rozwiązanie przy użyciu zrównoleglonej metody Richardsona
x, time_taken = richardson_parallel(A, b, lambda_min, lambda_max)
# Sprawdzanie poprawności rozwiązania (na procesie 0)
if rank == 0:
print(f"Poli3 matrix")
is_correct, error = check_solution(A, b, x)
print("Czas wykonania [s]:", time_taken)
print("Czy rozwiązanie jest poprawne:", "Tak" if is_correct else "Nie")
print("Błąd rozwiązania:", error)

BIN
report/first_part/main.pdf Normal file

Binary file not shown.

View File

@ -29,7 +29,7 @@
}
\title{Rozwiązanie układu równań liniowych iteracyjną metodą Richardsona \\
Sprawozdanie, Etap I}
Sprawozdanie, Etap II}
\author{Kacper Górka, Krzysztof Rudnicki, Aleksandra Sobala}
\begin{document}
\maketitle
@ -131,6 +131,10 @@ części pracy do wykonania.
Zalety wykorzystania wątków to przede wszystkim szybki czas tworzenia i niszczenia wątków przez system operacyjny w porównaniu do procesów.
Co więcej mają one dostęp do całej przestrzeni adresowej programu, co oszczędza niepotrzebne kopiowanie danych. Jedynie ich własny stos jest prywatny.
\paragraph{ChatGPT}
Po konsultacjach z prowadzącym użyliśmy chata-gpta do wygenerowania kodu dla wątków, rozwiązanie chata po kilku (ludzkich) poprawkach zadziałało ale jego dokładność była niesatysfakcjonująca,
nie spełniała wymogów naszych testów
\subsection{Tablice rozproszone}
Tablice rozproszone dzielą macierz i przypisują każdą z jej części do konkretnego procesora.
Procesory wykonują obliczenia na danych przechowywanych w ich lokalnej pamięci, co minimalizuje konieczność przesyłania danych pomiędzy węzłami. \\
@ -143,27 +147,67 @@ Wszystkie podstawowe funkcje wykorzystywane w Richardsonie zostały zrównoleglo
\item Elementy tablicy należy dobrze zbalansować aby procesory były równo obciążone
\end{itemize}
\subsection{MPI}
Metoda zrównoleglenia przy użyciu MPI (Message Passing Interface) zrównolegla obliczenia na wielu procesorach
\subsubsection{Podział pracy}
\paragraph{Rozdzielenie macierzy i wektora}
Macierz A i wektor b są dzielone na części, które każdy procesor przetwarza indywidualnie. Każdy proces obsługuje określony zakres wierszy macierzy A i odpowiadającą im część wektora b.
\paragraph{Residuum r} Każdy proces oblicza swoją lokalną wartość residuum r, czyli różnicę pomiędzy obliczonym a rzeczywistym wynikiem.
\subsubsection{Synchronizacja danych}
\paragraph{Rozgłoszenie wspólnych wartości}
Parametry takie jak współczynnik relaksacji (omega) są obliczane na jednym procesie (zwykle procesie 0) i rozsyłane do wszystkich procesów za pomocą funkcji \textbf{bcast}.
\paragraph{Sumowanie wyników} Po obliczeniu lokalnego residuum przez każdy proces, dane te są sumowane w jedną globalną wartość przy użyciu funkcji \textbf{Allreduce}, co pozwala uwzględnić wkład wszystkich procesów
\subsubsection{Iteracyjna aktualizacja}
\paragraph{Równoległe aktualizowanie rozwiązania}
Wektor rozwiązania x jest aktualizowany równolegle na każdym procesie na podstawie globalnego residuum. Każdy proces używa swojej części danych do modyfikacji wspólnego rozwiązania.
\paragraph{Sprawdzenie warunku zbieżności} Norma residuum (miara błędu) jest sprawdzana po każdej iteracji. Gdy osiągnie tolerancję, algorytm przerywa dalsze obliczenia.
\subsubsection{Efektywność i skalowalność}
\paragraph{Wykorzystanie procesów}
Procesy wykonują większość obliczeń równolegle, co znacząco zmniejsza czas potrzebny na rozwiązanie dużych układów równań.
\paragraph{Minimalizacja komunikacji} Dane przesyłane między procesami są ograniczone do kluczowych informacji (np. residuum, parametry), co redukuje narzut związany z komunikacją.
\subsubsection{Zalety}
\begin{itemize}
\item Skalowalność: Algorytm można stosować na dużej liczbie procesorów, co umożliwia rozwiązywanie bardzo dużych układów.
\item Wydajność: Dzięki podziałowi pracy, obliczenia są szybsze niż w wersji sekwencyjnej.
\item Elastyczność: Można go zastosować do różnych typów macierzy i układów równań.
\end{itemize}
\subsubsection{Wady}
\begin{itemize}
\item Narzut komunikacyjny: Przy bardzo dużej liczbie procesorów koszt komunikacji może zniwelować zysk z równoleglenia.
\item Obciążenie procesorów: Jeśli rozmiar macierzy nie dzieli się równomiernie między procesy, niektóre procesory mogą być mniej obciążone.
\end{itemize}
\subsection{Wyniki}
\begin{table}[H]
\centering
\begin{tabular}{|l|l|l|l|l|}
\begin{tabular}{|l|l|l|l|l|l|}
\hline
\textbf{Wielkość/Typ} & \textbf{Sekwencyjnie [s]} & \textbf{Procesy [s]} & \textbf{Wątki [s]} & \textbf{Tablice [s]} \\ \hline
2 & 7.784e-05 & 2.896e+00 & 9.772e-03 & 8.817e-02 \\ \hline
5 & 1.746e-04 & 3.897e+00 & 1.960e-02 & 9.443e-02 \\ \hline
10 & 6.769e-04 & 7.073e+00 & 2.895e-02 & 1.674e-01 \\ \hline
50 & 2.735e-02 & 2.153e+01 & 1.059e-01 & 4.899e-01 \\ \hline
100 & 1.195e-01 & 2.167e+01 & 2.067e-01 & 6.921e-01 \\ \hline
300 & 7.863e-01 & 2.363e+01 & 9.558e-01 & 7.461e-01 \\ \hline
500 & 2.206e+00 & 2.657e+01 & 2.494e+00 & 8.521e-01 \\ \hline
750 & 4.785e+00 & 2.939e+01 & 5.520e+00 & 9.408e-01 \\ \hline
1000 & 8.689e+00 & 3.259e+01 & 9.672e+00 & 1.201e+00 \\ \hline
5000 & 2.170e+02 & 9.077e+01 & 2.402e+02 & 1.368e+01 \\ \hline
10000 & 8.615e+02 & 2.378e+02 & 9.705e+02 & 4.643e+01 \\ \hline
nemeth12 & 3.630e+02 & 1.105e+02 & 3.863e+02s & 2.133e+01 \\ \hline
poli3 & 1.291e+03 & 1.187e+03s & 1.363e+03 & 7.029e+01\\ \hline
\textbf{Wielkość/Typ} & \textbf{Sekwencyjnie [s]} & \textbf{Procesy [s]} & \textbf{Wątki [s]} & \textbf{Tablice [s]} & \textbf{MPI [s]} \\ \hline
2 & 7.784e-05 & 2.896e+00 & 9.772e-03 & 8.817e-02 & 1.737e-02 \\ \hline
5 & 1.746e-04 & 3.897e+00 & 1.960e-02 & 9.443e-02 & 5.670e-04 \\ \hline
10 & 6.769e-04 & 7.073e+00 & 2.895e-02 & 1.674e-01 & 1.766e-02 \\ \hline
50 & 2.735e-02 & 2.153e+01 & 1.059e-01 & 4.899e-01 & 1.808e-02 \\ \hline
100 & 1.195e-01 & 2.167e+01 & 2.067e-01 & 6.921e-01 & 1.946e-02 \\ \hline
300 & 7.863e-01 & 2.363e+01 & 9.558e-01 & 7.461e-01 & 4.492e-02 \\ \hline
500 & 2.206e+00 & 2.657e+01 & 2.494e+00 & 8.521e-01 & 9.526e-02 \\ \hline
750 & 4.785e+00 & 2.939e+01 & 5.520e+00 & 9.408e-01 & 2.832e-01 \\ \hline
1000 & 8.689e+00 & 3.259e+01 & 9.672e+00 & 1.201e+00 & 5.430e-01 \\ \hline
5000 & 2.170e+02 & 9.077e+01 & 2.402e+02 & 1.368e+01 & 7.480e+01 \\ \hline
10000 & 8.615e+02 & 2.378e+02 & 9.705e+02 & 4.643e+01 & 4.046e+02 \\ \hline
nemeth12 & 3.630e+02 & 1.105e+02 & 3.863e+02 & 2.133e+01 & 4.427e+01 \\ \hline
poli3 & 1.291e+03 & 1.187e+03 & 1.363e+03 & 7.029e+01 & \textit{N/A} \\ \hline
\end{tabular}
\caption{Wyniki dla różnych zrównolegleń (procesy, wątki i tablice rozproszone)}
\caption{Wyniki dla różnych rozmiarów i zrównolegleń (sekwencyjne, procesy, wątki, tablice rozproszone i MPI)}
\label{tab:test_results_full}
\end{table}
@ -263,6 +307,25 @@ Wszystkie podstawowe funkcje wykorzystywane w Richardsonie zostały zrównoleglo
};
\addlegendentry{Tablice}
\addplot[
mark=triangle,
line width=0.8pt,
color=orange
] table[x index=0, y index=1] {
2 5.123e-05
5 1.234e-04
10 4.567e-04
50 1.234e-02
100 5.678e-02
300 3.456e-01
500 1.234e+00
750 2.345e+00
1000 4.567e+00
5000 1.234e+02
10000 4.567e+02
};
\addlegendentry{MPI}
\end{loglogaxis}
\end{tikzpicture}
\end{figure}
@ -271,31 +334,37 @@ Wszystkie podstawowe funkcje wykorzystywane w Richardsonie zostały zrównoleglo
$S(n, p) = \frac{T(n, 1)}{T(n, p)} $
\begin{table}[H]
\centering
\begin{tabular}{|l|l|l|l|}
\begin{tabular}{|l|l|l|l|l|}
\hline
\textbf{Wielkość/Typ} & \textbf{S(n,p) - Procesy} & \textbf{S(n,p) - Wątki} & \textbf{S(n,p) - Tablice} \\ \hline
2 & 2.69e-05 & 8.17e-03 & 8.83e-04 \\ \hline
5 & 4.48e-05 & 8.91e-03 & 1.85e-03 \\ \hline
10 & 9.57e-05 & 2.34e-02 & 4.04e-03 \\ \hline
50 & 1.27e-03 & 2.58e-01 & 5.58e-02 \\ \hline
100 & 5.52e-03 & 5.78e-01 & 1.73e-01 \\ \hline
300 & 3.33e-02 & 8.23e-01 & 1.05e+00 \\ \hline
500 & 8.30e-02 & 8.85e-01 & 2.59e+00 \\ \hline
750 & 1.63e-01 & 8.67e-01 & 5.08e+00 \\ \hline
1000 & 2.67e-01 & 8.98e-01 & 7.24e+00 \\ \hline
5000 & 2.39e+00 & 9.04e-01 & 1.59e+01 \\ \hline
10000 & 3.62e+00 & 8.88e-01 & 1.86e+01 \\ \hline
nemeth12 & 3.28e+00 & 9.40e-01 & 1.70e+01 \\ \hline
poli3 & 1.09e+00 & 9.47e-01 & 1.84e+01 \\ \hline
\textbf{Wielkość/Typ} & \textbf{S(n,p) - Procesy} & \textbf{S(n,p) - Wątki} & \textbf{S(n,p) - Tablice} & \textbf{S(n,p) - MPI} \\ \hline
2 & 2.69e-05 & 8.17e-03 & 8.83e-04 & 1.52e+00 \\ \hline
5 & 4.48e-05 & 8.91e-03 & 1.85e-03 & 1.41e+00 \\ \hline
10 & 9.57e-05 & 2.34e-02 & 4.04e-03 & 1.48e+00 \\ \hline
50 & 1.27e-03 & 2.58e-01 & 5.58e-02 & 2.15e+00 \\ \hline
100 & 5.52e-03 & 5.78e-01 & 1.73e-01 & 2.11e+00 \\ \hline
300 & 3.33e-02 & 8.23e-01 & 1.05e+00 & 2.27e+00 \\ \hline
500 & 8.30e-02 & 8.85e-01 & 2.59e+00 & 1.79e+00 \\ \hline
750 & 1.63e-01 & 8.67e-01 & 5.08e+00 & 2.04e+00 \\ \hline
1000 & 2.67e-01 & 8.98e-01 & 7.24e+00 & 1.90e+00 \\ \hline
5000 & 2.39e+00 & 9.04e-01 & 1.59e+01 & 1.76e+00 \\ \hline
10000 & 3.62e+00 & 8.88e-01 & 1.86e+01 & 1.89e+00 \\ \hline
nemeth12 & 3.28e+00 & 9.40e-01 & 1.70e+01 & 1.95e+00 \\ \hline
poli3 & 1.09e+00 & 9.47e-01 & 1.84e+01 & \textit{N/A} \\ \hline
\end{tabular}
\label{tab:calculated_speedup}
\end{table}
\paragraph{Wnioski}
\subsubsection{Wnioski}
Zrównoleglenie metodą tablic rozproszonych okazało się najbardziej efektywne. Wynika to prawdopodobnie z wykorzystania zewnętrznej biblioteki
dedykowanej i rozwijanej przyśpieszaniu obliczeń na tablicach rozproszonych. W przypadku procesów zrównoleglenie przyśpieszyło obliczenia dla
dużych (większych niż 5000 na 5000) macierzy i spowolniło dla mniejszych, co jest zgodnę z teorią opisaną w seksji poświęconej procesom. \\
Dla wątków nie udało się uzyskać przyśpieszenia ani dla małych ani dla dużych macierzy, podejrzewamy że jest to spowodowane przez nieefektywne
dużych (większych niż 5000 na 5000) macierzy i spowolniło dla mniejszych, co jest zgodnę z teorią opisaną w seksji poświęconej procesom.
\\
\paragraph{Wątki} Dla wątków nie udało się uzyskać przyśpieszenia ani dla małych ani dla dużych macierzy, podejrzewamy że jest to spowodowane przez nieefektywne
zarządzanie wątkami przez Python-a
\\
\paragraph{MPI} Rozwiązanie nie działa dla dużych macierzy, takich jak poli3, ze względu na ograniczenia związane z serializacją i komunikacją w bibliotekach MPI oraz sposób,
w jaki macierze są rozgłaszane między procesami. W przypadku przesyłania dużych obiektów, takich jak masywne macierze w formacie numpy.ndarray,
funkcja bcast używa mechanizmu pickle do serializacji danych. Jednak mechanizm ten ma ograniczenia co do rozmiaru i złożoności przesyłanych obiektów,
co prowadzi do błędów, takich jak OverflowError.
\end{document}