diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d482d2b2..ee0aa0aa 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -8,6 +8,7 @@ "usernamehw.errorlens", "ms-python.debugpy", "ms-python.vscode-pylance", - "james-yu.latex-workshop" + "james-yu.latex-workshop", + "github.copilot-chat" ] } \ No newline at end of file diff --git a/code/richardson_mpi.py b/code/richardson_mpi.py new file mode 100644 index 00000000..fe0e29ca --- /dev/null +++ b/code/richardson_mpi.py @@ -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) diff --git a/report/first_part/PORR_GORKA_RUDNICKI_SOBALA_RAPORT.pdf b/report/first_part/PORR_GORKA_RUDNICKI_SOBALA_RAPORT.pdf new file mode 100644 index 00000000..53d73af8 Binary files /dev/null and b/report/first_part/PORR_GORKA_RUDNICKI_SOBALA_RAPORT.pdf differ diff --git a/report/first_part/main.pdf b/report/first_part/main.pdf new file mode 100644 index 00000000..1e5bc565 Binary files /dev/null and b/report/first_part/main.pdf differ diff --git a/report/first_part/main.tex b/report/first_part/main.tex index eeb5a0ca..443ca457 100644 --- a/report/first_part/main.tex +++ b/report/first_part/main.tex @@ -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}