From fd94f597a550be4cf0178317d3cb9511e823aae2 Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Sat, 28 Dec 2024 13:24:58 +0100 Subject: [PATCH 1/8] feat: trying and testing different ways to implement threads --- code/linear_algebra_utils.py | 21 ++++--- code/tests.py | 23 ++++--- code/threads.py | 72 +++++++++++++++++++++ code/threads_nowy.py | 119 +++++++++++++++++++++++++++++++++++ code/time_measurement.py | 25 ++++++++ 5 files changed, 244 insertions(+), 16 deletions(-) create mode 100644 code/threads.py create mode 100644 code/threads_nowy.py diff --git a/code/linear_algebra_utils.py b/code/linear_algebra_utils.py index a8739a5d..4e6de0ab 100644 --- a/code/linear_algebra_utils.py +++ b/code/linear_algebra_utils.py @@ -2,6 +2,7 @@ import cmath import math import itertools import operator +import multiprocessing from multiprocessing import Pool from abc import ABC, abstractmethod from concurrent.futures import ThreadPoolExecutor @@ -10,7 +11,7 @@ from time_measurement import time_measurement, time_accumulator import numpy as np import dask.array as da -class LinearAlgebraUtils(ABC): +class LinearAlgebraUtils: @staticmethod @abstractmethod def dot_product(v1, v2): @@ -62,7 +63,7 @@ class LinearAlgebraUtils(ABC): pass -class SequentialLinearAlgebraUtils(ABC): +class SequentialLinearAlgebraUtils: @staticmethod def dot_product(v1, v2): return sum(x*y for x, y in zip(v1, v2)) @@ -106,11 +107,10 @@ class SequentialLinearAlgebraUtils(ABC): return [[A[i][j] - B[i][j] for j in range(len(A[0]))] for i in range(len(A))] -class ThreadsLinearAlgebraUtils(ABC): - NUM_THREADS = 4 +class ThreadsLinearAlgebraUtils: + NUM_THREADS = multiprocessing.cpu_count() @staticmethod - @time_measurement(time_accumulator) def get_chunk_size(data): num_elements = len(data) num_threads = min(ThreadsLinearAlgebraUtils.NUM_THREADS, num_elements) @@ -120,7 +120,6 @@ class ThreadsLinearAlgebraUtils(ABC): @staticmethod - @time_measurement(time_accumulator) def divide_vectors_to_chunks(v1, v2): chunk_size, num_threads, remainder = ThreadsLinearAlgebraUtils.get_chunk_size(v1) @@ -134,7 +133,6 @@ class ThreadsLinearAlgebraUtils(ABC): return chunks @staticmethod - @time_measurement(time_accumulator) def divide_vector_or_matrix_to_chunks(v): chunk_size, num_threads, remainder = ThreadsLinearAlgebraUtils.get_chunk_size(v) @@ -156,6 +154,15 @@ class ThreadsLinearAlgebraUtils(ABC): results = executor.map(lambda pair: SequentialLinearAlgebraUtils.dot_product(*pair), chunks) return sum(results) + # @staticmethod + # @time_measurement(time_accumulator) + # def matrix_vector_multiply(A, x): + # chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) + # with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: + # func = partial(SequentialLinearAlgebraUtils.matrix_vector_multiply, x=x) + # results = executor.map(func, chunks) + # return [item for sublist in results for item in sublist] + @staticmethod @time_measurement(time_accumulator) def matrix_vector_multiply(A, x): diff --git a/code/tests.py b/code/tests.py index f01f1e66..93c7990c 100644 --- a/code/tests.py +++ b/code/tests.py @@ -2,6 +2,7 @@ import pytest import numpy as np from matrix_generator import MatrixGenerator from richardson_method import RichardsonMethod +from threads import RichardsonMethodThreads from processing_type import ProcessingType from time_measurement import time_measurement, tests_time @@ -40,10 +41,10 @@ def solution_lib(A, b): 10000 ]) @pytest.mark.parametrize("processing_type", [ - ProcessingType.SEQUENTIAL, - ProcessingType.THREADS, - ProcessingType.PROCESSES, - ProcessingType.DISTRIBUTED_ARRAYS + # ProcessingType.SEQUENTIAL, + ProcessingType.THREADS#, + # ProcessingType.PROCESSES, + # ProcessingType.DISTRIBUTED_ARRAYS ]) @pytest.mark.parametrize("matrix_type", [ "spd", @@ -67,12 +68,16 @@ def test_richardson_vs_cg(n: int, processing_type: ProcessingType, matrix_type: else: raise ValueError("Invalid matrix type specified. Choose 'spd', 'poli3', or 'nemeth12'.") - richardson_solver = RichardsonMethod(processing_type, A, b, lambda_min, lambda_max, max_iterations, size=A.shape[0], tol=1e-7) - solution_richardson, info_richardson = None, None - with capsys.disabled(): - solution_richardson, info_richardson = richardson_solver.solve() - + + if processing_type != ProcessingType.THREADS: + richardson_solver = RichardsonMethod(processing_type, A, b, lambda_min, lambda_max, max_iterations, size=A.shape[0], tol=1e-7) + with capsys.disabled(): + solution_richardson, info_richardson = richardson_solver.solve() + else: + with capsys.disabled(): + solution_richardson, info_richardson = RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, tol=1e-7) + # Przechwytywanie wyjścia po solve captured = capsys.readouterr() print("Captured output:", captured.out) diff --git a/code/threads.py b/code/threads.py new file mode 100644 index 00000000..4f43f0bb --- /dev/null +++ b/code/threads.py @@ -0,0 +1,72 @@ +import numpy as np +import threading +import multiprocessing +import gc +import time +import sys +from time_measurement import time_measurement_longest, longest_time_accumulator, tests_time +import linear_algebra_utils as linAlg + + +@time_measurement_longest(longest_time_accumulator) +def RichardsonThread(A, b, x, _x, omega, start, end): + for i in range(start, end): + sigma = np.dot(A[i, :], _x) - A[i, i] * _x[i] + x[i] = (1 - omega) * _x[i] + omega * (b[i] - sigma) / A[i, i] + +def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): + longest_time_accumulator.total_time = 0 + longest_time_accumulator.start = sys.float_info.max + longest_time_accumulator.end = 0 + + gc.disable() + start_time = time.perf_counter() + + n = len(b) + x0 = x0 if x0 is not None else [0.0] * len(b) + x = x0[:] + omega = 0.05#2 / (lambda_min + lambda_max) + num_threads = multiprocessing.cpu_count() + threads = [] + chunk_size = n // num_threads + max_iterations = 1000 + + for _ in range(max_iterations): + _x = x[:] + for i in range(num_threads): + start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek + end = n if i == num_threads - 1 else (i + 1) * chunk_size + thread = threading.Thread(target=RichardsonThread, args=(A, b, x, _x, omega, start, end)) + threads.append(thread) + thread.start() + + for thread in threads: + thread.join() + + # Ax = linAlg.SequentialLinearAlgebraUtils.matrix_vector_multiply(A, x) + # residual = linAlg.SequentialLinearAlgebraUtils.vector_vector_subtraction(b, Ax) + # if (linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol): + # break + + end_time = time.perf_counter() + gc.enable() + total_time = end_time - start_time + sequential_time = total_time - longest_time_accumulator.total_time + + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") + + return x, 0 + + + + +# # Przykładowe dane wejściowe +# np.random.seed(0) # Ustalanie ziarna dla powtarzalności wyników +# A = np.random.rand(20, 20) + 20 * np.eye(20) # Macierz przekątniowa z losowymi elementami +# b = np.random.rand(20) # Wektor wyrazów wolnych +# omega = 0.2 +# n_iterations = 1000 + +# # Rozwiązanie układu równań metodą Richardson'a +# x = RichardsonMethodThreads(A, b, 5, 5, n_iterations) +# print("Rozwiązanie: ", x) diff --git a/code/threads_nowy.py b/code/threads_nowy.py new file mode 100644 index 00000000..7acfc6c3 --- /dev/null +++ b/code/threads_nowy.py @@ -0,0 +1,119 @@ +import numpy as np +import threading +import multiprocessing +import gc +import time +import sys +from time_measurement import time_measurement_longest, longest_time_accumulator, tests_time +import linear_algebra_utils as linAlg + + +@time_measurement_longest(longest_time_accumulator) +def RichardsonThread(A, b, x, _x, omega, start, end): + for i in range(start, end): + sigma = np.dot(A[i, :], x) - A[i, i] * x[i] + x[i] = (1 - omega) * x[i] + omega * (b[i] - sigma) / A[i, i] + +def matrix_vector_multiply(A, x, start, end, Ax): + Ax[start:end] = [sum(xx*yy for xx, yy in zip(row, x)) for row in A[start:end]] + +def vector_vector_subtraction(b, Ax, start, end, residual): + residual[start:end] = [xx-yy for xx, yy in zip(b[start:end], Ax[start:end])] + +def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): + longest_time_accumulator.total_time = 0 + longest_time_accumulator.start = sys.float_info.max + longest_time_accumulator.end = 0 + + gc.disable() + start_time = time.perf_counter() + + n = len(b) + x0 = x0 if x0 is not None else [0.0] * len(b) + x = x0[:] + omega = 2 / (lambda_min + lambda_max) + num_threads = multiprocessing.cpu_count() + threads = [] + chunk_size = n // num_threads + + for iteration in range(max_iterations): + # chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) + + # with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: + # func = partial(SequentialLinearAlgebraUtils.matrix_vector_multiply, x=x) + # results = executor.map(func, chunks) + Ax = [0] * len(x) + for i in range(num_threads): + start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek + end = n if i == num_threads - 1 else (i + 1) * chunk_size + thread = threading.Thread(target=matrix_vector_multiply, args=(A, x, start, end, Ax)) + threads.append(thread) + thread.start() + + for thread in threads: + thread.join() + + + residual = [0] * len(b) + + for i in range(num_threads): + start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek + end = n if i == num_threads - 1 else (i + 1) * chunk_size + thread = threading.Thread(target=vector_vector_subtraction, args=(b, Ax, start, end, residual)) + threads.append(thread) + thread.start() + + for thread in threads: + thread.join() + + + x = self.LinAlg.vector_vector_addition(x, self.LinAlg.scalar_vector_multiply(self.omega, residual)) + + for i in range(num_threads): + start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek + end = n if i == num_threads - 1 else (i + 1) * chunk_size + thread = threading.Thread(target=scalar_vector_multiply, args=(A, b, x, omega, start, end)) + threads.append(thread) + thread.start() + + for thread in threads: + thread.join() + + + x = self.LinAlg.vector_vector_addition(x, self.LinAlg.scalar_vector_multiply(self.omega, residual)) + + for i in range(num_threads): + start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek + end = n if i == num_threads - 1 else (i + 1) * chunk_size + thread = threading.Thread(target=vector_vector_addition, args=(A, b, x, omega, start, end)) + threads.append(thread) + thread.start() + + for thread in threads: + thread.join() + + if (linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < self.tol): + break + + end_time = time.perf_counter() + gc.enable() + total_time = end_time - start_time + sequential_time = total_time - longest_time_accumulator.total_time + + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") + + return x, 0 + + + + +# # Przykładowe dane wejściowe +# np.random.seed(0) # Ustalanie ziarna dla powtarzalności wyników +# A = np.random.rand(20, 20) + 20 * np.eye(20) # Macierz przekątniowa z losowymi elementami +# b = np.random.rand(20) # Wektor wyrazów wolnych +# omega = 0.2 +# n_iterations = 1000 + +# # Rozwiązanie układu równań metodą Richardson'a +# x = RichardsonMethodThreads(A, b, 5, 5, n_iterations) +# print("Rozwiązanie: ", x) diff --git a/code/time_measurement.py b/code/time_measurement.py index 03ca4c0f..c6be2a49 100644 --- a/code/time_measurement.py +++ b/code/time_measurement.py @@ -1,13 +1,22 @@ import time +import sys from functools import wraps class TimeAccumulator: def __init__(self): self.total_time = 0 +class ComplexTimeAcumulator: + def __init__(self): + self.total_time = 0 + self.start = sys.float_info.max + self.end = 0 + time_accumulator = TimeAccumulator() tests_time = TimeAccumulator() +longest_time_accumulator = ComplexTimeAcumulator() + def time_measurement(accumulator): def decorator(func): @wraps(func) @@ -20,6 +29,22 @@ def time_measurement(accumulator): return inner return decorator +def time_measurement_longest(accumulator: ComplexTimeAcumulator): + def decorator(func): + @wraps(func) + def inner(*args, **kwargs): + start = time.perf_counter() + result = func(*args, **kwargs) + end = time.perf_counter() + if start < accumulator.start: + accumulator.start = start + if end > accumulator.end: + accumulator.end = end + accumulator.total_time = accumulator.end - accumulator.start # "=" instead of "+=" + return result + return inner + return decorator + From fcfd95a1afa7ef8b98e2b4e213b7b0abf69bfc61 Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Wed, 1 Jan 2025 18:50:11 +0100 Subject: [PATCH 2/8] feat: faster thread method --- code/linear_algebra_utils.py | 9 -- code/tests.py | 10 +- code/threads_indep.py | 103 ++++++++++++++++++ code/threads_nowy.py | 119 --------------------- code/{threads.py => threads_too_simple.py} | 12 --- code/time_measurement.py | 18 +++- 6 files changed, 123 insertions(+), 148 deletions(-) create mode 100644 code/threads_indep.py delete mode 100644 code/threads_nowy.py rename code/{threads.py => threads_too_simple.py} (83%) diff --git a/code/linear_algebra_utils.py b/code/linear_algebra_utils.py index 4e6de0ab..1f56d806 100644 --- a/code/linear_algebra_utils.py +++ b/code/linear_algebra_utils.py @@ -154,15 +154,6 @@ class ThreadsLinearAlgebraUtils: results = executor.map(lambda pair: SequentialLinearAlgebraUtils.dot_product(*pair), chunks) return sum(results) - # @staticmethod - # @time_measurement(time_accumulator) - # def matrix_vector_multiply(A, x): - # chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) - # with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - # func = partial(SequentialLinearAlgebraUtils.matrix_vector_multiply, x=x) - # results = executor.map(func, chunks) - # return [item for sublist in results for item in sublist] - @staticmethod @time_measurement(time_accumulator) def matrix_vector_multiply(A, x): diff --git a/code/tests.py b/code/tests.py index 93c7990c..1bc1ba20 100644 --- a/code/tests.py +++ b/code/tests.py @@ -2,7 +2,7 @@ import pytest import numpy as np from matrix_generator import MatrixGenerator from richardson_method import RichardsonMethod -from threads import RichardsonMethodThreads +from threads_indep import RichardsonMethodThreads from processing_type import ProcessingType from time_measurement import time_measurement, tests_time @@ -41,10 +41,10 @@ def solution_lib(A, b): 10000 ]) @pytest.mark.parametrize("processing_type", [ - # ProcessingType.SEQUENTIAL, - ProcessingType.THREADS#, - # ProcessingType.PROCESSES, - # ProcessingType.DISTRIBUTED_ARRAYS + ProcessingType.SEQUENTIAL, + ProcessingType.THREADS, + ProcessingType.PROCESSES, + ProcessingType.DISTRIBUTED_ARRAYS ]) @pytest.mark.parametrize("matrix_type", [ "spd", diff --git a/code/threads_indep.py b/code/threads_indep.py new file mode 100644 index 00000000..7c5ecc5e --- /dev/null +++ b/code/threads_indep.py @@ -0,0 +1,103 @@ +import multiprocessing +import gc +import time +from concurrent.futures import ThreadPoolExecutor +from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time +import linear_algebra_utils as linAlg + + +@time_measurement_longest(longest_threads_time_accumulator) +def matrix_vector_multiply(A, input_x, start, end, Ax): + Ax[start:end] = [sum(x*y for x, y in zip(row, input_x)) for row in A[start:end]] + +@time_measurement_longest(longest_threads_time_accumulator) +def vector_vector_subtraction(b, Ax, start, end, residual): + residual[start:end] = [x-y for x, y in zip(b[start:end], Ax[start:end])] + +@time_measurement_longest(longest_threads_time_accumulator) +def scalar_vector_multiply(omega, vector, start, end, result): + result[start:end] = [omega * x for x in vector[start:end]] + +@time_measurement_longest(longest_threads_time_accumulator) +def vector_vector_addition(input_x, vector, start, end, output_x): + output_x[start:end] = [x+y for x, y in zip(input_x[start:end], vector[start:end])] + +def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): + longest_threads_time_accumulator.hard_reset() + + gc.disable() + start_time = time.perf_counter() + + n = len(b) + x0 = x0 if x0 is not None else [0.0] * len(b) + x = x0[:] + omega = 2 / (lambda_min + lambda_max) + num_threads = multiprocessing.cpu_count() + chunk_size = n // num_threads + + with ThreadPoolExecutor(max_workers=num_threads) as executor: # wątki są tworzone raz i nie są niszczone + for iteration in range(max_iterations): + + Ax = [0] * len(x) # tutaj zostanie przypisany wynik z mnożenia macierzy A z wektorem x + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(matrix_vector_multiply, A, x, start, end, Ax)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + residual = [0] * len(b) # tutaj zostanie przypisany wynik z vector_vector_subtraction + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(vector_vector_subtraction, b, Ax, start, end, residual)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + change_vector = [0] * len(residual) # zostanie tu przypisany wynik scalar_vector_multiply po pracy wątków + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(scalar_vector_multiply, omega, residual, start, end, change_vector)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + _x = x[:] # do _x zostanie przez wątki przypisany wynik pracy w danej iteracji + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(vector_vector_addition, x, change_vector, start, end, _x)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + x = _x[:] + + + if (linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol): + break + + + end_time = time.perf_counter() + gc.enable() + total_time = end_time - start_time + sequential_time = total_time - longest_threads_time_accumulator.total_time + + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_threads_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") + + return x, 0 \ No newline at end of file diff --git a/code/threads_nowy.py b/code/threads_nowy.py deleted file mode 100644 index 7acfc6c3..00000000 --- a/code/threads_nowy.py +++ /dev/null @@ -1,119 +0,0 @@ -import numpy as np -import threading -import multiprocessing -import gc -import time -import sys -from time_measurement import time_measurement_longest, longest_time_accumulator, tests_time -import linear_algebra_utils as linAlg - - -@time_measurement_longest(longest_time_accumulator) -def RichardsonThread(A, b, x, _x, omega, start, end): - for i in range(start, end): - sigma = np.dot(A[i, :], x) - A[i, i] * x[i] - x[i] = (1 - omega) * x[i] + omega * (b[i] - sigma) / A[i, i] - -def matrix_vector_multiply(A, x, start, end, Ax): - Ax[start:end] = [sum(xx*yy for xx, yy in zip(row, x)) for row in A[start:end]] - -def vector_vector_subtraction(b, Ax, start, end, residual): - residual[start:end] = [xx-yy for xx, yy in zip(b[start:end], Ax[start:end])] - -def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): - longest_time_accumulator.total_time = 0 - longest_time_accumulator.start = sys.float_info.max - longest_time_accumulator.end = 0 - - gc.disable() - start_time = time.perf_counter() - - n = len(b) - x0 = x0 if x0 is not None else [0.0] * len(b) - x = x0[:] - omega = 2 / (lambda_min + lambda_max) - num_threads = multiprocessing.cpu_count() - threads = [] - chunk_size = n // num_threads - - for iteration in range(max_iterations): - # chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) - - # with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - # func = partial(SequentialLinearAlgebraUtils.matrix_vector_multiply, x=x) - # results = executor.map(func, chunks) - Ax = [0] * len(x) - for i in range(num_threads): - start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek - end = n if i == num_threads - 1 else (i + 1) * chunk_size - thread = threading.Thread(target=matrix_vector_multiply, args=(A, x, start, end, Ax)) - threads.append(thread) - thread.start() - - for thread in threads: - thread.join() - - - residual = [0] * len(b) - - for i in range(num_threads): - start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek - end = n if i == num_threads - 1 else (i + 1) * chunk_size - thread = threading.Thread(target=vector_vector_subtraction, args=(b, Ax, start, end, residual)) - threads.append(thread) - thread.start() - - for thread in threads: - thread.join() - - - x = self.LinAlg.vector_vector_addition(x, self.LinAlg.scalar_vector_multiply(self.omega, residual)) - - for i in range(num_threads): - start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek - end = n if i == num_threads - 1 else (i + 1) * chunk_size - thread = threading.Thread(target=scalar_vector_multiply, args=(A, b, x, omega, start, end)) - threads.append(thread) - thread.start() - - for thread in threads: - thread.join() - - - x = self.LinAlg.vector_vector_addition(x, self.LinAlg.scalar_vector_multiply(self.omega, residual)) - - for i in range(num_threads): - start = i * chunk_size # start jest indeksem w A. Wątki otrzymują kolejny punkt startowy będący wielokrotnością rozmiaru porcji na wątek - end = n if i == num_threads - 1 else (i + 1) * chunk_size - thread = threading.Thread(target=vector_vector_addition, args=(A, b, x, omega, start, end)) - threads.append(thread) - thread.start() - - for thread in threads: - thread.join() - - if (linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < self.tol): - break - - end_time = time.perf_counter() - gc.enable() - total_time = end_time - start_time - sequential_time = total_time - longest_time_accumulator.total_time - - print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") - - return x, 0 - - - - -# # Przykładowe dane wejściowe -# np.random.seed(0) # Ustalanie ziarna dla powtarzalności wyników -# A = np.random.rand(20, 20) + 20 * np.eye(20) # Macierz przekątniowa z losowymi elementami -# b = np.random.rand(20) # Wektor wyrazów wolnych -# omega = 0.2 -# n_iterations = 1000 - -# # Rozwiązanie układu równań metodą Richardson'a -# x = RichardsonMethodThreads(A, b, 5, 5, n_iterations) -# print("Rozwiązanie: ", x) diff --git a/code/threads.py b/code/threads_too_simple.py similarity index 83% rename from code/threads.py rename to code/threads_too_simple.py index 4f43f0bb..d5ac2713 100644 --- a/code/threads.py +++ b/code/threads_too_simple.py @@ -58,15 +58,3 @@ def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=Non return x, 0 - - -# # Przykładowe dane wejściowe -# np.random.seed(0) # Ustalanie ziarna dla powtarzalności wyników -# A = np.random.rand(20, 20) + 20 * np.eye(20) # Macierz przekątniowa z losowymi elementami -# b = np.random.rand(20) # Wektor wyrazów wolnych -# omega = 0.2 -# n_iterations = 1000 - -# # Rozwiązanie układu równań metodą Richardson'a -# x = RichardsonMethodThreads(A, b, 5, 5, n_iterations) -# print("Rozwiązanie: ", x) diff --git a/code/time_measurement.py b/code/time_measurement.py index c6be2a49..d70992a2 100644 --- a/code/time_measurement.py +++ b/code/time_measurement.py @@ -8,16 +8,28 @@ class TimeAccumulator: class ComplexTimeAcumulator: def __init__(self): + self.hard_reset() + + def hard_reset(self): self.total_time = 0 + self.reset() + + def reset(self): + self.lap_time = 0 self.start = sys.float_info.max self.end = 0 + + def save_lap_and_reset(self): + self.total_time += self.lap_time + self.reset() + time_accumulator = TimeAccumulator() tests_time = TimeAccumulator() -longest_time_accumulator = ComplexTimeAcumulator() +longest_threads_time_accumulator = ComplexTimeAcumulator() -def time_measurement(accumulator): +def time_measurement(accumulator: TimeAccumulator): def decorator(func): @wraps(func) def inner(*args, **kwargs): @@ -40,7 +52,7 @@ def time_measurement_longest(accumulator: ComplexTimeAcumulator): accumulator.start = start if end > accumulator.end: accumulator.end = end - accumulator.total_time = accumulator.end - accumulator.start # "=" instead of "+=" + accumulator.lap_time = accumulator.end - accumulator.start # "=" instead of "+=" return result return inner return decorator From 8b457a2f43045673242ecdfdfe355ccb258298af Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Mon, 13 Jan 2025 15:44:36 +0100 Subject: [PATCH 3/8] fix: remove unused thread functions --- code/linear_algebra_utils.py | 75 ------------------------------------ 1 file changed, 75 deletions(-) diff --git a/code/linear_algebra_utils.py b/code/linear_algebra_utils.py index 1f56d806..5448545b 100644 --- a/code/linear_algebra_utils.py +++ b/code/linear_algebra_utils.py @@ -118,7 +118,6 @@ class ThreadsLinearAlgebraUtils: remainder = num_elements % num_threads return chunk_size, num_threads, remainder - @staticmethod def divide_vectors_to_chunks(v1, v2): chunk_size, num_threads, remainder = ThreadsLinearAlgebraUtils.get_chunk_size(v1) @@ -145,15 +144,6 @@ class ThreadsLinearAlgebraUtils: return chunks - - @staticmethod - @time_measurement(time_accumulator) - def dot_product(v1, v2): - chunks = ThreadsLinearAlgebraUtils.divide_vectors_to_chunks(v1, v2) - with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - results = executor.map(lambda pair: SequentialLinearAlgebraUtils.dot_product(*pair), chunks) - return sum(results) - @staticmethod @time_measurement(time_accumulator) def matrix_vector_multiply(A, x): @@ -176,23 +166,6 @@ class ThreadsLinearAlgebraUtils: total_sum = sum(results) return total_sum**0.5 - @staticmethod - @time_measurement(time_accumulator) - def vector_scalar_divide(x, scalar): - chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(x) - - with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - results = executor.map(lambda chunk: SequentialLinearAlgebraUtils.vector_scalar_divide(chunk, scalar), chunks) - return [item for sublist in results for item in sublist] - - @staticmethod - @time_measurement(time_accumulator) - def matrix_scalar_multiply(A, w): - chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) - with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - results = executor.map(lambda chunk: SequentialLinearAlgebraUtils.matrix_scalar_multiply(w, chunk), chunks) - return [item for sublist in results for item in sublist] - @staticmethod @time_measurement(time_accumulator) def vector_vector_subtraction(v1, v2): @@ -219,54 +192,6 @@ class ThreadsLinearAlgebraUtils: return [item for sublist in results for item in sublist] - @staticmethod - @time_measurement(time_accumulator) - def matrix_norm(A): - chunks = ThreadsLinearAlgebraUtils.divide_vector_or_matrix_to_chunks(A) - - def partial_norm(chunk): - return sum(element ** 2 for row in chunk for element in row) - - with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - results = executor.map(partial_norm, chunks) - - total_sum = sum(results) - return math.sqrt(total_sum) - - @staticmethod - @time_measurement(time_accumulator) - def divide_matrixes_to_chunks(A, B): - num_rows = len(A) - num_threads = ThreadsLinearAlgebraUtils.NUM_THREADS - if num_threads > num_rows: - num_threads = num_rows - if num_rows == 0: - return [] - chunk_size = num_rows // num_threads - remainder = num_rows % num_threads - chunks = [] - start = 0 - for _ in range(num_threads): - end = start + chunk_size + (1 if remainder > 0 else 0) - chunks.append((A[start:end], B[start:end])) - start = end - if remainder > 0: - remainder -= 1 - return chunks - - @staticmethod - @time_measurement(time_accumulator) - def matrix_matrix_subtraction(A, B): - - def subtract_chunk(pair): - chunk_A, chunk_B = pair - return [[chunk_A[i][j] - chunk_B[i][j] for j in range(len(chunk_A[0]))] for i in range(len(chunk_A))] - - chunks = ThreadsLinearAlgebraUtils.divide_matrixes_to_chunks(A, B) - with ThreadPoolExecutor(max_workers=ThreadsLinearAlgebraUtils.NUM_THREADS) as executor: - results = executor.map(subtract_chunk, chunks) - return [row for chunk in results for row in chunk] - @time_measurement(time_accumulator) def process_row(params): From d1a3deab53e9cc80de3f2bf762e71dbaac049682 Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Mon, 13 Jan 2025 17:05:20 +0100 Subject: [PATCH 4/8] feat: moving to numba package --- code/threads_indep.py | 132 +++++++++++++++++--------------------- code/threads_indep_old.py | 103 +++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 72 deletions(-) create mode 100644 code/threads_indep_old.py diff --git a/code/threads_indep.py b/code/threads_indep.py index 7c5ecc5e..deae5381 100644 --- a/code/threads_indep.py +++ b/code/threads_indep.py @@ -1,103 +1,91 @@ -import multiprocessing import gc import time -from concurrent.futures import ThreadPoolExecutor +import numpy as np +from numba import njit, prange from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time import linear_algebra_utils as linAlg +# Funkcje równoległe z Numba +@njit(parallel=True) +def numba_matrix_vector_multiply(A, input_x, Ax): + n, m = A.shape + for i in prange(n): + Ax[i] = np.dot(A[i], input_x) + +@njit(parallel=True) +def numba_vector_vector_subtraction(b, Ax, residual): + for i in prange(len(b)): + residual[i] = b[i] - Ax[i] + +@njit(parallel=True) +def numba_scalar_vector_multiply(omega, vector, result): + for i in prange(len(vector)): + result[i] = omega * vector[i] + +@njit(parallel=True) +def numba_vector_vector_addition(input_x, vector, output_x): + for i in prange(len(input_x)): + output_x[i] = input_x[i] + vector[i] + +# Funkcje z dekoratorem +@time_measurement_longest(longest_threads_time_accumulator) +def matrix_vector_multiply(A, input_x, Ax): + numba_matrix_vector_multiply(A, input_x, Ax) @time_measurement_longest(longest_threads_time_accumulator) -def matrix_vector_multiply(A, input_x, start, end, Ax): - Ax[start:end] = [sum(x*y for x, y in zip(row, input_x)) for row in A[start:end]] +def vector_vector_subtraction(b, Ax, residual): + numba_vector_vector_subtraction(b, Ax, residual) @time_measurement_longest(longest_threads_time_accumulator) -def vector_vector_subtraction(b, Ax, start, end, residual): - residual[start:end] = [x-y for x, y in zip(b[start:end], Ax[start:end])] +def scalar_vector_multiply(omega, vector, result): + numba_scalar_vector_multiply(omega, vector, result) @time_measurement_longest(longest_threads_time_accumulator) -def scalar_vector_multiply(omega, vector, start, end, result): - result[start:end] = [omega * x for x in vector[start:end]] - -@time_measurement_longest(longest_threads_time_accumulator) -def vector_vector_addition(input_x, vector, start, end, output_x): - output_x[start:end] = [x+y for x, y in zip(input_x[start:end], vector[start:end])] +def vector_vector_addition(input_x, vector, output_x): + numba_vector_vector_addition(input_x, vector, output_x) +# Metoda Richardson z obsługą wątków def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): longest_threads_time_accumulator.hard_reset() gc.disable() start_time = time.perf_counter() - n = len(b) - x0 = x0 if x0 is not None else [0.0] * len(b) - x = x0[:] + A = np.array(A) + b = np.array(b) + x0 = np.array(x0) if x0 is not None else np.zeros_like(b) + x = x0.copy() + omega = 2 / (lambda_min + lambda_max) - num_threads = multiprocessing.cpu_count() - chunk_size = n // num_threads + n = len(b) - with ThreadPoolExecutor(max_workers=num_threads) as executor: # wątki są tworzone raz i nie są niszczone - for iteration in range(max_iterations): + for iteration in range(max_iterations): + Ax = np.zeros_like(x) + matrix_vector_multiply(A, x, Ax) + longest_threads_time_accumulator.save_lap_and_reset() - Ax = [0] * len(x) # tutaj zostanie przypisany wynik z mnożenia macierzy A z wektorem x - futures = [] + residual = np.zeros_like(b) + vector_vector_subtraction(b, Ax, residual) + longest_threads_time_accumulator.save_lap_and_reset() - for i in range(num_threads): - start = i * chunk_size - end = n if i == num_threads - 1 else (i + 1) * chunk_size - futures.append(executor.submit(matrix_vector_multiply, A, x, start, end, Ax)) - - for future in futures: - future.result() + change_vector = np.zeros_like(residual) + scalar_vector_multiply(omega, residual, change_vector) + longest_threads_time_accumulator.save_lap_and_reset() - longest_threads_time_accumulator.save_lap_and_reset() - residual = [0] * len(b) # tutaj zostanie przypisany wynik z vector_vector_subtraction - futures = [] + _x = np.zeros_like(x) + vector_vector_addition(x, change_vector, _x) + longest_threads_time_accumulator.save_lap_and_reset() - for i in range(num_threads): - start = i * chunk_size - end = n if i == num_threads - 1 else (i + 1) * chunk_size - futures.append(executor.submit(vector_vector_subtraction, b, Ax, start, end, residual)) - - for future in futures: - future.result() + x = _x.copy() - longest_threads_time_accumulator.save_lap_and_reset() - change_vector = [0] * len(residual) # zostanie tu przypisany wynik scalar_vector_multiply po pracy wątków - futures = [] - - for i in range(num_threads): - start = i * chunk_size - end = n if i == num_threads - 1 else (i + 1) * chunk_size - futures.append(executor.submit(scalar_vector_multiply, omega, residual, start, end, change_vector)) - - for future in futures: - future.result() - - longest_threads_time_accumulator.save_lap_and_reset() - _x = x[:] # do _x zostanie przez wątki przypisany wynik pracy w danej iteracji - futures = [] - - for i in range(num_threads): - start = i * chunk_size - end = n if i == num_threads - 1 else (i + 1) * chunk_size - futures.append(executor.submit(vector_vector_addition, x, change_vector, start, end, _x)) - - for future in futures: - future.result() - - longest_threads_time_accumulator.save_lap_and_reset() - x = _x[:] - - - if (linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol): - break - + if linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol: + break end_time = time.perf_counter() gc.enable() total_time = end_time - start_time sequential_time = total_time - longest_threads_time_accumulator.total_time - + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_threads_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") - - return x, 0 \ No newline at end of file + + return x, 0 diff --git a/code/threads_indep_old.py b/code/threads_indep_old.py new file mode 100644 index 00000000..7c5ecc5e --- /dev/null +++ b/code/threads_indep_old.py @@ -0,0 +1,103 @@ +import multiprocessing +import gc +import time +from concurrent.futures import ThreadPoolExecutor +from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time +import linear_algebra_utils as linAlg + + +@time_measurement_longest(longest_threads_time_accumulator) +def matrix_vector_multiply(A, input_x, start, end, Ax): + Ax[start:end] = [sum(x*y for x, y in zip(row, input_x)) for row in A[start:end]] + +@time_measurement_longest(longest_threads_time_accumulator) +def vector_vector_subtraction(b, Ax, start, end, residual): + residual[start:end] = [x-y for x, y in zip(b[start:end], Ax[start:end])] + +@time_measurement_longest(longest_threads_time_accumulator) +def scalar_vector_multiply(omega, vector, start, end, result): + result[start:end] = [omega * x for x in vector[start:end]] + +@time_measurement_longest(longest_threads_time_accumulator) +def vector_vector_addition(input_x, vector, start, end, output_x): + output_x[start:end] = [x+y for x, y in zip(input_x[start:end], vector[start:end])] + +def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): + longest_threads_time_accumulator.hard_reset() + + gc.disable() + start_time = time.perf_counter() + + n = len(b) + x0 = x0 if x0 is not None else [0.0] * len(b) + x = x0[:] + omega = 2 / (lambda_min + lambda_max) + num_threads = multiprocessing.cpu_count() + chunk_size = n // num_threads + + with ThreadPoolExecutor(max_workers=num_threads) as executor: # wątki są tworzone raz i nie są niszczone + for iteration in range(max_iterations): + + Ax = [0] * len(x) # tutaj zostanie przypisany wynik z mnożenia macierzy A z wektorem x + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(matrix_vector_multiply, A, x, start, end, Ax)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + residual = [0] * len(b) # tutaj zostanie przypisany wynik z vector_vector_subtraction + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(vector_vector_subtraction, b, Ax, start, end, residual)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + change_vector = [0] * len(residual) # zostanie tu przypisany wynik scalar_vector_multiply po pracy wątków + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(scalar_vector_multiply, omega, residual, start, end, change_vector)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + _x = x[:] # do _x zostanie przez wątki przypisany wynik pracy w danej iteracji + futures = [] + + for i in range(num_threads): + start = i * chunk_size + end = n if i == num_threads - 1 else (i + 1) * chunk_size + futures.append(executor.submit(vector_vector_addition, x, change_vector, start, end, _x)) + + for future in futures: + future.result() + + longest_threads_time_accumulator.save_lap_and_reset() + x = _x[:] + + + if (linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol): + break + + + end_time = time.perf_counter() + gc.enable() + total_time = end_time - start_time + sequential_time = total_time - longest_threads_time_accumulator.total_time + + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_threads_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") + + return x, 0 \ No newline at end of file From 28de076911a4cde56677d4ee8937f4c2d7595266 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Mon, 13 Jan 2025 16:22:25 +0000 Subject: [PATCH 5/8] feat: added usage instruction to readme, numba to requirements and generated report with fixed info about mpi --- README.md | 23 +++++++++++++++++++++++ code/requirements.txt | 3 ++- report/first_part/main.pdf | Bin 64632 -> 64620 bytes 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..a8a0487e --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +cd code + +pyenv init + +COPY AND PASTE PYENV CONFIGURATION TO YOUR SHELL CONFIG + +source ~/.zshrc (or your shell) + +pyenv install 3.11 + +pyenv global 3.11 + +Ensure that python DID change its version + +python --version + +python -m venv ./venv + +source ./venv/bin/activate + +pip install -r requirements.txt + +python main.py diff --git a/code/requirements.txt b/code/requirements.txt index 95d5e87f..72bf1c03 100644 --- a/code/requirements.txt +++ b/code/requirements.txt @@ -1,3 +1,4 @@ scipy pytest -dask \ No newline at end of file +dask +numba \ No newline at end of file diff --git a/report/first_part/main.pdf b/report/first_part/main.pdf index 2268fb0b6fac21b514600983882a8c64697a4d0e..6f37e13e94d32c38a16a5ee876158838e333d217 100644 GIT binary patch delta 17126 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 delta 17151 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 F0RW~~I+Xwb From e38f28a0b5ca83d212cd2881e16fa77d93944857 Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Mon, 13 Jan 2025 17:32:54 +0100 Subject: [PATCH 6/8] fix: remove numpy --- code/threads_indep.py | 24 ++++---- code/threads_indep_numba_numpy.py | 91 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 code/threads_indep_numba_numpy.py diff --git a/code/threads_indep.py b/code/threads_indep.py index deae5381..1a4fd1b2 100644 --- a/code/threads_indep.py +++ b/code/threads_indep.py @@ -1,16 +1,14 @@ import gc import time -import numpy as np from numba import njit, prange from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time import linear_algebra_utils as linAlg -# Funkcje równoległe z Numba + @njit(parallel=True) def numba_matrix_vector_multiply(A, input_x, Ax): - n, m = A.shape - for i in prange(n): - Ax[i] = np.dot(A[i], input_x) + for i in prange(len(A)): + Ax[i] = sum(A[i][j] * input_x[j] for j in range(len(input_x))) @njit(parallel=True) def numba_vector_vector_subtraction(b, Ax, residual): @@ -51,32 +49,30 @@ def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=Non gc.disable() start_time = time.perf_counter() - A = np.array(A) - b = np.array(b) - x0 = np.array(x0) if x0 is not None else np.zeros_like(b) - x = x0.copy() + x0 = x0 if x0 is not None else [0.0] * len(b) + x = x0[:] omega = 2 / (lambda_min + lambda_max) n = len(b) for iteration in range(max_iterations): - Ax = np.zeros_like(x) + Ax = [0.0] * n matrix_vector_multiply(A, x, Ax) longest_threads_time_accumulator.save_lap_and_reset() - residual = np.zeros_like(b) + residual = [0.0] * n vector_vector_subtraction(b, Ax, residual) longest_threads_time_accumulator.save_lap_and_reset() - change_vector = np.zeros_like(residual) + change_vector = [0.0] * n scalar_vector_multiply(omega, residual, change_vector) longest_threads_time_accumulator.save_lap_and_reset() - _x = np.zeros_like(x) + _x = [0.0] * n vector_vector_addition(x, change_vector, _x) longest_threads_time_accumulator.save_lap_and_reset() - x = _x.copy() + x = _x[:] if linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol: break diff --git a/code/threads_indep_numba_numpy.py b/code/threads_indep_numba_numpy.py new file mode 100644 index 00000000..deae5381 --- /dev/null +++ b/code/threads_indep_numba_numpy.py @@ -0,0 +1,91 @@ +import gc +import time +import numpy as np +from numba import njit, prange +from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time +import linear_algebra_utils as linAlg + +# Funkcje równoległe z Numba +@njit(parallel=True) +def numba_matrix_vector_multiply(A, input_x, Ax): + n, m = A.shape + for i in prange(n): + Ax[i] = np.dot(A[i], input_x) + +@njit(parallel=True) +def numba_vector_vector_subtraction(b, Ax, residual): + for i in prange(len(b)): + residual[i] = b[i] - Ax[i] + +@njit(parallel=True) +def numba_scalar_vector_multiply(omega, vector, result): + for i in prange(len(vector)): + result[i] = omega * vector[i] + +@njit(parallel=True) +def numba_vector_vector_addition(input_x, vector, output_x): + for i in prange(len(input_x)): + output_x[i] = input_x[i] + vector[i] + +# Funkcje z dekoratorem +@time_measurement_longest(longest_threads_time_accumulator) +def matrix_vector_multiply(A, input_x, Ax): + numba_matrix_vector_multiply(A, input_x, Ax) + +@time_measurement_longest(longest_threads_time_accumulator) +def vector_vector_subtraction(b, Ax, residual): + numba_vector_vector_subtraction(b, Ax, residual) + +@time_measurement_longest(longest_threads_time_accumulator) +def scalar_vector_multiply(omega, vector, result): + numba_scalar_vector_multiply(omega, vector, result) + +@time_measurement_longest(longest_threads_time_accumulator) +def vector_vector_addition(input_x, vector, output_x): + numba_vector_vector_addition(input_x, vector, output_x) + +# Metoda Richardson z obsługą wątków +def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): + longest_threads_time_accumulator.hard_reset() + + gc.disable() + start_time = time.perf_counter() + + A = np.array(A) + b = np.array(b) + x0 = np.array(x0) if x0 is not None else np.zeros_like(b) + x = x0.copy() + + omega = 2 / (lambda_min + lambda_max) + n = len(b) + + for iteration in range(max_iterations): + Ax = np.zeros_like(x) + matrix_vector_multiply(A, x, Ax) + longest_threads_time_accumulator.save_lap_and_reset() + + residual = np.zeros_like(b) + vector_vector_subtraction(b, Ax, residual) + longest_threads_time_accumulator.save_lap_and_reset() + + change_vector = np.zeros_like(residual) + scalar_vector_multiply(omega, residual, change_vector) + longest_threads_time_accumulator.save_lap_and_reset() + + _x = np.zeros_like(x) + vector_vector_addition(x, change_vector, _x) + longest_threads_time_accumulator.save_lap_and_reset() + + x = _x.copy() + + if linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol: + break + + end_time = time.perf_counter() + gc.enable() + total_time = end_time - start_time + sequential_time = total_time - longest_threads_time_accumulator.total_time + + print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_threads_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") + + return x, 0 From 9f8671352c6c8bcb6d5ee079824826d8c9b03024 Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Mon, 13 Jan 2025 18:21:02 +0100 Subject: [PATCH 7/8] fix: minor errors --- code/threads_indep.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/code/threads_indep.py b/code/threads_indep.py index 1a4fd1b2..08103025 100644 --- a/code/threads_indep.py +++ b/code/threads_indep.py @@ -1,5 +1,6 @@ import gc import time +import numpy as np from numba import njit, prange from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time import linear_algebra_utils as linAlg @@ -8,23 +9,28 @@ import linear_algebra_utils as linAlg @njit(parallel=True) def numba_matrix_vector_multiply(A, input_x, Ax): for i in prange(len(A)): - Ax[i] = sum(A[i][j] * input_x[j] for j in range(len(input_x))) + acc = 0.0 + for j in range(len(input_x)): + acc += A[i][j] * input_x[j] + Ax[i] = acc @njit(parallel=True) def numba_vector_vector_subtraction(b, Ax, residual): for i in prange(len(b)): residual[i] = b[i] - Ax[i] -@njit(parallel=True) +@njit(nopython=True) def numba_scalar_vector_multiply(omega, vector, result): - for i in prange(len(vector)): - result[i] = omega * vector[i] + omega_real = omega.real + for i in range(len(vector)): + result[i] = omega_real * vector[i] @njit(parallel=True) def numba_vector_vector_addition(input_x, vector, output_x): for i in prange(len(input_x)): output_x[i] = input_x[i] + vector[i] + # Funkcje z dekoratorem @time_measurement_longest(longest_threads_time_accumulator) def matrix_vector_multiply(A, input_x, Ax): From 5279920e01a85141a314275951ccb7380a76a076 Mon Sep 17 00:00:00 2001 From: Gromiusz Date: Mon, 13 Jan 2025 23:19:32 +0100 Subject: [PATCH 8/8] remove: file --- code/threads_indep_numba_numpy.py | 91 ------------------------------ report/first_part/main.pdf | Bin 64620 -> 0 bytes 2 files changed, 91 deletions(-) delete mode 100644 code/threads_indep_numba_numpy.py delete mode 100644 report/first_part/main.pdf diff --git a/code/threads_indep_numba_numpy.py b/code/threads_indep_numba_numpy.py deleted file mode 100644 index deae5381..00000000 --- a/code/threads_indep_numba_numpy.py +++ /dev/null @@ -1,91 +0,0 @@ -import gc -import time -import numpy as np -from numba import njit, prange -from time_measurement import time_measurement_longest, longest_threads_time_accumulator, tests_time -import linear_algebra_utils as linAlg - -# Funkcje równoległe z Numba -@njit(parallel=True) -def numba_matrix_vector_multiply(A, input_x, Ax): - n, m = A.shape - for i in prange(n): - Ax[i] = np.dot(A[i], input_x) - -@njit(parallel=True) -def numba_vector_vector_subtraction(b, Ax, residual): - for i in prange(len(b)): - residual[i] = b[i] - Ax[i] - -@njit(parallel=True) -def numba_scalar_vector_multiply(omega, vector, result): - for i in prange(len(vector)): - result[i] = omega * vector[i] - -@njit(parallel=True) -def numba_vector_vector_addition(input_x, vector, output_x): - for i in prange(len(input_x)): - output_x[i] = input_x[i] + vector[i] - -# Funkcje z dekoratorem -@time_measurement_longest(longest_threads_time_accumulator) -def matrix_vector_multiply(A, input_x, Ax): - numba_matrix_vector_multiply(A, input_x, Ax) - -@time_measurement_longest(longest_threads_time_accumulator) -def vector_vector_subtraction(b, Ax, residual): - numba_vector_vector_subtraction(b, Ax, residual) - -@time_measurement_longest(longest_threads_time_accumulator) -def scalar_vector_multiply(omega, vector, result): - numba_scalar_vector_multiply(omega, vector, result) - -@time_measurement_longest(longest_threads_time_accumulator) -def vector_vector_addition(input_x, vector, output_x): - numba_vector_vector_addition(input_x, vector, output_x) - -# Metoda Richardson z obsługą wątków -def RichardsonMethodThreads(A, b, lambda_min, lambda_max, max_iterations, x0=None, tol=1e-5): - longest_threads_time_accumulator.hard_reset() - - gc.disable() - start_time = time.perf_counter() - - A = np.array(A) - b = np.array(b) - x0 = np.array(x0) if x0 is not None else np.zeros_like(b) - x = x0.copy() - - omega = 2 / (lambda_min + lambda_max) - n = len(b) - - for iteration in range(max_iterations): - Ax = np.zeros_like(x) - matrix_vector_multiply(A, x, Ax) - longest_threads_time_accumulator.save_lap_and_reset() - - residual = np.zeros_like(b) - vector_vector_subtraction(b, Ax, residual) - longest_threads_time_accumulator.save_lap_and_reset() - - change_vector = np.zeros_like(residual) - scalar_vector_multiply(omega, residual, change_vector) - longest_threads_time_accumulator.save_lap_and_reset() - - _x = np.zeros_like(x) - vector_vector_addition(x, change_vector, _x) - longest_threads_time_accumulator.save_lap_and_reset() - - x = _x.copy() - - if linAlg.SequentialLinearAlgebraUtils.vector_norm(residual) < tol: - break - - end_time = time.perf_counter() - gc.enable() - total_time = end_time - start_time - sequential_time = total_time - longest_threads_time_accumulator.total_time - - print(f"Total: {total_time:.3e}s, Seq: {sequential_time:.3e}s, Parallel (threads): {longest_threads_time_accumulator.total_time:.3e}s, Tests time: {tests_time.total_time:.3e}s") - - return x, 0 diff --git a/report/first_part/main.pdf b/report/first_part/main.pdf deleted file mode 100644 index 6f37e13e94d32c38a16a5ee876158838e333d217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64620 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 AEC2ui