diff --git a/docs/conf.py b/docs/conf.py index ec0fb703f..a6f41a5f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,15 +8,15 @@ import os -project = 'Parallel Programming Course' -copyright = '2025, Learning Process' -author = 'Learning Process' +project = "Parallel Programming Course" +copyright = "2025, Learning Process" +author = "Learning Process" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ - 'breathe', + "breathe", ] breathe_projects = { @@ -24,16 +24,16 @@ } breathe_default_project = "ParallelProgrammingCourse" -templates_path = ['_templates'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] -locale_dirs = ['locale'] +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +locale_dirs = ["locale"] gettext_compact = False # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] html_sidebars = { "**": [ diff --git a/scoreboard/main.py b/scoreboard/main.py index 67a95913c..8dfa1d0de 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -5,52 +5,54 @@ import csv from jinja2 import Environment, FileSystemLoader -task_types = ['all', 'mpi', 'omp', 'seq', 'stl', 'tbb'] +task_types = ["all", "mpi", "omp", "seq", "stl", "tbb"] -tasks_dir = Path('tasks') +tasks_dir = Path("tasks") directories = defaultdict(dict) if tasks_dir.exists() and tasks_dir.is_dir(): for task_name_dir in tasks_dir.iterdir(): - if task_name_dir.is_dir() and task_name_dir.name not in ['common']: + if task_name_dir.is_dir() and task_name_dir.name not in ["common"]: task_name = task_name_dir.name for task_type in task_types: task_type_dir = task_name_dir / task_type if task_type_dir.exists() and task_type_dir.is_dir(): if task_name.endswith("_disabled"): - clean_task_name = task_name[:-len("_disabled")] + clean_task_name = task_name[: -len("_disabled")] directories[clean_task_name][task_type] = "disabled" else: directories[task_name][task_type] = "done" config_path = Path(__file__).parent / "data" / "threads-config.yml" assert config_path.exists(), f"Config file not found: {config_path}" -with open(config_path, 'r') as file: +with open(config_path, "r") as file: cfg = yaml.safe_load(file) assert cfg, "Configuration is empty" plagiarism_config_path = Path(__file__).parent / "data" / "plagiarism.yml" -with open(plagiarism_config_path, 'r') as file: +with open(plagiarism_config_path, "r") as file: plagiarism_cfg = yaml.safe_load(file) assert plagiarism_cfg, "Plagiarism configuration is empty" env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates")) -perf_stat_file_path = Path(__file__).parent.parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv" +perf_stat_file_path = ( + Path(__file__).parent.parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv" +) # Read and parse performance statistics CSV perf_stats = dict() if perf_stat_file_path.exists(): - with open(perf_stat_file_path, 'r', newline='') as csvfile: + with open(perf_stat_file_path, "r", newline="") as csvfile: reader = csv.DictReader(csvfile) for row in reader: - perf_stats[row['Task']] = { - "seq": row['SEQ'], - "omp": row['OMP'], - "tbb": row['TBB'], - "stl": row['STL'], - "all": row['ALL'], + perf_stats[row["Task"]] = { + "seq": row["SEQ"], + "omp": row["OMP"], + "tbb": row["TBB"], + "stl": row["STL"], + "all": row["ALL"], "mpi": "N/A", } else: @@ -99,12 +101,12 @@ template = env.get_template("index.html.j2") html_content = template.render(task_types=task_types, rows=rows) -parser = argparse.ArgumentParser(description='Generate HTML scoreboard.') -parser.add_argument('-o', '--output', type=str, required=True, help='Output file path') +parser = argparse.ArgumentParser(description="Generate HTML scoreboard.") +parser.add_argument("-o", "--output", type=str, required=True, help="Output file path") args = parser.parse_args() output_file = Path(args.output) / "index.html" -with open(output_file, 'w') as file: +with open(output_file, "w") as file: file.write(html_content) print(f"HTML page generated at {output_file}") diff --git a/scripts/create_perf_table.py b/scripts/create_perf_table.py index e588af85a..3d2dc277e 100644 --- a/scripts/create_perf_table.py +++ b/scripts/create_perf_table.py @@ -5,8 +5,12 @@ import csv parser = argparse.ArgumentParser() -parser.add_argument('-i', '--input', help='Input file path (logs of perf tests, .txt)', required=True) -parser.add_argument('-o', '--output', help='Output file path (path to .xlsx table)', required=True) +parser.add_argument( + "-i", "--input", help="Input file path (logs of perf tests, .txt)", required=True +) +parser.add_argument( + "-o", "--output", help="Output file path (path to .xlsx table)", required=True +) args = parser.parse_args() logs_path = os.path.abspath(args.input) xlsx_path = os.path.abspath(args.output) @@ -19,7 +23,7 @@ logs_file = open(logs_path, "r") logs_lines = logs_file.readlines() for line in logs_lines: - pattern = r'tasks[\/|\\](\w*)[\/|\\](\w*):(\w*):(-*\d*\.\d*)' + pattern = r"tasks[\/|\\](\w*)[\/|\\](\w*):(\w*):(-*\d*\.\d*)" result = re.findall(pattern, line) if len(result): task_name = result[0][1] @@ -31,7 +35,7 @@ result_tables[perf_type][task_name][ttype] = -1.0 for line in logs_lines: - pattern = r'tasks[\/|\\](\w*)[\/|\\](\w*):(\w*):(-*\d*\.\d*)' + pattern = r"tasks[\/|\\](\w*)[\/|\\](\w*):(\w*):(-*\d*\.\d*)" result = re.findall(pattern, line) if len(result): task_type = result[0][0] @@ -45,37 +49,72 @@ for table_name in result_tables: - workbook = xlsxwriter.Workbook(os.path.join(xlsx_path, table_name + '_perf_table.xlsx')) + workbook = xlsxwriter.Workbook( + os.path.join(xlsx_path, table_name + "_perf_table.xlsx") + ) worksheet = workbook.add_worksheet() - worksheet.set_column('A:Z', 23) - right_bold_border = workbook.add_format({'bold': True, 'right': 2, 'bottom': 2}) - bottom_bold_border = workbook.add_format({'bold': True, 'bottom': 2}) + worksheet.set_column("A:Z", 23) + right_bold_border = workbook.add_format({"bold": True, "right": 2, "bottom": 2}) + bottom_bold_border = workbook.add_format({"bold": True, "bottom": 2}) cpu_num = os.environ.get("PPC_NUM_PROC") if cpu_num is None: - raise EnvironmentError("Required environment variable 'PPC_NUM_PROC' is not set.") + raise EnvironmentError( + "Required environment variable 'PPC_NUM_PROC' is not set." + ) cpu_num = int(cpu_num) worksheet.write(0, 0, "cpu_num = " + str(cpu_num), right_bold_border) it = 1 for type_of_task in list_of_type_of_tasks: - worksheet.write(0, it, "T_" + type_of_task + "(" + str(cpu_num) + ")", bottom_bold_border) + worksheet.write( + 0, it, "T_" + type_of_task + "(" + str(cpu_num) + ")", bottom_bold_border + ) it += 1 - worksheet.write(0, it, "S(" + str(cpu_num) + ")" + " = " + - "T_seq(" + str(cpu_num) + ")" + " / " + - "T_" + type_of_task + "(" + str(cpu_num) + ")", bottom_bold_border) + worksheet.write( + 0, + it, + "S(" + + str(cpu_num) + + ")" + + " = " + + "T_seq(" + + str(cpu_num) + + ")" + + " / " + + "T_" + + type_of_task + + "(" + + str(cpu_num) + + ")", + bottom_bold_border, + ) it += 1 - worksheet.write(0, it, "Eff(" + str(cpu_num) + ")" + " = " + - "S(" + str(cpu_num) + ")" + " / " + str(cpu_num), right_bold_border) + worksheet.write( + 0, + it, + "Eff(" + + str(cpu_num) + + ")" + + " = " + + "S(" + + str(cpu_num) + + ")" + + " / " + + str(cpu_num), + right_bold_border, + ) it += 1 it = 1 for task_name in list(set(set_of_task_name)): - worksheet.write(it, 0, task_name, workbook.add_format({'bold': True, 'right': 2})) + worksheet.write( + it, 0, task_name, workbook.add_format({"bold": True, "right": 2}) + ) it += 1 it_i = 1 it_j = 1 - right_border = workbook.add_format({'right': 2}) + right_border = workbook.add_format({"right": 2}) for task_name in list(set(set_of_task_name)): for type_of_task in list_of_type_of_tasks: if task_name not in result_tables[table_name].keys(): @@ -104,19 +143,27 @@ it_j += 1 workbook.close() # Dump CSV for performance times - csv_file = os.path.join(xlsx_path, table_name + '_perf_table.csv') - with open(csv_file, 'w', newline='') as csvfile: + csv_file = os.path.join(xlsx_path, table_name + "_perf_table.csv") + with open(csv_file, "w", newline="") as csvfile: writer = csv.writer(csvfile) # Write header: Task, SEQ, OMP, TBB, STL, ALL - writer.writerow(['Task', 'SEQ', 'OMP', 'TBB', 'STL', 'ALL']) + writer.writerow(["Task", "SEQ", "OMP", "TBB", "STL", "ALL"]) for task_name in sorted(result_tables[table_name].keys()): - seq_time = result_tables[table_name][task_name]['seq'] + seq_time = result_tables[table_name][task_name]["seq"] row = [ task_name, - 1.0 if seq_time != 0 else '?', - (result_tables[table_name][task_name]['omp'] / seq_time) if seq_time != 0 else '?', - (result_tables[table_name][task_name]['tbb'] / seq_time) if seq_time != 0 else '?', - (result_tables[table_name][task_name]['stl'] / seq_time) if seq_time != 0 else '?', - (result_tables[table_name][task_name]['all'] / seq_time) if seq_time != 0 else '?', + 1.0 if seq_time != 0 else "?", + (result_tables[table_name][task_name]["omp"] / seq_time) + if seq_time != 0 + else "?", + (result_tables[table_name][task_name]["tbb"] / seq_time) + if seq_time != 0 + else "?", + (result_tables[table_name][task_name]["stl"] / seq_time) + if seq_time != 0 + else "?", + (result_tables[table_name][task_name]["all"] / seq_time) + if seq_time != 0 + else "?", ] writer.writerow(row) diff --git a/scripts/run_tests.py b/scripts/run_tests.py index 0a11e7b14..473060490 100755 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -9,29 +9,28 @@ def init_cmd_args(): import argparse + parser = argparse.ArgumentParser() parser.add_argument( "--running-type", required=True, choices=["threads", "processes", "performance"], - help="Specify the execution mode. Choose 'threads' for multithreading or 'processes' for multiprocessing." + help="Specify the execution mode. Choose 'threads' for multithreading or 'processes' for multiprocessing.", ) parser.add_argument( "--additional-mpi-args", required=False, default="", - help="Additional MPI arguments to pass to the mpirun command (optional)." + help="Additional MPI arguments to pass to the mpirun command (optional).", ) parser.add_argument( "--counts", nargs="+", type=int, - help="List of process/thread counts to run sequentially" + help="List of process/thread counts to run sequentially", ) parser.add_argument( - "--verbose", - action="store_true", - help="Print commands executed by the script" + "--verbose", action="store_true", help="Print commands executed by the script" ) args = parser.parse_args() _args_dict = vars(args) @@ -46,7 +45,9 @@ def __init__(self, verbose=False): self.work_dir = None self.verbose = verbose - self.valgrind_cmd = "valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all" + self.valgrind_cmd = ( + "valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all" + ) if platform.system() == "Windows": self.mpi_exec = "mpiexec" @@ -64,12 +65,16 @@ def setup_env(self, ppc_env): self.__ppc_num_threads = self.__ppc_env.get("PPC_NUM_THREADS") if self.__ppc_num_threads is None: - raise EnvironmentError("Required environment variable 'PPC_NUM_THREADS' is not set.") + raise EnvironmentError( + "Required environment variable 'PPC_NUM_THREADS' is not set." + ) self.__ppc_env["OMP_NUM_THREADS"] = self.__ppc_num_threads self.__ppc_num_proc = self.__ppc_env.get("PPC_NUM_PROC") if self.__ppc_num_proc is None: - raise EnvironmentError("Required environment variable 'PPC_NUM_PROC' is not set.") + raise EnvironmentError( + "Required environment variable 'PPC_NUM_PROC' is not set." + ) if (Path(self.__get_project_path()) / "install").exists(): self.work_dir = Path(self.__get_project_path()) / "install" / "bin" @@ -99,40 +104,45 @@ def run_threads(self): for task_type in ["seq", "stl"]: self.__run_exec( shlex.split(self.valgrind_cmd) - + [str(self.work_dir / 'ppc_func_tests')] - + self.__get_gtest_settings(1, '_' + task_type + '_') + + [str(self.work_dir / "ppc_func_tests")] + + self.__get_gtest_settings(1, "_" + task_type + "_") ) for task_type in ["omp", "seq", "stl", "tbb"]: self.__run_exec( - [str(self.work_dir / 'ppc_func_tests')] + self.__get_gtest_settings(3, '_' + task_type + '_') + [str(self.work_dir / "ppc_func_tests")] + + self.__get_gtest_settings(3, "_" + task_type + "_") ) def run_core(self): if platform.system() == "Linux" and not self.__ppc_env.get("PPC_ASAN_RUN"): self.__run_exec( shlex.split(self.valgrind_cmd) - + [str(self.work_dir / 'core_func_tests')] - + self.__get_gtest_settings(1, '*') + + [str(self.work_dir / "core_func_tests")] + + self.__get_gtest_settings(1, "*") + ["--gtest_filter=*:-*_disabled_valgrind"] ) self.__run_exec( - [str(self.work_dir / 'core_func_tests')] + self.__get_gtest_settings(1, '*') + [str(self.work_dir / "core_func_tests")] + self.__get_gtest_settings(1, "*") ) def run_processes(self, additional_mpi_args): ppc_num_proc = self.__ppc_env.get("PPC_NUM_PROC") if ppc_num_proc is None: - raise EnvironmentError("Required environment variable 'PPC_NUM_PROC' is not set.") + raise EnvironmentError( + "Required environment variable 'PPC_NUM_PROC' is not set." + ) - mpi_running = [self.mpi_exec] + shlex.split(additional_mpi_args) + ["-np", ppc_num_proc] + mpi_running = ( + [self.mpi_exec] + shlex.split(additional_mpi_args) + ["-np", ppc_num_proc] + ) if not self.__ppc_env.get("PPC_ASAN_RUN"): for task_type in ["all", "mpi"]: self.__run_exec( mpi_running - + [str(self.work_dir / 'ppc_func_tests')] - + self.__get_gtest_settings(10, '_' + task_type) + + [str(self.work_dir / "ppc_func_tests")] + + self.__get_gtest_settings(10, "_" + task_type) ) def run_performance(self): @@ -141,13 +151,14 @@ def run_performance(self): for task_type in ["all", "mpi"]: self.__run_exec( mpi_running - + [str(self.work_dir / 'ppc_perf_tests')] - + self.__get_gtest_settings(1, '_' + task_type) + + [str(self.work_dir / "ppc_perf_tests")] + + self.__get_gtest_settings(1, "_" + task_type) ) for task_type in ["omp", "seq", "stl", "tbb"]: self.__run_exec( - [str(self.work_dir / 'ppc_perf_tests')] + self.__get_gtest_settings(1, '_' + task_type) + [str(self.work_dir / "ppc_perf_tests")] + + self.__get_gtest_settings(1, "_" + task_type) ) diff --git a/scripts/variants_generation.py b/scripts/variants_generation.py index 17f078abf..7e1284b15 100644 --- a/scripts/variants_generation.py +++ b/scripts/variants_generation.py @@ -12,7 +12,9 @@ def get_project_path(): def generate_group_table(_num_tasks, _num_students, _num_variants, _csv_file): if _num_tasks != len(_num_variants): - raise Exception(f"Count of students: {_num_tasks} != count of list of variants: {len(_num_variants)}") + raise Exception( + f"Count of students: {_num_tasks} != count of list of variants: {len(_num_variants)}" + ) list_of_tasks = [] str_of_print = "" @@ -27,16 +29,18 @@ def generate_group_table(_num_tasks, _num_students, _num_variants, _csv_file): shuffled_list_of_variants.append(variant) result_variants = np.concatenate(shuffled_list_of_variants) list_of_tasks.append(result_variants[:_num_students]) - str_of_print += '%d,' - str_of_headers += 'Task ' + str(i + 1) + ',' + str_of_print += "%d," + str_of_headers += "Task " + str(i + 1) + "," str_of_print = str_of_print[:-1] str_of_headers = str_of_headers[:-1] - np.savetxt(_csv_file, np.dstack(list_of_tasks)[0], str_of_print, header=str_of_headers) + np.savetxt( + _csv_file, np.dstack(list_of_tasks)[0], str_of_print, header=str_of_headers + ) - workbook = Workbook(_csv_file[:-4] + '.xlsx') + workbook = Workbook(_csv_file[:-4] + ".xlsx") worksheet = workbook.add_worksheet() - with open(_csv_file, 'rt') as f: + with open(_csv_file, "rt") as f: reader = csv.reader(f) for r, row in enumerate(reader): for c, col in enumerate(row): @@ -59,5 +63,5 @@ def generate_group_table(_num_tasks, _num_students, _num_variants, _csv_file): path_to_results.mkdir(parents=True, exist_ok=True) for num_students, index in zip(list_students, range(len(list_students))): - csv_path = path_to_results / f'variants_group_{index + 1}.csv' + csv_path = path_to_results / f"variants_group_{index + 1}.csv" generate_group_table(num_tasks, num_students, num_variants, csv_path.as_posix())