From d51431a1c39d3740b9c91a827187ec09515b836f Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Wed, 2 Jul 2025 18:46:03 +0200 Subject: [PATCH 01/18] refactor CMake scripts: centralize target linking functionality --- CMakeLists.txt | 1 + cmake/gtest.cmake | 12 +++++++ cmake/json.cmake | 11 +++++++ cmake/libenvpp.cmake | 18 ++++++++++ cmake/mpi.cmake | 17 +++++++++- cmake/onetbb.cmake | 14 ++++++++ cmake/openmp.cmake | 10 ++++++ cmake/stb.cmake | 5 +++ modules/core/CMakeLists.txt | 65 +++++-------------------------------- 9 files changed, 95 insertions(+), 58 deletions(-) create mode 100644 cmake/stb.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 88f343287..1a0982a27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ include(cmake/modes.cmake) include(cmake/sanitizers.cmake) include(cmake/json.cmake) include(cmake/libenvpp.cmake) +include(cmake/stb.cmake) ################# Parallel programming technologies ################# diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake index a9bdd6f13..eb41a7cd3 100644 --- a/cmake/gtest.cmake +++ b/cmake/gtest.cmake @@ -24,3 +24,15 @@ ExternalProject_Add( "${CMAKE_COMMAND}" --install "${CMAKE_CURRENT_BINARY_DIR}/ppc_googletest/build" --prefix "${CMAKE_CURRENT_BINARY_DIR}/ppc_googletest/install") + +function(ppc_link_gtest exec_func_lib) + # Add external project include directories + target_include_directories( + ${exec_func_lib} + PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/googletest/googletest/include) + + add_dependencies(${exec_func_lib} ppc_googletest) + target_link_directories(${exec_func_lib} PUBLIC + "${CMAKE_BINARY_DIR}/ppc_googletest/install/lib") + target_link_libraries(${exec_func_lib} PUBLIC gtest gtest_main) +endfunction() diff --git a/cmake/json.cmake b/cmake/json.cmake index 89070d4b7..882553058 100644 --- a/cmake/json.cmake +++ b/cmake/json.cmake @@ -19,3 +19,14 @@ ExternalProject_Add( INSTALL_COMMAND "${CMAKE_COMMAND}" --install "${CMAKE_CURRENT_BINARY_DIR}/ppc_json/build" --prefix "${CMAKE_CURRENT_BINARY_DIR}/ppc_json/install") + +function(ppc_link_json exec_func_lib) + # Add external project include directories + target_include_directories( + ${exec_func_lib} + PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/json/include) + + add_dependencies(${exec_func_lib} ppc_json) + target_link_directories(${exec_func_lib} INTERFACE + "${CMAKE_BINARY_DIR}/ppc_json/install/include") +endfunction() \ No newline at end of file diff --git a/cmake/libenvpp.cmake b/cmake/libenvpp.cmake index 564a7d488..e150de19b 100644 --- a/cmake/libenvpp.cmake +++ b/cmake/libenvpp.cmake @@ -38,3 +38,21 @@ if(WIN32) else() set(PPC_ENVPP_LIB_NAME envpp) endif() + +function(ppc_link_envpp exec_func_lib) + # Add external project include directories + target_include_directories( + ${exec_func_lib} + PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/libenvpp/include) + target_include_directories( + ${exec_func_lib} SYSTEM + PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/libenvpp/external/fmt/include) + + add_dependencies(${exec_func_lib} ppc_libenvpp) + target_link_directories(${exec_func_lib} PUBLIC + "${CMAKE_BINARY_DIR}/ppc_libenvpp/install/lib") + target_link_directories(${exec_func_lib} PUBLIC + "${CMAKE_BINARY_DIR}/ppc_libenvpp/build") + target_link_libraries(${exec_func_lib} PUBLIC ${PPC_ENVPP_LIB_NAME}) + target_link_libraries(${exec_func_lib} PUBLIC ${PPC_FMT_LIB_NAME}) +endfunction() diff --git a/cmake/mpi.cmake b/cmake/mpi.cmake index 8b307ccdd..9394ff932 100644 --- a/cmake/mpi.cmake +++ b/cmake/mpi.cmake @@ -1,4 +1,19 @@ find_package(MPI REQUIRED) if(NOT MPI_FOUND) message(FATAL_ERROR "MPI NOT FOUND") -endif(MPI_FOUND) +endif() + +function(ppc_link_mpi exec_func_lib) + find_package(MPI REQUIRED) + if(MPI_COMPILE_FLAGS) + set_target_properties(${exec_func_lib} PROPERTIES COMPILE_FLAGS + "${MPI_COMPILE_FLAGS}") + endif(MPI_COMPILE_FLAGS) + + if(MPI_LINK_FLAGS) + set_target_properties(${exec_func_lib} PROPERTIES LINK_FLAGS + "${MPI_LINK_FLAGS}") + endif(MPI_LINK_FLAGS) + target_include_directories(${exec_func_lib} PUBLIC ${MPI_INCLUDE_PATH}) + target_link_libraries(${exec_func_lib} PUBLIC ${MPI_LIBRARIES}) +endfunction() diff --git a/cmake/onetbb.cmake b/cmake/onetbb.cmake index df89aa354..b14b2ed0e 100644 --- a/cmake/onetbb.cmake +++ b/cmake/onetbb.cmake @@ -42,3 +42,17 @@ if(cmake_build_type_lower STREQUAL "debug") else() set(PPC_TBB_LIB_NAME tbb) endif() + +function(ppc_link_tbb exec_func_lib) + # Add external project include directories + target_include_directories( + ${exec_func_lib} + PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/onetbb/include) + + add_dependencies(${exec_func_lib} ppc_onetbb) + target_link_directories(${exec_func_lib} PUBLIC + ${CMAKE_BINARY_DIR}/ppc_onetbb/install/lib) + if(NOT MSVC) + target_link_libraries(${exec_func_lib} PUBLIC ${PPC_TBB_LIB_NAME}) + endif() +endfunction() diff --git a/cmake/openmp.cmake b/cmake/openmp.cmake index 445815153..33b56e339 100644 --- a/cmake/openmp.cmake +++ b/cmake/openmp.cmake @@ -23,3 +23,13 @@ if(OpenMP_FOUND) else(OpenMP_FOUND) message(FATAL_ERROR "OpenMP NOT FOUND") endif(OpenMP_FOUND) + +function(ppc_link_threads exec_func_lib) + target_link_libraries(${exec_func_lib} PUBLIC Threads::Threads) +endfunction() + +function(ppc_link_openmp exec_func_lib) + find_package(OpenMP REQUIRED) + target_link_libraries(${exec_func_lib} PUBLIC ${OpenMP_libomp_LIBRARY} + OpenMP::OpenMP_CXX) +endfunction() diff --git a/cmake/stb.cmake b/cmake/stb.cmake new file mode 100644 index 000000000..2770d4440 --- /dev/null +++ b/cmake/stb.cmake @@ -0,0 +1,5 @@ +function(ppc_link_stb exec_func_lib) + add_library(stb_image STATIC ${CMAKE_SOURCE_DIR}/3rdparty/stb_image_wrapper.cpp) + target_include_directories(stb_image PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/stb) + target_link_libraries(${exec_func_lib} PUBLIC stb_image) +endfunction() \ No newline at end of file diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 318572711..487b2c9f5 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -29,63 +29,14 @@ target_include_directories( ${exec_func_lib} PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty ${CMAKE_SOURCE_DIR}/modules ${CMAKE_SOURCE_DIR}/tasks) -# Add external project include directories -target_include_directories( - ${exec_func_lib} - PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/onetbb/include - ${CMAKE_SOURCE_DIR}/3rdparty/json/include - ${CMAKE_SOURCE_DIR}/3rdparty/googletest/googletest/include - ${CMAKE_SOURCE_DIR}/3rdparty/libenvpp/include) -target_include_directories( - ${exec_func_lib} SYSTEM - PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/libenvpp/external/fmt/include) - -add_dependencies(${exec_func_lib} ppc_libenvpp) -target_link_directories(${exec_func_lib} PUBLIC - "${CMAKE_BINARY_DIR}/ppc_libenvpp/install/lib") -target_link_directories(${exec_func_lib} PUBLIC - "${CMAKE_BINARY_DIR}/ppc_libenvpp/build") -target_link_libraries(${exec_func_lib} PUBLIC ${PPC_ENVPP_LIB_NAME}) -target_link_libraries(${exec_func_lib} PUBLIC ${PPC_FMT_LIB_NAME}) - -add_dependencies(${exec_func_lib} ppc_json) -target_link_directories(${exec_func_lib} INTERFACE - "${CMAKE_BINARY_DIR}/ppc_json/install/include") - -add_dependencies(${exec_func_lib} ppc_googletest) -target_link_directories(${exec_func_lib} PUBLIC - "${CMAKE_BINARY_DIR}/ppc_googletest/install/lib") -target_link_libraries(${exec_func_lib} PUBLIC gtest gtest_main) - -target_link_libraries(${exec_func_lib} PUBLIC Threads::Threads) - -find_package(OpenMP REQUIRED) -target_link_libraries(${exec_func_lib} PUBLIC ${OpenMP_libomp_LIBRARY} - OpenMP::OpenMP_CXX) - -add_dependencies(${exec_func_lib} ppc_onetbb) -target_link_directories(${exec_func_lib} PUBLIC - ${CMAKE_BINARY_DIR}/ppc_onetbb/install/lib) -if(NOT MSVC) - target_link_libraries(${exec_func_lib} PUBLIC ${PPC_TBB_LIB_NAME}) -endif() - -find_package(MPI REQUIRED) -if(MPI_COMPILE_FLAGS) - set_target_properties(${exec_func_lib} PROPERTIES COMPILE_FLAGS - "${MPI_COMPILE_FLAGS}") -endif(MPI_COMPILE_FLAGS) - -if(MPI_LINK_FLAGS) - set_target_properties(${exec_func_lib} PROPERTIES LINK_FLAGS - "${MPI_LINK_FLAGS}") -endif(MPI_LINK_FLAGS) -target_include_directories(${exec_func_lib} PUBLIC ${MPI_INCLUDE_PATH}) -target_link_libraries(${exec_func_lib} PUBLIC ${MPI_LIBRARIES}) - -add_library(stb_image STATIC ${CMAKE_SOURCE_DIR}/3rdparty/stb_image_wrapper.cpp) -target_include_directories(stb_image PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/stb) -target_link_libraries(${exec_func_lib} PUBLIC stb_image) +ppc_link_envpp(${exec_func_lib}) +ppc_link_json(${exec_func_lib}) +ppc_link_gtest(${exec_func_lib}) +ppc_link_threads(${exec_func_lib}) +ppc_link_openmp(${exec_func_lib}) +ppc_link_tbb(${exec_func_lib}) +ppc_link_mpi(${exec_func_lib}) +ppc_link_stb(${exec_func_lib}) add_executable(${exec_func_tests} ${FUNC_TESTS_SOURCE_FILES}) From e329437dc77129e800bb300fa1a5d9ea496894ea Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Wed, 8 Oct 2025 15:03:30 +0200 Subject: [PATCH 02/18] split HTML scoreboard into threads and processes views; add a menu index page --- .github/workflows/pages.yml | 4 +- scoreboard/main.py | 162 ++++++++++++++++++++---- scoreboard/templates/index.html.j2 | 12 +- scoreboard/templates/menu_index.html.j2 | 35 +++++ 4 files changed, 175 insertions(+), 38 deletions(-) create mode 100644 scoreboard/templates/menu_index.html.j2 diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index ec0f14fe1..37256a4b9 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -83,8 +83,8 @@ jobs: name: perf-stat - name: Extract performance data run: | - mkdir -p build/perf_stat_dir - unzip -o perf-stat.zip -d . + mkdir -p build + unzip -o perf-stat.zip -d build - name: CMake configure run: | cmake -S . -B build -DUSE_SCOREBOARD=ON diff --git a/scoreboard/main.py b/scoreboard/main.py index dceaf820f..564515d53 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -13,19 +13,44 @@ logger = logging.getLogger(__name__) task_types = ["all", "mpi", "omp", "seq", "stl", "tbb"] +task_types_threads = ["all", "omp", "seq", "stl", "tbb"] +task_types_processes = ["mpi", "seq"] script_dir = Path(__file__).parent tasks_dir = script_dir.parent / "tasks" +def _read_tasks_type(task_dir: Path) -> str | None: + """Read tasks_type from settings.json in the task directory (if present).""" + settings_path = task_dir / "settings.json" + if settings_path.exists(): + try: + import json + + with open(settings_path, "r") as f: + data = json.load(f) + return data.get("tasks_type") # "threads" or "processes" + except Exception as e: + logger.warning("Failed to parse %s: %s", settings_path, e) + return None + + def discover_tasks(tasks_dir, task_types): - """Discover tasks and their implementation status from the filesystem.""" + """Discover tasks and their implementation status from the filesystem. + + Returns: + directories: dict[task_name][task_type] -> status + tasks_type_map: dict[task_name] -> "threads" | "processes" | None + """ directories = defaultdict(dict) + tasks_type_map: dict[str, str | None] = {} 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"]: task_name = task_name_dir.name + # Save tasks_type from settings.json if present + tasks_type_map[task_name] = _read_tasks_type(task_name_dir) 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(): @@ -35,10 +60,10 @@ def discover_tasks(tasks_dir, task_types): else: directories[task_name][task_type] = "done" - return directories + return directories, tasks_type_map -directories = discover_tasks(tasks_dir, task_types) +directories, tasks_type_map = discover_tasks(tasks_dir, task_types) def load_performance_data(perf_stat_file_path): @@ -163,24 +188,20 @@ def load_configurations(): return cfg, eff_num_proc, deadlines_cfg, plagiarism_cfg -def main(): - """Main function to generate the scoreboard.""" - cfg, eff_num_proc, deadlines_cfg, plagiarism_cfg = load_configurations() - - env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates")) - - perf_stat_file_path = ( - script_dir.parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv" - ) - - # Read and parse performance statistics CSV - perf_stats = load_performance_data(perf_stat_file_path) - +def _build_rows_for_task_types( + selected_task_types: list[str], + dir_names: list[str], + perf_stats: dict, + cfg, + eff_num_proc, + deadlines_cfg, +): + """Build rows for the given list of task directories and selected task types.""" rows = [] - for dir in sorted(directories.keys()): + for dir in sorted(dir_names): row_types = [] total_count = 0 - for task_type in task_types: + for task_type in selected_task_types: status = directories[dir].get(task_type) sol_points, solution_style = get_solution_points_and_style( task_type, status, cfg @@ -219,22 +240,113 @@ def main(): total_count += task_points rows.append({"task": dir, "types": row_types, "total": total_count}) + return rows + + +def main(): + """Main function to generate the scoreboard. + + Now generates three pages in the output dir: + - index.html: simple menu linking to threads.html and processes.html + - threads.html: scoreboard for thread-based tasks + - processes.html: scoreboard for process-based tasks + """ + cfg, eff_num_proc, deadlines_cfg, plagiarism_cfg_local = load_configurations() - template = env.get_template("index.html.j2") - html_content = template.render(task_types=task_types, rows=rows) + # Make plagiarism config available to rows builder + global plagiarism_cfg + plagiarism_cfg = plagiarism_cfg_local + + env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates")) + + # Locate perf CSV from CI or local runs + candidates = [ + script_dir.parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv", + script_dir.parent / "perf_stat_dir" / "task_run_perf_table.csv", + ] + perf_stat_file_path = next((p for p in candidates if p.exists()), candidates[0]) + + # Read and parse performance statistics CSV + perf_stats = load_performance_data(perf_stat_file_path) + + # Partition tasks by tasks_type from settings.json + threads_task_dirs = [ + name for name, ttype in tasks_type_map.items() if ttype == "threads" + ] + processes_task_dirs = [ + name for name, ttype in tasks_type_map.items() if ttype == "processes" + ] + + # Fallback: if settings.json is missing, guess by directory name heuristic + for name in directories.keys(): + if name not in tasks_type_map or tasks_type_map[name] is None: + if "threads" in name: + threads_task_dirs.append(name) + elif "processes" in name: + processes_task_dirs.append(name) + + # Build rows for each page + threads_rows = _build_rows_for_task_types( + task_types_threads, threads_task_dirs, perf_stats, cfg, eff_num_proc, deadlines_cfg + ) + processes_rows = _build_rows_for_task_types( + task_types_processes, + processes_task_dirs, + perf_stats, + cfg, + eff_num_proc, + deadlines_cfg, + ) parser = argparse.ArgumentParser(description="Generate HTML scoreboard.") parser.add_argument( - "-o", "--output", type=str, required=True, help="Output file path" + "-o", "--output", type=str, required=True, help="Output directory path" ) args = parser.parse_args() output_path = Path(args.output) output_path.mkdir(parents=True, exist_ok=True) - output_file = output_path / "index.html" - with open(output_file, "w") as file: - file.write(html_content) + # Render tables + table_template = env.get_template("index.html.j2") + threads_html = table_template.render( + task_types=task_types_threads, rows=threads_rows + ) + processes_html = table_template.render( + task_types=task_types_processes, rows=processes_rows + ) + + with open(output_path / "threads.html", "w") as f: + f.write(threads_html) + with open(output_path / "processes.html", "w") as f: + f.write(processes_html) + + # Render index menu page + try: + menu_template = env.get_template("menu_index.html.j2") + except Exception: + # Simple fallback menu if template missing + menu_html_content = ( + "Scoreboard" + "

Scoreboard

" + "" + ) + else: + menu_html_content = menu_template.render( + pages=[ + {"href": "threads.html", "title": "Threads Scoreboard"}, + {"href": "processes.html", "title": "Processes Scoreboard"}, + ] + ) + + with open(output_path / "index.html", "w") as f: + f.write(menu_html_content) + + # Copy static assets static_src = script_dir / "static" static_dst = output_path / "static" if static_src.exists(): @@ -245,7 +357,7 @@ def main(): else: logger.warning("Static directory not found at %s", static_src) - logger.info("HTML page generated at %s", output_file) + logger.info("HTML pages generated at %s (index.html, threads.html, processes.html)", output_path) if __name__ == "__main__": diff --git a/scoreboard/templates/index.html.j2 b/scoreboard/templates/index.html.j2 index 4e4d9dbb9..ccd919087 100644 --- a/scoreboard/templates/index.html.j2 +++ b/scoreboard/templates/index.html.j2 @@ -5,17 +5,7 @@ -

Scoreboard

-

Note: This is experimental and results are for reference only!

-

- (S)olution - The correctness and completeness of the implemented solution.
- (A)cceleration - The process of speeding up software to improve performance. - Speedup = T(seq) / T(parallel)
- (E)fficiency - Optimizing software speed-up by improving CPU utilization and resource management. - Efficiency = Speedup / NumProcs * 100%
- (D)eadline - The timeliness of the submission in relation to the given deadline.
- (P)lagiarism - The originality of the work, ensuring no copied content from external sources.
-

+ diff --git a/scoreboard/templates/menu_index.html.j2 b/scoreboard/templates/menu_index.html.j2 new file mode 100644 index 000000000..88e54d4aa --- /dev/null +++ b/scoreboard/templates/menu_index.html.j2 @@ -0,0 +1,35 @@ + + + + + Scoreboard Menu + + + + + +

Scoreboard

+ +

+ (S)olution - The correctness and completeness of the implemented solution.
+ (A)cceleration - The process of speeding up software to improve performance. Speedup = T(seq) / T(parallel)
+ (E)fficiency - Optimizing software speed-up by improving CPU utilization and resource management. Efficiency = Speedup / NumProcs * 100%
+ (D)eadline - The timeliness of the submission in relation to the given deadline.
+ (P)lagiarism - The originality of the work, ensuring no copied content from external sources. +

+
Choose a scoreboard above to view. Defaults to Threads.
+ + + From 05f2264e2fceeb4303ecade5d3a6817e60556db1 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Wed, 8 Oct 2025 15:10:06 +0200 Subject: [PATCH 03/18] refactor scoreboard: improve code formatting and HTML consistency --- scoreboard/main.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/scoreboard/main.py b/scoreboard/main.py index 564515d53..f8c707b8b 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -287,7 +287,12 @@ def main(): # Build rows for each page threads_rows = _build_rows_for_task_types( - task_types_threads, threads_task_dirs, perf_stats, cfg, eff_num_proc, deadlines_cfg + task_types_threads, + threads_task_dirs, + perf_stats, + cfg, + eff_num_proc, + deadlines_cfg, ) processes_rows = _build_rows_for_task_types( task_types_processes, @@ -327,12 +332,12 @@ def main(): except Exception: # Simple fallback menu if template missing menu_html_content = ( - "Scoreboard" + 'Scoreboard' "

Scoreboard

" "" ) else: @@ -357,7 +362,10 @@ def main(): else: logger.warning("Static directory not found at %s", static_src) - logger.info("HTML pages generated at %s (index.html, threads.html, processes.html)", output_path) + logger.info( + "HTML pages generated at %s (index.html, threads.html, processes.html)", + output_path, + ) if __name__ == "__main__": From 6f65278e97a71313e8a66729ff606ecb4d2ae9cb Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 13:05:18 +0200 Subject: [PATCH 04/18] extend scoreboard: add group-specific views, student variants, and enhanced processes layout --- scoreboard/main.py | 413 +++++++++++++++++++++++- scoreboard/templates/index.html.j2 | 4 +- scoreboard/templates/menu_index.html.j2 | 27 +- tasks/example_processes/info.json | 12 +- tasks/example_threads/info.json | 12 +- 5 files changed, 438 insertions(+), 30 deletions(-) diff --git a/scoreboard/main.py b/scoreboard/main.py index f8c707b8b..1abb28021 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -1,5 +1,5 @@ from pathlib import Path -from collections import defaultdict +from collections import defaultdict, Counter from datetime import datetime import csv import argparse @@ -198,6 +198,37 @@ def _build_rows_for_task_types( ): """Build rows for the given list of task directories and selected task types.""" rows = [] + def _load_student_info_label(dir_name: str): + import json + + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return None + try: + with open(info_path, "r") as f: + data = json.load(f) + s = data.get("student", {}) + last = s.get("last_name", "") + first = s.get("first_name", "") + middle = s.get("middle_name", "") + parts = [p for p in [last, first, middle] if p] + label = "
".join(parts) + return label if label else None + except Exception: + return None + + def _load_variant(dir_name: str): + import json + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return "?" + try: + with open(info_path, "r") as f: + data = json.load(f) + return str(data.get("student", {}).get("variant_number", "?")) + except Exception: + return "?" + for dir in sorted(dir_names): row_types = [] total_count = 0 @@ -239,7 +270,14 @@ def _build_rows_for_task_types( ) total_count += task_points - rows.append({"task": dir, "types": row_types, "total": total_count}) + label_name = _load_student_info_label(dir) or dir + variant = _load_variant(dir) + rows.append({ + "task": label_name, + "variant": variant, + "types": row_types, + "total": total_count, + }) return rows @@ -294,14 +332,160 @@ def main(): eff_num_proc, deadlines_cfg, ) - processes_rows = _build_rows_for_task_types( - task_types_processes, - processes_task_dirs, - perf_stats, - cfg, - eff_num_proc, - deadlines_cfg, - ) + # Processes page: build 3 tasks as columns for a single student + import json + + def _load_student_info(dir_name: str): + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return None + try: + with open(info_path, "r") as f: + data = json.load(f) + return data.get("student", {}) + except Exception as e: + logger.warning("Failed to parse %s: %s", info_path, e) + return None + + def _identity_key(student: dict) -> str: + return "|".join( + [ + str(student.get("first_name", "")), + str(student.get("last_name", "")), + str(student.get("middle_name", "")), + str(student.get("group_number", "")), + ] + ) + + def _build_cell(dir_name: str, ttype: str): + status = directories[dir_name].get(ttype) + sol_points, solution_style = get_solution_points_and_style(ttype, status, cfg) + task_points = sol_points + is_cheated, plagiarism_points = check_plagiarism_and_calculate_penalty( + dir_name, ttype, sol_points, plagiarism_cfg, cfg + ) + task_points += plagiarism_points + perf_val = perf_stats.get(dir_name, {}).get(ttype, "?") + acceleration, efficiency = calculate_performance_metrics( + perf_val, eff_num_proc, ttype + ) + deadline_points = calculate_deadline_penalty( + dir_name, ttype, status, deadlines_cfg, tasks_dir + ) + return ( + { + "solution_points": sol_points, + "solution_style": solution_style, + "perf": perf_val, + "acceleration": acceleration, + "efficiency": efficiency, + "deadline_points": deadline_points, + "plagiarised": is_cheated, + "plagiarism_points": plagiarism_points, + }, + task_points, + ) + + proc_infos = [] + for d in processes_task_dirs: + s = _load_student_info(d) + if s: + proc_infos.append((d, s)) + + # Choose target identity: prefer example_processes; otherwise most common + target_identity = None + if "example_processes" in processes_task_dirs: + s0 = _load_student_info("example_processes") + if s0: + target_identity = _identity_key(s0) + if not target_identity and proc_infos: + cnt = Counter(_identity_key(s) for _, s in proc_infos) + target_identity = cnt.most_common(1)[0][0] + + # Map task_number -> (dir_name, display_label) + num_to_dir: dict[int, tuple[str, str]] = {} + if target_identity: + for d, s in proc_infos: + if _identity_key(s) == target_identity: + try: + tn = int(str(s.get("task_number", "0"))) + except Exception: + continue + display = d + num_to_dir[tn] = (d, display) + + expected_numbers = [1, 2, 3] + proc_group_headers = [] + proc_top_headers = [] + proc_groups = [] + total_points_sum = 0 + for n in expected_numbers: + entry = num_to_dir.get(n) + if entry: + d, display_label = entry + # Top header shows task name (directory) + proc_top_headers.append(f"task-{n}") + # Second header row shows only mpi/seq + proc_group_headers.append({"type": "mpi"}) + proc_group_headers.append({"type": "seq"}) + for ttype in ["mpi", "seq"]: + cell, pts = _build_cell(d, ttype) + proc_groups.append(cell) + total_points_sum += pts + else: + proc_group_headers.append({"type": "mpi", "task_label": f"task_{n}"}) + proc_group_headers.append({"type": "seq", "task_label": f"task_{n}"}) + proc_top_headers.append(f"task-{n}") + for _ in ["mpi", "seq"]: + proc_groups.append( + { + "solution_points": "?", + "solution_style": "", + "perf": "?", + "acceleration": "?", + "efficiency": "?", + "deadline_points": "?", + "plagiarised": False, + "plagiarism_points": "?", + } + ) + # Do not affect total; sum only existing tasks + + # Label for processes row: show Last, First, Middle on separate lines; no group number + row_label = "processes" + row_variant = "?" + if target_identity: + parts = target_identity.split("|") + if len(parts) >= 4: + first, last, middle, _group = parts[0], parts[1], parts[2], parts[3] + name_parts = [p for p in [last, first, middle] if p] + name = "
".join(name_parts) + row_label = name or row_label + # Choose variant from the first available task (1..3) + def _load_variant(dir_name: str): + import json + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return "?" + try: + with open(info_path, "r") as f: + data = json.load(f) + return str(data.get("student", {}).get("variant_number", "?")) + except Exception: + return "?" + for n in expected_numbers: + ent = num_to_dir.get(n) + if ent: + row_variant = _load_variant(ent[0]) + break + processes_rows = [ + { + "task": row_label, + "variant": row_variant, + "groups": proc_groups, + "total": total_points_sum, + } + ] parser = argparse.ArgumentParser(description="Generate HTML scoreboard.") parser.add_argument( @@ -317,8 +501,10 @@ def main(): threads_html = table_template.render( task_types=task_types_threads, rows=threads_rows ) - processes_html = table_template.render( - task_types=task_types_processes, rows=processes_rows + # Use dedicated template for processes table layout + processes_template = env.get_template("processes.html.j2") + processes_html = processes_template.render( + top_task_names=proc_top_headers, group_headers=proc_group_headers, rows=processes_rows ) with open(output_path / "threads.html", "w") as f: @@ -326,6 +512,205 @@ def main(): with open(output_path / "processes.html", "w") as f: f.write(processes_html) + # ——— Build per-group pages and group menus ———————————————————————— + def _load_group_number(dir_name: str): + import json + + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return None + try: + with open(info_path, "r") as f: + data = json.load(f) + return data.get("student", {}).get("group_number") + except Exception: + return None + + def _slugify(text: str) -> str: + return "".join(ch if ch.isalnum() or ch in ("-", "_") else "_" for ch in str(text)) + + # Collect groups + threads_groups = sorted( + set(filter(None, (_load_group_number(d) for d in threads_task_dirs))) + ) + processes_groups = sorted( + set(filter(None, (_load_group_number(d) for d in processes_task_dirs))) + ) + + # Threads: per-group pages + threads_groups_menu = [] + for g in threads_groups: + slug = _slugify(g) + out_file = output_path / f"threads_{slug}.html" + filtered_dirs = [d for d in threads_task_dirs if _load_group_number(d) == g] + rows_g = _build_rows_for_task_types( + task_types_threads, filtered_dirs, perf_stats, cfg, eff_num_proc, deadlines_cfg + ) + html_g = table_template.render(task_types=task_types_threads, rows=rows_g) + with open(out_file, "w") as f: + f.write(html_g) + threads_groups_menu.append({"href": out_file.name, "title": g}) + + # Processes: per-group pages + processes_groups_menu = [] + for g in processes_groups: + slug = _slugify(g) + out_file = output_path / f"processes_{slug}.html" + filtered_dirs = [d for d in processes_task_dirs if _load_group_number(d) == g] + + # Reuse earlier logic but limited to filtered_dirs + import json as _json + def _load_student_info_group(dir_name: str): + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return None + try: + with open(info_path, "r") as f: + data = _json.load(f) + return data.get("student", {}) + except Exception: + return None + + def _id_key(stud: dict) -> str: + return "|".join( + [ + str(stud.get("first_name", "")), + str(stud.get("last_name", "")), + str(stud.get("middle_name", "")), + str(stud.get("group_number", "")), + ] + ) + + proc_infos_g = [] + for d in filtered_dirs: + s = _load_student_info_group(d) + if s: + proc_infos_g.append((d, s)) + + target_identity_g = None + if "example_processes" in filtered_dirs: + s0 = _load_student_info_group("example_processes") + if s0 and s0.get("group_number") == g: + target_identity_g = _id_key(s0) + if not target_identity_g and proc_infos_g: + cnt = Counter(_id_key(s) for _, s in proc_infos_g) + target_identity_g = cnt.most_common(1)[0][0] + + num_to_dir_g: dict[int, tuple[str, str]] = {} + if target_identity_g: + for d, s in proc_infos_g: + if _id_key(s) == target_identity_g: + try: + tn = int(str(s.get("task_number", "0"))) + except Exception: + continue + num_to_dir_g[tn] = (d, d) + + proc_top_headers_g = [] + proc_group_headers_g = [] + proc_groups_g = [] + total_points_sum_g = 0 + for n in [1, 2, 3]: + entry = num_to_dir_g.get(n) + if entry: + d, display_label = entry + proc_top_headers_g.append(f"task-{n}") + for ttype in ["mpi", "seq"]: + proc_group_headers_g.append({"type": ttype}) + # build cell + status = directories[d].get(ttype) + sol_points, solution_style = get_solution_points_and_style( + ttype, status, cfg + ) + task_points = sol_points + is_cheated, plagiarism_points = check_plagiarism_and_calculate_penalty( + d, ttype, sol_points, plagiarism_cfg, cfg + ) + task_points += plagiarism_points + perf_val = perf_stats.get(d, {}).get(ttype, "?") + acceleration, efficiency = calculate_performance_metrics( + perf_val, eff_num_proc, ttype + ) + deadline_points = calculate_deadline_penalty( + d, ttype, status, deadlines_cfg, tasks_dir + ) + proc_groups_g.append( + { + "solution_points": sol_points, + "solution_style": solution_style, + "perf": perf_val, + "acceleration": acceleration, + "efficiency": efficiency, + "deadline_points": deadline_points, + "plagiarised": is_cheated, + "plagiarism_points": plagiarism_points, + } + ) + total_points_sum_g += task_points + else: + proc_top_headers_g.append(f"task-{n}") + for ttype in ["mpi", "seq"]: + proc_group_headers_g.append({"type": ttype}) + proc_groups_g.append( + { + "solution_points": "?", + "solution_style": "", + "perf": "?", + "acceleration": "?", + "efficiency": "?", + "deadline_points": "?", + "plagiarised": False, + "plagiarism_points": "?", + } + ) + # Missing task: do not affect total; sum only existing + + # Row label for group page: name without group (three lines max) + row_label_g = f"group {g}" + if target_identity_g: + parts = target_identity_g.split("|") + if len(parts) >= 4: + first, last, middle, _group = parts[0], parts[1], parts[2], parts[3] + nm_parts = [p for p in [last, first, middle] if p] + nm = "
".join(nm_parts) + row_label_g = nm or row_label_g + # Variant for group row + def _load_variant_g(dir_name: str): + import json + info_path = tasks_dir / dir_name / "info.json" + if not info_path.exists(): + return "?" + try: + with open(info_path, "r") as f: + data = json.load(f) + return str(data.get("student", {}).get("variant_number", "?")) + except Exception: + return "?" + row_variant_g = "?" + for n in [1, 2, 3]: + entry2 = num_to_dir_g.get(n) + if entry2: + row_variant_g = _load_variant_g(entry2[0]) + break + + rows_g = [ + { + "task": row_label_g, + "variant": row_variant_g, + "groups": proc_groups_g, + "total": total_points_sum_g, + } + ] + + html_g = processes_template.render( + top_task_names=proc_top_headers_g, + group_headers=proc_group_headers_g, + rows=rows_g, + ) + with open(out_file, "w") as f: + f.write(html_g) + processes_groups_menu.append({"href": out_file.name, "title": g}) + # Render index menu page try: menu_template = env.get_template("menu_index.html.j2") @@ -345,7 +730,9 @@ def main(): pages=[ {"href": "threads.html", "title": "Threads Scoreboard"}, {"href": "processes.html", "title": "Processes Scoreboard"}, - ] + ], + groups_threads=threads_groups_menu, + groups_processes=processes_groups_menu, ) with open(output_path / "index.html", "w") as f: diff --git a/scoreboard/templates/index.html.j2 b/scoreboard/templates/index.html.j2 index ccd919087..534c6b551 100644 --- a/scoreboard/templates/index.html.j2 +++ b/scoreboard/templates/index.html.j2 @@ -8,7 +8,8 @@
Tasks
- + + {% for type in task_types %} {% endfor %} @@ -24,6 +25,7 @@ {% for row in rows %} + {% for cell in row.types %} diff --git a/scoreboard/templates/menu_index.html.j2 b/scoreboard/templates/menu_index.html.j2 index 88e54d4aa..ef04119b8 100644 --- a/scoreboard/templates/menu_index.html.j2 +++ b/scoreboard/templates/menu_index.html.j2 @@ -7,29 +7,48 @@

Scoreboard

+

+ (V)ariant - Task variant number assigned to the student.
(S)olution - The correctness and completeness of the implemented solution.
(A)cceleration - The process of speeding up software to improve performance. Speedup = T(seq) / T(parallel)
(E)fficiency - Optimizing software speed-up by improving CPU utilization and resource management. Efficiency = Speedup / NumProcs * 100%
(D)eadline - The timeliness of the submission in relation to the given deadline.
(P)lagiarism - The originality of the work, ensuring no copied content from external sources.

-
Choose a scoreboard above to view. Defaults to Threads.
diff --git a/tasks/example_processes/info.json b/tasks/example_processes/info.json index a2c75b29a..513436aac 100644 --- a/tasks/example_processes/info.json +++ b/tasks/example_processes/info.json @@ -1,10 +1,10 @@ { "student": { - "first_name": "", - "last_name": "", - "middle_name": "", - "group_number": "", - "task_number": "", - "variant_number": "" + "first_name": "first_name_p", + "last_name": "last_name_p", + "middle_name": "middle_name_p", + "group_number": "2222222_p", + "task_number": "1", + "variant_number": "23" } } diff --git a/tasks/example_threads/info.json b/tasks/example_threads/info.json index a2c75b29a..cde6f792c 100644 --- a/tasks/example_threads/info.json +++ b/tasks/example_threads/info.json @@ -1,10 +1,10 @@ { "student": { - "first_name": "", - "last_name": "", - "middle_name": "", - "group_number": "", - "task_number": "", - "variant_number": "" + "first_name": "first_name_t", + "last_name": "last_name_t", + "middle_name": "middle_name_t", + "group_number": "2222222_t", + "task_number": "1", + "variant_number": "23" } } From 6d01e07fd2d7ab5b81bb6c8d09bdb3fb7dc9ad48 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 13:05:33 +0200 Subject: [PATCH 05/18] add processes view: introduce detailed layout for processes scoreboard --- scoreboard/templates/processes.html.j2 | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 scoreboard/templates/processes.html.j2 diff --git a/scoreboard/templates/processes.html.j2 b/scoreboard/templates/processes.html.j2 new file mode 100644 index 000000000..0898dff5f --- /dev/null +++ b/scoreboard/templates/processes.html.j2 @@ -0,0 +1,45 @@ + + + + Processes Scoreboard + + + +
TasksNameV{{ type }}
{{ row.task }}{{ row.variant }}{{ cell.solution_points }} {{ cell.acceleration }}
+ + + + {% for name in top_task_names %} + + {% endfor %} + + + + {% for header in group_headers %} + + {% endfor %} + + + {% for _ in group_headers %} + {% for letter in ('S', 'A', 'E', 'D', 'P') %} + + {% endfor %} + {% endfor %} + + {% for row in rows %} + + + + {% for cell in row.groups %} + + + + + + {% endfor %} + + + {% endfor %} +
NameV{{ name }}Total
{{ header.type }}
{{ letter }}
{{ row.task }}{{ row.variant }}{{ cell.solution_points }}{{ cell.acceleration }}{{ cell.efficiency }}{{ cell.deadline_points }}{{ cell.plagiarism_points }}{{ row.total }}
+ + From 8261acd6028ed59eb714042d514836d2414b76fc Mon Sep 17 00:00:00 2001 From: Nesterov Alexander Date: Thu, 9 Oct 2025 13:11:51 +0200 Subject: [PATCH 06/18] Update pages.yml --- .github/workflows/pages.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 37256a4b9..ec0f14fe1 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -83,8 +83,8 @@ jobs: name: perf-stat - name: Extract performance data run: | - mkdir -p build - unzip -o perf-stat.zip -d build + mkdir -p build/perf_stat_dir + unzip -o perf-stat.zip -d . - name: CMake configure run: | cmake -S . -B build -DUSE_SCOREBOARD=ON From 47c4e0e61fdd7f24a5822209c4bd0bb241c6d55d Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 13:14:10 +0200 Subject: [PATCH 07/18] comment out dependencies for perf workflow in main.yml --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b41003750..d5c526753 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,10 +31,10 @@ jobs: - pre-commit uses: ./.github/workflows/windows.yml perf: - needs: - - ubuntu - - mac - - windows +# needs: +# - ubuntu +# - mac +# - windows uses: ./.github/workflows/perf.yml pages: From e3848bc4565d5a92940019c7c33b4e4748d13067 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 13:16:29 +0200 Subject: [PATCH 08/18] uncomment dependencies for perf workflow in main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d5c526753..68afc8704 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,8 +31,8 @@ jobs: - pre-commit uses: ./.github/workflows/windows.yml perf: -# needs: -# - ubuntu + needs: + - ubuntu # - mac # - windows uses: ./.github/workflows/perf.yml From c03fa1773aa82d4703d32d7d7f4ece75f87d9b26 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 13:24:57 +0200 Subject: [PATCH 09/18] refactor scoreboard: improve code formatting and ensure consistent indentation --- scoreboard/main.py | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/scoreboard/main.py b/scoreboard/main.py index 1abb28021..24b3f2b4b 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -198,6 +198,7 @@ def _build_rows_for_task_types( ): """Build rows for the given list of task directories and selected task types.""" rows = [] + def _load_student_info_label(dir_name: str): import json @@ -219,6 +220,7 @@ def _load_student_info_label(dir_name: str): def _load_variant(dir_name: str): import json + info_path = tasks_dir / dir_name / "info.json" if not info_path.exists(): return "?" @@ -272,12 +274,14 @@ def _load_variant(dir_name: str): label_name = _load_student_info_label(dir) or dir variant = _load_variant(dir) - rows.append({ - "task": label_name, - "variant": variant, - "types": row_types, - "total": total_count, - }) + rows.append( + { + "task": label_name, + "variant": variant, + "types": row_types, + "total": total_count, + } + ) return rows @@ -456,14 +460,16 @@ def _build_cell(dir_name: str, ttype: str): row_variant = "?" if target_identity: parts = target_identity.split("|") - if len(parts) >= 4: - first, last, middle, _group = parts[0], parts[1], parts[2], parts[3] + if len(parts) >= 3: + first, last, middle = parts[0], parts[1], parts[2] name_parts = [p for p in [last, first, middle] if p] name = "
".join(name_parts) row_label = name or row_label + # Choose variant from the first available task (1..3) def _load_variant(dir_name: str): import json + info_path = tasks_dir / dir_name / "info.json" if not info_path.exists(): return "?" @@ -473,6 +479,7 @@ def _load_variant(dir_name: str): return str(data.get("student", {}).get("variant_number", "?")) except Exception: return "?" + for n in expected_numbers: ent = num_to_dir.get(n) if ent: @@ -504,7 +511,9 @@ def _load_variant(dir_name: str): # Use dedicated template for processes table layout processes_template = env.get_template("processes.html.j2") processes_html = processes_template.render( - top_task_names=proc_top_headers, group_headers=proc_group_headers, rows=processes_rows + top_task_names=proc_top_headers, + group_headers=proc_group_headers, + rows=processes_rows, ) with open(output_path / "threads.html", "w") as f: @@ -527,7 +536,9 @@ def _load_group_number(dir_name: str): return None def _slugify(text: str) -> str: - return "".join(ch if ch.isalnum() or ch in ("-", "_") else "_" for ch in str(text)) + return "".join( + ch if ch.isalnum() or ch in ("-", "_") else "_" for ch in str(text) + ) # Collect groups threads_groups = sorted( @@ -544,7 +555,12 @@ def _slugify(text: str) -> str: out_file = output_path / f"threads_{slug}.html" filtered_dirs = [d for d in threads_task_dirs if _load_group_number(d) == g] rows_g = _build_rows_for_task_types( - task_types_threads, filtered_dirs, perf_stats, cfg, eff_num_proc, deadlines_cfg + task_types_threads, + filtered_dirs, + perf_stats, + cfg, + eff_num_proc, + deadlines_cfg, ) html_g = table_template.render(task_types=task_types_threads, rows=rows_g) with open(out_file, "w") as f: @@ -560,6 +576,7 @@ def _slugify(text: str) -> str: # Reuse earlier logic but limited to filtered_dirs import json as _json + def _load_student_info_group(dir_name: str): info_path = tasks_dir / dir_name / "info.json" if not info_path.exists(): @@ -623,8 +640,10 @@ def _id_key(stud: dict) -> str: ttype, status, cfg ) task_points = sol_points - is_cheated, plagiarism_points = check_plagiarism_and_calculate_penalty( - d, ttype, sol_points, plagiarism_cfg, cfg + is_cheated, plagiarism_points = ( + check_plagiarism_and_calculate_penalty( + d, ttype, sol_points, plagiarism_cfg, cfg + ) ) task_points += plagiarism_points perf_val = perf_stats.get(d, {}).get(ttype, "?") @@ -669,14 +688,16 @@ def _id_key(stud: dict) -> str: row_label_g = f"group {g}" if target_identity_g: parts = target_identity_g.split("|") - if len(parts) >= 4: - first, last, middle, _group = parts[0], parts[1], parts[2], parts[3] + if len(parts) >= 3: + first, last, middle = parts[0], parts[1], parts[2] nm_parts = [p for p in [last, first, middle] if p] nm = "
".join(nm_parts) row_label_g = nm or row_label_g + # Variant for group row def _load_variant_g(dir_name: str): import json + info_path = tasks_dir / dir_name / "info.json" if not info_path.exists(): return "?" @@ -686,6 +707,7 @@ def _load_variant_g(dir_name: str): return str(data.get("student", {}).get("variant_number", "?")) except Exception: return "?" + row_variant_g = "?" for n in [1, 2, 3]: entry2 = num_to_dir_g.get(n) From 6ad024cc18769b192cbc941455127054958c8df5 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 20:28:05 +0200 Subject: [PATCH 10/18] refactor scoreboard: replace threads-config.yml with points-info.yml; extend scoring with reports section --- scoreboard/README.md | 2 +- scoreboard/assign_variant.py | 141 +++++++++++++++++++++ scoreboard/data/performance.yml | 1 - scoreboard/data/plagiarism.yml | 8 ++ scoreboard/data/points-info.yml | 63 ++++++++++ scoreboard/data/threads-config.yml | 49 -------- scoreboard/main.py | 157 +++++++++++++++++++++--- scoreboard/templates/index.html.j2 | 9 +- scoreboard/templates/menu_index.html.j2 | 1 + scoreboard/templates/processes.html.j2 | 45 +++++-- tasks/example_processes/report.md | 0 tasks/example_threads/all/report.md | 0 tasks/example_threads/omp/report.md | 0 tasks/example_threads/seq/report.md | 0 tasks/example_threads/stl/report.md | 0 tasks/example_threads/tbb/report.md | 0 16 files changed, 392 insertions(+), 84 deletions(-) create mode 100644 scoreboard/assign_variant.py delete mode 100644 scoreboard/data/performance.yml create mode 100644 scoreboard/data/points-info.yml delete mode 100644 scoreboard/data/threads-config.yml create mode 100644 tasks/example_processes/report.md create mode 100644 tasks/example_threads/all/report.md create mode 100644 tasks/example_threads/omp/report.md create mode 100644 tasks/example_threads/seq/report.md create mode 100644 tasks/example_threads/stl/report.md create mode 100644 tasks/example_threads/tbb/report.md diff --git a/scoreboard/README.md b/scoreboard/README.md index 69cadd6c1..efaeca2fd 100644 --- a/scoreboard/README.md +++ b/scoreboard/README.md @@ -16,7 +16,7 @@ Generates `output_directory/index.html` with the scoreboard. ## Configuration -- `data/threads-config.yml` - Task points, deadlines, penalties +- `data/points-info.yml` - Task points, deadlines, penalties - `data/plagiarism.yml` - Flagged submissions ## Testing diff --git a/scoreboard/assign_variant.py b/scoreboard/assign_variant.py new file mode 100644 index 000000000..a1ccb5ffa --- /dev/null +++ b/scoreboard/assign_variant.py @@ -0,0 +1,141 @@ +# file: assign_variant.py +""" +Deterministic variant assignment from Full Name + Group +with the repository name as the ONLY salt. + +Algorithm: + 1) Normalize strings (NFKC, trim, lowercase, map 'ё'->'е', collapse spaces). + 2) Build a key: "surname|name|patronymic|group|repo". + 3) SHA-256(key) -> big integer -> modulo `num_variants`. + +Properties: +- Stable: same inputs → same output. +- Uniform: modulo of a cryptographic hash distributes evenly. +- Note: Without the full group roster, zero collisions cannot be *guaranteed* + (birthday paradox). This is intended for “approximately unique” per-group use. + +Usage: + from assign_variant import assign_variant + v = assign_variant( + surname="Petrov", + name="Pyotr", + patronymic="Petrovich", + group="MEN-201", + repo="learning-process/parallel_programming_course", + num_variants=31, # produces values in 0..30 + ) + print(v) +""" + +from __future__ import annotations + +import hashlib +import re +import unicodedata +from typing import Optional + +__all__ = ["assign_variant", "normalize"] + + +def normalize(s: Optional[str]) -> str: + """ + Normalize a string: + - Unicode NFKC, + - trim, + - lowercase, + - map Cyrillic 'ё' -> 'е' (common normalization in Russian names), + - collapse multiple spaces to a single space. + + None -> '' (empty string). + """ + if not s: + return "" + s = unicodedata.normalize("NFKC", s).strip().lower() + s = s.replace("ё", "е") + s = re.sub(r"\s+", " ", s) + return s + + +def _hash_int(key: str) -> int: + """Return SHA-256(key) as a big integer.""" + return int.from_bytes(hashlib.sha256(key.encode("utf-8")).digest(), "big") + + +def assign_variant( + surname: str, + name: str, + group: str, + repo: str, + patronymic: Optional[str] = "", + num_variants: int = 31, +) -> int: + """ + Deterministically returns a variant index in [0 .. num_variants-1] + based on (surname, name, patronymic, group) and the repository name (repo) + as the sole salt. + + :param surname: Last name + :param name: First name + :param group: Group identifier (e.g., "MEN-201") + :param repo: Repository name used as salt (e.g., "org/repo" or just "repo") + :param patronymic: Middle name / patronymic (optional) + :param num_variants: Total number of variants (> 0). Output range: 0..num_variants-1 + :return: int — the variant index + """ + if not isinstance(num_variants, int) or num_variants < 1: + raise ValueError("num_variants must be a positive integer (> 0)") + if not repo or not isinstance(repo, str): + raise ValueError("repo must be a non-empty string") + + key = "|".join( + ( + normalize(surname), + normalize(name), + normalize(patronymic), + normalize(group), + normalize(repo), + ) + ) + h = _hash_int(key) + return h % num_variants + + +# Minimal self-check when executed directly (no CLI arguments). +if __name__ == "__main__": + def demo(): + print("Demo: deterministic assignment\n") + + v1 = assign_variant( + surname="Петров", + name="Пётр", + patronymic="Петрович", + group="МЕН-201", + repo="learning-process/parallel_programming_course", + num_variants=31, + ) + # Different casing/spacing/ё→е should not change the result: + v2 = assign_variant( + surname="ПЕТРОВ", + name="петр ", + patronymic="пЕТРОВИЧ", + group=" мен-201 ", + repo="learning-process/parallel_programming_course", + num_variants=31, + ) + assert v1 == v2, "Normalization should make results identical" + + v_other_repo = assign_variant( + surname="Petrov", + name="Pyotr", + patronymic="Petrovich", + group="MEN-201", + repo="learning-process/ppc_2025_fall", # different salt → likely different value + num_variants=31, + ) + + print(f"Variant (repo=A): {v1}") + print(f"Variant (same inputs, normalized): {v2}") + print(f"Variant (repo=B): {v_other_repo}") + print("\nOK: deterministic & normalized.") + + demo() diff --git a/scoreboard/data/performance.yml b/scoreboard/data/performance.yml deleted file mode 100644 index e4d741028..000000000 --- a/scoreboard/data/performance.yml +++ /dev/null @@ -1 +0,0 @@ -performance: diff --git a/scoreboard/data/plagiarism.yml b/scoreboard/data/plagiarism.yml index 3a4d581a4..578f371ff 100644 --- a/scoreboard/data/plagiarism.yml +++ b/scoreboard/data/plagiarism.yml @@ -6,3 +6,11 @@ plagiarism: stl: [] tbb: [] all: [] + +deadlines: + mpi: "2025-12-31" + omp: "2025-12-31" + seq: "2025-12-31" + stl: "2025-12-31" + tbb: "2025-12-31" + all: "2025-12-31" \ No newline at end of file diff --git a/scoreboard/data/points-info.yml b/scoreboard/data/points-info.yml new file mode 100644 index 000000000..ec30f4526 --- /dev/null +++ b/scoreboard/data/points-info.yml @@ -0,0 +1,63 @@ +processes: + semester_total: 70 + tasks: + - name: mpi_task_1 + mpi: + - S: 8 + - A: 0 + seq: + - S: 2 + R: 2 + variants_max: 27 + Total: 12 + - name: mpi_task_2 + mpi: + - S: 12 + - A: 5 + seq: + - S: 3 + R: 3 + variants_max: 23 + Total: 23 + - name: mpi_task_3 + mpi: + - S: 16 + - A: 10 + seq: + - S: 4 + R: 5 + variants_max: 32 + Total: 35 +threads: + semester_total: 64 + variants_max: 30 + tasks: + - name: seq + S: 4 + R: 1 + Total: 5 + - name: omp + S: 6 + A: 3 + R: 2 + Total: 11 + - name: tbb + S: 6 + A: 3 + R: 2 + Total: 11 + - name: stl + S: 8 + A: 6 + R: 2 + Total: 16 + - name: all + S: 10 + A: 8 + R: 3 + Total: 21 +efficiency: + num_proc: 4 +plagiarism: + coefficient: 0.5 + note: "Penalty P = -coefficient * S (scoreboard notation)" diff --git a/scoreboard/data/threads-config.yml b/scoreboard/data/threads-config.yml deleted file mode 100644 index ed8b5683c..000000000 --- a/scoreboard/data/threads-config.yml +++ /dev/null @@ -1,49 +0,0 @@ -scoreboard: - task: - mpi: - solution: - max: 0 - performance: - max: 0 - visible: true - omp: - solution: - max: 6 - performance: - max: 3 - visible: true - seq: - solution: - max: 4 - performance: - max: 0 - visible: true - stl: - solution: - max: 8 - performance: - max: 6 - visible: true - tbb: - solution: - max: 6 - performance: - max: 3 - visible: true - all: - solution: - max: 10 - performance: - max: 8 - visible: true - plagiarism: - coefficient: 0.5 - efficiency: - num_proc: 4 - deadlines: - mpi: "2025-12-31" - omp: "2025-12-31" - seq: "2025-12-31" - stl: "2025-12-31" - tbb: "2025-12-31" - all: "2025-12-31" diff --git a/scoreboard/main.py b/scoreboard/main.py index 24b3f2b4b..f1c3706cf 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -7,6 +7,7 @@ import yaml import shutil from jinja2 import Environment, FileSystemLoader +from zoneinfo import ZoneInfo import logging logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") @@ -112,9 +113,83 @@ def calculate_performance_metrics(perf_val, eff_num_proc, task_type): return acceleration, efficiency +def _find_max_solution(points_info, task_type: str) -> int: + """Resolve max S for a given task type from points-info (threads list).""" + threads_tasks = (points_info.get("threads", {}) or {}).get("tasks", []) + for t in threads_tasks: + if str(t.get("name")) == task_type: + try: + return int(t.get("S", 0)) + except Exception: + return 0 + if task_type == "mpi": + return 0 + return 0 + + +def _find_report_max(points_info, task_type: str) -> int: + """Resolve max Report (R) points for a given task type from points-info (threads). + Returns 0 if not found. + """ + threads_tasks = (points_info.get("threads", {}) or {}).get("tasks", []) + for t in threads_tasks: + if str(t.get("name")) == task_type: + try: + return int(t.get("R", 0)) + except Exception: + return 0 + return 0 + + +def _find_process_report_max(points_info, task_number: int) -> int: + """Get max report (R) points for process task by ordinal (1..3). + Looks up processes.tasks with names like 'mpi_task_1'. + """ + proc = (points_info.get("processes", {}) or {}).get("tasks", []) + key = f"mpi_task_{task_number}" + for t in proc: + if str(t.get("name")) == key: + try: + return int(t.get("R", 0)) + except Exception: + return 0 + return 0 + +def _find_process_points(points_info, task_number: int) -> tuple[int, int, int, int]: + """Return (S_mpi, S_seq, A_mpi, R) maxima for a given process task ordinal (1..3). + Supports both mapping and list-of-maps (per user's YAML example). + """ + proc_tasks = (points_info.get("processes", {}) or {}).get("tasks", []) + key = f"mpi_task_{task_number}" + for t in proc_tasks: + if str(t.get("name")) == key: + def _extract(obj, k): + if isinstance(obj, dict): + return int(obj.get(k, 0)) + if isinstance(obj, list): + for it in obj: + if isinstance(it, dict) and k in it: + try: + return int(it.get(k, 0)) + except Exception: + return 0 + return 0 + + mpi_blk = t.get("mpi", {}) + seq_blk = t.get("seq", {}) + s_mpi = _extract(mpi_blk, "S") + a_mpi = _extract(mpi_blk, "A") + s_seq = _extract(seq_blk, "S") + try: + r = int(t.get("R", 0)) + except Exception: + r = 0 + return s_mpi, s_seq, a_mpi, r + return 0, 0, 0, 0 + def get_solution_points_and_style(task_type, status, cfg): """Get solution points and CSS style based on task type and status.""" - max_sol_points = int(cfg["scoreboard"]["task"][task_type]["solution"]["max"]) + max_sol_points = _find_max_solution(cfg, task_type) sol_points = max_sol_points if status in ("done", "disabled") else 0 solution_style = "" if status == "done": @@ -135,7 +210,7 @@ def check_plagiarism_and_calculate_penalty( ) plagiarism_points = 0 if is_cheated: - plag_coeff = float(cfg["scoreboard"]["plagiarism"]["coefficient"]) + plag_coeff = float(cfg.get("plagiarism", {}).get("coefficient", 0.0)) plagiarism_points = -plag_coeff * sol_points return is_cheated, plagiarism_points @@ -170,22 +245,22 @@ def calculate_deadline_penalty(dir, task_type, status, deadlines_cfg, tasks_dir) def load_configurations(): - """Load all configuration files and return parsed data.""" - 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: - cfg = yaml.safe_load(file) - assert cfg, "Configuration is empty" + """Load points-info (max points, deadlines, efficiency) and plagiarism lists.""" + points_info_path = Path(__file__).parent / "data" / "points-info.yml" + assert points_info_path.exists(), f"Points info file not found: {points_info_path}" + with open(points_info_path, "r") as f: + points_info = yaml.safe_load(f) + assert points_info, "Points info is empty" - eff_num_proc = int(cfg["scoreboard"].get("efficiency", {}).get("num_proc", 1)) - deadlines_cfg = cfg["scoreboard"].get("deadlines", {}) + eff_num_proc = int(points_info.get("efficiency", {}).get("num_proc", 1)) + deadlines_cfg = points_info.get("deadlines", {}) plagiarism_config_path = Path(__file__).parent / "data" / "plagiarism.yml" with open(plagiarism_config_path, "r") as file: plagiarism_cfg = yaml.safe_load(file) assert plagiarism_cfg, "Plagiarism configuration is empty" - return cfg, eff_num_proc, deadlines_cfg, plagiarism_cfg + return points_info, eff_num_proc, deadlines_cfg, plagiarism_cfg def _build_rows_for_task_types( @@ -258,6 +333,10 @@ def _load_variant(dir_name: str): dir, task_type, status, deadlines_cfg, tasks_dir ) + # Report presence: award R only if report.md exists inside the task directory + report_present = (tasks_dir / dir / "report.md").exists() + report_points = _find_report_max(cfg, task_type) if report_present else 0 + row_types.append( { "solution_points": sol_points, @@ -268,6 +347,7 @@ def _load_variant(dir_name: str): "deadline_points": deadline_points, "plagiarised": is_cheated, "plagiarism_points": plagiarism_points, + "report": report_points, } ) total_count += task_points @@ -422,6 +502,7 @@ def _build_cell(dir_name: str, ttype: str): proc_group_headers = [] proc_top_headers = [] proc_groups = [] + proc_r_values = [] total_points_sum = 0 for n in expected_numbers: entry = num_to_dir.get(n) @@ -432,10 +513,26 @@ def _build_cell(dir_name: str, ttype: str): # Second header row shows only mpi/seq proc_group_headers.append({"type": "mpi"}) proc_group_headers.append({"type": "seq"}) + group_cells = [] for ttype in ["mpi", "seq"]: - cell, pts = _build_cell(d, ttype) - proc_groups.append(cell) - total_points_sum += pts + cell, _ = _build_cell(d, ttype) + group_cells.append(cell) + # Override displayed points for processes: S under MPI/SEQ from points-info; A points under MPI only + s_mpi, s_seq, a_mpi, r_max = _find_process_points(cfg, n) + has_mpi = bool(directories[d].get("mpi")) + has_seq = bool(directories[d].get("seq")) + report_present = (tasks_dir / d / "report.md").exists() + group_cells[0]["solution_points"] = s_mpi if has_mpi else 0 + group_cells[1]["solution_points"] = s_seq if has_seq else 0 + group_cells[0]["a_points"] = a_mpi if (has_mpi and has_seq) else 0 + group_cells[1]["a_points"] = 0 + proc_groups.extend(group_cells) + # Sum points S + A + R with gating + s_inc = (s_mpi if has_mpi else 0) + (s_seq if has_seq else 0) + a_inc = a_mpi if (has_mpi and has_seq) else 0 + r_inc = r_max if report_present else 0 + total_points_sum += s_inc + a_inc + r_inc + proc_r_values.append(r_inc) else: proc_group_headers.append({"type": "mpi", "task_label": f"task_{n}"}) proc_group_headers.append({"type": "seq", "task_label": f"task_{n}"}) @@ -453,7 +550,8 @@ def _build_cell(dir_name: str, ttype: str): "plagiarism_points": "?", } ) - # Do not affect total; sum only existing tasks + # Do not affect total; sum only existing tasks; report points 0 + proc_r_values.append(0) # Label for processes row: show Last, First, Middle on separate lines; no group number row_label = "processes" @@ -490,6 +588,8 @@ def _load_variant(dir_name: str): "task": row_label, "variant": row_variant, "groups": proc_groups, + "r_values": proc_r_values, + "r_total": sum(proc_r_values), "total": total_points_sum, } ] @@ -504,9 +604,10 @@ def _load_variant(dir_name: str): output_path.mkdir(parents=True, exist_ok=True) # Render tables + generated_msk = datetime.now(ZoneInfo("Europe/Moscow")).strftime("%Y-%m-%d %H:%M:%S") table_template = env.get_template("index.html.j2") threads_html = table_template.render( - task_types=task_types_threads, rows=threads_rows + task_types=task_types_threads, rows=threads_rows, generated_msk=generated_msk ) # Use dedicated template for processes table layout processes_template = env.get_template("processes.html.j2") @@ -514,6 +615,7 @@ def _load_variant(dir_name: str): top_task_names=proc_top_headers, group_headers=proc_group_headers, rows=processes_rows, + generated_msk=generated_msk, ) with open(output_path / "threads.html", "w") as f: @@ -562,7 +664,9 @@ def _slugify(text: str) -> str: eff_num_proc, deadlines_cfg, ) - html_g = table_template.render(task_types=task_types_threads, rows=rows_g) + html_g = table_template.render( + task_types=task_types_threads, rows=rows_g, generated_msk=generated_msk + ) with open(out_file, "w") as f: f.write(html_g) threads_groups_menu.append({"href": out_file.name, "title": g}) @@ -626,6 +730,7 @@ def _id_key(stud: dict) -> str: proc_top_headers_g = [] proc_group_headers_g = [] proc_groups_g = [] + proc_r_values_g = [] total_points_sum_g = 0 for n in [1, 2, 3]: entry = num_to_dir_g.get(n) @@ -665,7 +770,16 @@ def _id_key(stud: dict) -> str: "plagiarism_points": plagiarism_points, } ) - total_points_sum_g += task_points + # Sum points by processes maxima with gating (+ report presence) + s_mpi_g, s_seq_g, a_max_g, r_max_g = _find_process_points(cfg, n) + has_mpi_g = bool(directories[d].get("mpi")) + has_seq_g = bool(directories[d].get("seq")) + report_present_g = (tasks_dir / d / "report.md").exists() + s_inc_g = (s_mpi_g if has_mpi_g else 0) + (s_seq_g if has_seq_g else 0) + a_inc_g = a_max_g if (has_mpi_g and has_seq_g) else 0 + r_inc_g = r_max_g if report_present_g else 0 + total_points_sum_g += s_inc_g + a_inc_g + r_inc_g + proc_r_values_g.append(r_inc_g) else: proc_top_headers_g.append(f"task-{n}") for ttype in ["mpi", "seq"]: @@ -682,7 +796,8 @@ def _id_key(stud: dict) -> str: "plagiarism_points": "?", } ) - # Missing task: do not affect total; sum only existing + # Missing task: do not affect total; sum only existing; report=0 + proc_r_values_g.append(0) # Row label for group page: name without group (three lines max) row_label_g = f"group {g}" @@ -720,6 +835,8 @@ def _load_variant_g(dir_name: str): "task": row_label_g, "variant": row_variant_g, "groups": proc_groups_g, + "r_values": proc_r_values_g, + "r_total": sum(proc_r_values_g), "total": total_points_sum_g, } ] @@ -728,6 +845,7 @@ def _load_variant_g(dir_name: str): top_task_names=proc_top_headers_g, group_headers=proc_group_headers_g, rows=rows_g, + generated_msk=generated_msk, ) with open(out_file, "w") as f: f.write(html_g) @@ -755,6 +873,7 @@ def _load_variant_g(dir_name: str): ], groups_threads=threads_groups_menu, groups_processes=processes_groups_menu, + generated_msk=generated_msk, ) with open(output_path / "index.html", "w") as f: diff --git a/scoreboard/templates/index.html.j2 b/scoreboard/templates/index.html.j2 index 534c6b551..c720b6164 100644 --- a/scoreboard/templates/index.html.j2 +++ b/scoreboard/templates/index.html.j2 @@ -5,19 +5,21 @@ - +
+ Generated (MSK): {{ generated_msk }} +
{% for type in task_types %} - + {% endfor %} {% for type in task_types %} - {% for letter in ('S', 'A', 'E', 'D', 'P') %} + {% for letter in ('S', 'A', 'E', 'D', 'P', 'R') %} {% endfor %} {% endfor %} @@ -32,6 +34,7 @@ + {% endfor %} diff --git a/scoreboard/templates/menu_index.html.j2 b/scoreboard/templates/menu_index.html.j2 index ef04119b8..73532a379 100644 --- a/scoreboard/templates/menu_index.html.j2 +++ b/scoreboard/templates/menu_index.html.j2 @@ -43,6 +43,7 @@

(V)ariant - Task variant number assigned to the student.
+ (R)eport - Task report in Markdown (.md), required.
(S)olution - The correctness and completeness of the implemented solution.
(A)cceleration - The process of speeding up software to improve performance. Speedup = T(seq) / T(parallel)
(E)fficiency - Optimizing software speed-up by improving CPU utilization and resource management. Efficiency = Speedup / NumProcs * 100%
diff --git a/scoreboard/templates/processes.html.j2 b/scoreboard/templates/processes.html.j2 index 0898dff5f..47184654f 100644 --- a/scoreboard/templates/processes.html.j2 +++ b/scoreboard/templates/processes.html.j2 @@ -5,23 +5,34 @@ +

+ Generated (MSK): {{ generated_msk }} +
Name V{{ type }}{{ type }}Total
{{ letter }}{{ cell.efficiency }} {{ cell.deadline_points }} {{ cell.plagiarism_points }}{{ cell.report }}{{ row.total }}
{% for name in top_task_names %} - + {# For each task: seq (3 cols) + mpi (4 cols) + R (1 col) = 8 #} + {% endfor %} - {% for header in group_headers %} - + {% for _ in top_task_names %} + + + {% endfor %} - {% for _ in group_headers %} - {% for letter in ('S', 'A', 'E', 'D', 'P') %} + {% for _ in top_task_names %} + {# seq sub-columns (no A/E) #} + {% for letter in ('S', 'D', 'P') %} + + {% endfor %} + {# mpi sub-columns (no E) #} + {% for letter in ('S', 'A', 'D', 'P') %} {% endfor %} {% endfor %} @@ -30,12 +41,24 @@ - {% for cell in row.groups %} - - - - - + {% set ns = namespace(idx=0, gi=0) %} + {% for _ in top_task_names %} + {# cells are stored as [mpi, seq] per task; render seq first #} + {% set cell_mpi = row.groups[ns.idx] %} + {% set cell_seq = row.groups[ns.idx + 1] %} + {# seq: S, D, P #} + + + + {# mpi: S, A, D, P #} + + + + + {% set ns.idx = ns.idx + 2 %} + {# R value for this task group #} + + {% set ns.gi = ns.gi + 1 %} {% endfor %} diff --git a/tasks/example_processes/report.md b/tasks/example_processes/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_threads/all/report.md b/tasks/example_threads/all/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_threads/omp/report.md b/tasks/example_threads/omp/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_threads/seq/report.md b/tasks/example_threads/seq/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_threads/stl/report.md b/tasks/example_threads/stl/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_threads/tbb/report.md b/tasks/example_threads/tbb/report.md new file mode 100644 index 000000000..e69de29bb From 1c4be32f7459b7fb7b6f83d2f3e311e1948ee91c Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 23:09:58 +0200 Subject: [PATCH 11/18] add processes tasks: implement processes module, extend scoring system and enhance UI integration --- scoreboard/README.md | 25 +- scoreboard/data/copying.yml | 14 + scoreboard/data/deadlines.yml | 13 + scoreboard/data/plagiarism.yml | 16 - scoreboard/data/points-info.yml | 4 +- scoreboard/main.py | 558 +++++++++++++++--- scoreboard/templates/index.html.j2 | 95 ++- scoreboard/templates/menu_index.html.j2 | 9 +- scoreboard/templates/processes.html.j2 | 81 ++- tasks/example_processes/info.json | 3 +- .../common/include/common.hpp | 15 + tasks/example_processes_2/data/pic.jpg | Bin 0 -> 15356 bytes tasks/example_processes_2/info.json | 9 + .../mpi/include/ops_mpi.hpp | 22 + tasks/example_processes_2/mpi/src/ops_mpi.cpp | 72 +++ tasks/example_processes_2/report.md | 0 .../seq/include/ops_seq.hpp | 22 + tasks/example_processes_2/seq/src/ops_seq.cpp | 60 ++ tasks/example_processes_2/settings.json | 7 + tasks/example_processes_2/tests/.clang-tidy | 13 + .../tests/functional/main.cpp | 85 +++ .../tests/performance/main.cpp | 40 ++ .../common/include/common.hpp | 15 + tasks/example_processes_3/data/pic.jpg | Bin 0 -> 15356 bytes tasks/example_processes_3/info.json | 9 + .../mpi/include/ops_mpi.hpp | 22 + tasks/example_processes_3/mpi/src/ops_mpi.cpp | 72 +++ tasks/example_processes_3/report.md | 0 .../seq/include/ops_seq.hpp | 22 + tasks/example_processes_3/seq/src/ops_seq.cpp | 60 ++ tasks/example_processes_3/settings.json | 7 + tasks/example_processes_3/tests/.clang-tidy | 13 + .../tests/functional/main.cpp | 85 +++ .../tests/performance/main.cpp | 40 ++ tasks/example_threads/info.json | 3 +- tasks/example_threads/report.md | 0 36 files changed, 1399 insertions(+), 112 deletions(-) create mode 100644 scoreboard/data/copying.yml create mode 100644 scoreboard/data/deadlines.yml delete mode 100644 scoreboard/data/plagiarism.yml create mode 100644 tasks/example_processes_2/common/include/common.hpp create mode 100644 tasks/example_processes_2/data/pic.jpg create mode 100644 tasks/example_processes_2/info.json create mode 100644 tasks/example_processes_2/mpi/include/ops_mpi.hpp create mode 100644 tasks/example_processes_2/mpi/src/ops_mpi.cpp create mode 100644 tasks/example_processes_2/report.md create mode 100644 tasks/example_processes_2/seq/include/ops_seq.hpp create mode 100644 tasks/example_processes_2/seq/src/ops_seq.cpp create mode 100644 tasks/example_processes_2/settings.json create mode 100644 tasks/example_processes_2/tests/.clang-tidy create mode 100644 tasks/example_processes_2/tests/functional/main.cpp create mode 100644 tasks/example_processes_2/tests/performance/main.cpp create mode 100644 tasks/example_processes_3/common/include/common.hpp create mode 100644 tasks/example_processes_3/data/pic.jpg create mode 100644 tasks/example_processes_3/info.json create mode 100644 tasks/example_processes_3/mpi/include/ops_mpi.hpp create mode 100644 tasks/example_processes_3/mpi/src/ops_mpi.cpp create mode 100644 tasks/example_processes_3/report.md create mode 100644 tasks/example_processes_3/seq/include/ops_seq.hpp create mode 100644 tasks/example_processes_3/seq/src/ops_seq.cpp create mode 100644 tasks/example_processes_3/settings.json create mode 100644 tasks/example_processes_3/tests/.clang-tidy create mode 100644 tasks/example_processes_3/tests/functional/main.cpp create mode 100644 tasks/example_processes_3/tests/performance/main.cpp create mode 100644 tasks/example_threads/report.md diff --git a/scoreboard/README.md b/scoreboard/README.md index efaeca2fd..b43c87571 100644 --- a/scoreboard/README.md +++ b/scoreboard/README.md @@ -18,6 +18,7 @@ Generates `output_directory/index.html` with the scoreboard. - `data/points-info.yml` - Task points, deadlines, penalties - `data/plagiarism.yml` - Flagged submissions +- `data/deadlines.yml` - Optional display deadlines and day offsets ## Testing @@ -31,4 +32,26 @@ python -m pytest tests/ -v ## Output -HTML table with columns: S (solution), A (acceleration), E (efficiency), D (deadline), P (plagiarism), Total. +HTML table with columns: S (solution), A (acceleration), E (efficiency), D (deadline), C (copying), Total. + +### Deadlines display + +- Threads deadlines are auto-distributed across the Spring window: 1 Feb → 15 May. +- Processes deadlines are auto-distributed across the Autumn window: 15 Oct → 14 Dec. +- Due time is 23:59 MSK on the shown date. +- File `data/deadlines.yml` can shift dates per item by integer day offsets (default 0). Example: + +``` +threads: + seq: 0 # no shift + omp: -2 # 2 days earlier + tbb: 3 # 3 days later + stl: 0 + all: 0 +processes: + task_1: 0 + task_2: 5 + task_3: -1 +``` + +- If you put a non-integer string instead of a number, it is used as-is as the label (e.g., `"10 Nov"`). diff --git a/scoreboard/data/copying.yml b/scoreboard/data/copying.yml new file mode 100644 index 000000000..f38f6fbad --- /dev/null +++ b/scoreboard/data/copying.yml @@ -0,0 +1,14 @@ +threads: + copying: + seq: + - example_threads + omp: [] + tbb: [] + stl: [] + all: [] +processes: + copying: + mpi: + - example_processes + seq: + - example_processes diff --git a/scoreboard/data/deadlines.yml b/scoreboard/data/deadlines.yml new file mode 100644 index 000000000..152422d2e --- /dev/null +++ b/scoreboard/data/deadlines.yml @@ -0,0 +1,13 @@ +threads: + # Put integer to shift auto date by N days (negative allowed). Default 0. + seq: 0 + omp: 0 + tbb: 0 + stl: 0 + all: 0 + +processes: + # Use integer offsets for tasks; default 0. + task_1: 0 + task_2: 0 + task_3: 0 diff --git a/scoreboard/data/plagiarism.yml b/scoreboard/data/plagiarism.yml deleted file mode 100644 index 578f371ff..000000000 --- a/scoreboard/data/plagiarism.yml +++ /dev/null @@ -1,16 +0,0 @@ -plagiarism: - mpi: [] - omp: [] - seq: - - broken_example - stl: [] - tbb: [] - all: [] - -deadlines: - mpi: "2025-12-31" - omp: "2025-12-31" - seq: "2025-12-31" - stl: "2025-12-31" - tbb: "2025-12-31" - all: "2025-12-31" \ No newline at end of file diff --git a/scoreboard/data/points-info.yml b/scoreboard/data/points-info.yml index ec30f4526..82c5b774f 100644 --- a/scoreboard/data/points-info.yml +++ b/scoreboard/data/points-info.yml @@ -58,6 +58,6 @@ threads: Total: 21 efficiency: num_proc: 4 -plagiarism: +copying: coefficient: 0.5 - note: "Penalty P = -coefficient * S (scoreboard notation)" + note: "Penalty C = -coefficient * S (scoreboard notation)" diff --git a/scoreboard/main.py b/scoreboard/main.py index f1c3706cf..d2d739d77 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -2,6 +2,7 @@ from collections import defaultdict, Counter from datetime import datetime import csv +import math import argparse import subprocess import yaml @@ -9,16 +10,31 @@ from jinja2 import Environment, FileSystemLoader from zoneinfo import ZoneInfo import logging +import sys logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") logger = logging.getLogger(__name__) task_types = ["all", "mpi", "omp", "seq", "stl", "tbb"] -task_types_threads = ["all", "omp", "seq", "stl", "tbb"] +# Threads table order: seq first, then omp, tbb, stl, all +task_types_threads = ["seq", "omp", "tbb", "stl", "all"] task_types_processes = ["mpi", "seq"] script_dir = Path(__file__).parent tasks_dir = script_dir.parent / "tasks" +# Salt is derived from the repository root directory name (dynamic) +REPO_ROOT = script_dir.parent.resolve() +# Salt format: "learning_process/" +REPO_SALT = f"learning_process/{REPO_ROOT.name}" + +# Ensure we can import assign_variant from scoreboard directory +if str(script_dir) not in sys.path: + sys.path.insert(0, str(script_dir)) +try: + from assign_variant import assign_variant +except Exception: + def assign_variant(surname: str, name: str, group: str, repo: str, patronymic: str = "", num_variants: int = 1) -> int: + return 0 def _read_tasks_type(task_dir: Path) -> str | None: @@ -141,6 +157,60 @@ def _find_report_max(points_info, task_type: str) -> int: return 0 +def _find_performance_max(points_info, task_type: str) -> int: + """Resolve max Performance (A) points for a given task type (threads).""" + threads_tasks = (points_info.get("threads", {}) or {}).get("tasks", []) + for t in threads_tasks: + if str(t.get("name")) == task_type: + try: + return int(t.get("A", 0)) + except Exception: + return 0 + return 0 + + +def _calc_perf_points_from_efficiency(efficiency_str: str, max_points: int) -> float: + """Calculate Performance points as a real number (x.yy). + + Mapping (eff -> percent of max): + >=50 -> 100; [45,50) -> 90; [42,45) -> 80; [40,42) -> 70; [37,40) -> 60; + [35,37) -> 50; [32,35) -> 40; [30,32) -> 30; [27,30) -> 20; [25,27) -> 10; <25 -> 0 + Returns a float rounded to 2 decimals (no ceil). + """ + if not isinstance(efficiency_str, str) or not efficiency_str.endswith("%"): + return 0.0 + try: + val = float(efficiency_str.rstrip("%")) + except Exception: + return 0.0 + perc = 0.0 + if val >= 50: + perc = 1.0 + elif 45 <= val < 50: + perc = 0.9 + elif 42 <= val < 45: + perc = 0.8 + elif 40 <= val < 42: + perc = 0.7 + elif 37 <= val < 40: + perc = 0.6 + elif 35 <= val < 37: + perc = 0.5 + elif 32 <= val < 35: + perc = 0.4 + elif 30 <= val < 32: + perc = 0.3 + elif 27 <= val < 30: + perc = 0.2 + elif 25 <= val < 27: + perc = 0.1 + else: + perc = 0.0 + pts = max_points * perc if max_points > 0 else 0.0 + # round to 2 decimals (banker's rounding acceptable here) + return round(pts, 2) + + def _find_process_report_max(points_info, task_number: int) -> int: """Get max report (R) points for process task by ordinal (1..3). Looks up processes.tasks with names like 'mpi_task_1'. @@ -187,6 +257,18 @@ def _extract(obj, k): return s_mpi, s_seq, a_mpi, r return 0, 0, 0, 0 + +def _find_process_variants_max(points_info, task_number: int) -> int: + proc_tasks = (points_info.get("processes", {}) or {}).get("tasks", []) + key = f"mpi_task_{task_number}" + for t in proc_tasks: + if str(t.get("name")) == key: + try: + return int(t.get("variants_max", 1)) + except Exception: + return 1 + return 1 + def get_solution_points_and_style(task_type, status, cfg): """Get solution points and CSS style based on task type and status.""" max_sol_points = _find_max_solution(cfg, task_type) @@ -200,17 +282,48 @@ def get_solution_points_and_style(task_type, status, cfg): def check_plagiarism_and_calculate_penalty( - dir, task_type, sol_points, plagiarism_cfg, cfg + dir, task_type, sol_points, plagiarism_cfg, cfg, semester: str | None ): - """Check if task is plagiarized and calculate penalty points.""" + """Check if task is plagiarized and calculate penalty points. + + Supports two config layouts: + - legacy: { plagiarism: { seq: [...], omp: [...], ... } } + - semesters: { threads: {plagiarism: {...}}, processes: {plagiarism: {...}} } + """ clean_dir = dir[: -len("_disabled")] if dir.endswith("_disabled") else dir - is_cheated = ( - dir in plagiarism_cfg["plagiarism"][task_type] - or clean_dir in plagiarism_cfg["plagiarism"][task_type] - ) + + # Resolve copying/plagiarism mapping based on layout + plag_map = {} + if isinstance(plagiarism_cfg, dict) and ( + "copying" in plagiarism_cfg or "plagiarism" in plagiarism_cfg + ): + plag_map = ( + plagiarism_cfg.get("copying") + if "copying" in plagiarism_cfg + else plagiarism_cfg.get("plagiarism", {}) + ) or {} + elif ( + isinstance(plagiarism_cfg, dict) + and semester + and semester in plagiarism_cfg + and isinstance(plagiarism_cfg[semester], dict) + ): + inner = plagiarism_cfg[semester] + plag_map = (inner.get("copying") if "copying" in inner else inner.get("plagiarism", {})) or {} + + flagged_list = set(plag_map.get(task_type, []) or []) + is_cheated = dir in flagged_list or clean_dir in flagged_list plagiarism_points = 0 if is_cheated: - plag_coeff = float(cfg.get("plagiarism", {}).get("coefficient", 0.0)) + # Prefer new key 'copying', fallback to legacy 'plagiarism' + try: + plag_coeff = float( + (cfg.get("copying", {}) or cfg.get("plagiarism", {})).get( + "coefficient", 0.0 + ) + ) + except Exception: + plag_coeff = 0.0 plagiarism_points = -plag_coeff * sol_points return is_cheated, plagiarism_points @@ -255,7 +368,7 @@ def load_configurations(): eff_num_proc = int(points_info.get("efficiency", {}).get("num_proc", 1)) deadlines_cfg = points_info.get("deadlines", {}) - plagiarism_config_path = Path(__file__).parent / "data" / "plagiarism.yml" + plagiarism_config_path = Path(__file__).parent / "data" / "copying.yml" with open(plagiarism_config_path, "r") as file: plagiarism_cfg = yaml.safe_load(file) assert plagiarism_cfg, "Plagiarism configuration is empty" @@ -293,18 +406,24 @@ def _load_student_info_label(dir_name: str): except Exception: return None - def _load_variant(dir_name: str): + def _load_student_fields(dir_name: str): import json info_path = tasks_dir / dir_name / "info.json" if not info_path.exists(): - return "?" + return None try: with open(info_path, "r") as f: data = json.load(f) - return str(data.get("student", {}).get("variant_number", "?")) + s = data.get("student", {}) + return ( + str(s.get("last_name", "")), + str(s.get("first_name", "")), + str(s.get("middle_name", "")), + str(s.get("group_number", "")), + ) except Exception: - return "?" + return None for dir in sorted(dir_names): row_types = [] @@ -317,7 +436,7 @@ def _load_variant(dir_name: str): task_points = sol_points is_cheated, plagiarism_points = check_plagiarism_and_calculate_penalty( - dir, task_type, sol_points, plagiarism_cfg, cfg + dir, task_type, sol_points, plagiarism_cfg, cfg, semester="threads" ) task_points += plagiarism_points @@ -337,6 +456,19 @@ def _load_variant(dir_name: str): report_present = (tasks_dir / dir / "report.md").exists() report_points = _find_report_max(cfg, task_type) if report_present else 0 + # Performance points P for non-seq types, based on efficiency + perf_max = _find_performance_max(cfg, task_type) + if task_type != "seq": + perf_points = _calc_perf_points_from_efficiency( + efficiency, perf_max + ) + perf_points_display = ( + f"{perf_points:.2f}" if isinstance(efficiency, str) and efficiency.endswith("%") else "—" + ) + else: + perf_points = 0.0 + perf_points_display = "—" + row_types.append( { "solution_points": sol_points, @@ -344,16 +476,30 @@ def _load_variant(dir_name: str): "perf": perf_val, "acceleration": acceleration, "efficiency": efficiency, + "perf_points": perf_points, + "perf_points_display": perf_points_display, "deadline_points": deadline_points, "plagiarised": is_cheated, "plagiarism_points": plagiarism_points, "report": report_points, } ) - total_count += task_points + # Total: include Solution + Performance + Report + Copying penalty (exclude Deadline) + total_count += task_points + perf_points + report_points label_name = _load_student_info_label(dir) or dir - variant = _load_variant(dir) + # Generate variant for threads based on student info and variants_max + threads_vmax = int((cfg.get("threads", {}) or {}).get("variants_max", 1)) + fields = _load_student_fields(dir) + if fields: + last, first, middle, group = fields + try: + v_idx = assign_variant(last, first, group, REPO_SALT, patronymic=middle, num_variants=threads_vmax) + variant = str(v_idx + 1) + except Exception: + variant = "?" + else: + variant = "?" rows.append( { "task": label_name, @@ -381,6 +527,89 @@ def main(): env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates")) + # Load optional display deadlines from deadlines.yml and/or auto-compute evenly + deadlines_display_threads: dict[str, str] | None = None + deadlines_display_processes: dict[str, str] | None = None + try: + dl_file = script_dir / "data" / "deadlines.yml" + if dl_file.exists(): + with open(dl_file, "r") as f: + dl_cfg = yaml.safe_load(f) or {} + deadlines_display_threads = (dl_cfg.get("threads") or {}) + deadlines_display_processes = (dl_cfg.get("processes") or {}) + except Exception: + pass + + # Helper: compute evenly spaced dates for current semester (MSK) + from datetime import date, timedelta + from zoneinfo import ZoneInfo as _ZoneInfo + import calendar + + def _abbr(day: date) -> str: + return f"{day.day} {calendar.month_abbr[day.month]}" + + def _spring_bounds(today: date) -> tuple[date, date]: + """Return [1 Feb .. 15 May] window for the appropriate year. + If today is past 15 May, use next year's spring; otherwise this year's. + """ + y = today.year + start = date(y, 2, 1) + end = date(y, 5, 15) + if today > end: + y += 1 + start = date(y, 2, 1) + end = date(y, 5, 15) + return start, end + + def _autumn_bounds(today: date) -> tuple[date, date]: + """Return [15 Oct .. 14 Dec] window for the appropriate year. + If today is past 14 Dec, use next year's autumn; otherwise this year's. + """ + y = today.year + start = date(y, 10, 15) + end = date(y, 12, 14) + if today > end: + y += 1 + start = date(y, 10, 15) + end = date(y, 12, 14) + return start, end + + def _evenly_spaced_dates(n: int, start: date, end: date) -> list[date]: + """ + Return n deadlines evenly spaced across the window (start..end], + i.e., strictly after the start date, with the last at end. + Positions are at fractions (i+1)/n of the total span. + """ + if n <= 1: + return [end] + total = (end - start).days + if total < 0: + start, end = end, start + total = -total + res = [] + for i in range(n): + off = int(round((i + 1) * total / n)) + if off <= 0: + off = 1 + if off > total: + off = total + res.append(start + timedelta(days=off)) + return res + + def _compute_display_deadlines_threads(order: list[str]) -> dict[str, date]: + # Threads = Spring semester + today = datetime.now(_ZoneInfo("Europe/Moscow")).date() + s, e = _spring_bounds(today) + ds = _evenly_spaced_dates(len(order), s, e) + return {t: d for t, d in zip(order, ds)} + + def _compute_display_deadlines_processes(n_items: int) -> list[date]: + # Processes = Autumn semester + today = datetime.now(_ZoneInfo("Europe/Moscow")).date() + s, e = _autumn_bounds(today) + ds = _evenly_spaced_dates(n_items, s, e) + return ds + # Locate perf CSV from CI or local runs candidates = [ script_dir.parent / "build" / "perf_stat_dir" / "task_run_perf_table.csv", @@ -446,7 +675,7 @@ def _build_cell(dir_name: str, ttype: str): sol_points, solution_style = get_solution_points_and_style(ttype, status, cfg) task_points = sol_points is_cheated, plagiarism_points = check_plagiarism_and_calculate_penalty( - dir_name, ttype, sol_points, plagiarism_cfg, cfg + dir_name, ttype, sol_points, plagiarism_cfg, cfg, semester="processes" ) task_points += plagiarism_points perf_val = perf_stats.get(dir_name, {}).get(ttype, "?") @@ -524,14 +753,44 @@ def _build_cell(dir_name: str, ttype: str): report_present = (tasks_dir / d / "report.md").exists() group_cells[0]["solution_points"] = s_mpi if has_mpi else 0 group_cells[1]["solution_points"] = s_seq if has_seq else 0 - group_cells[0]["a_points"] = a_mpi if (has_mpi and has_seq) else 0 - group_cells[1]["a_points"] = 0 + # Calculate Performance P for MPI based on efficiency and max a_mpi + mpi_eff = group_cells[0].get("efficiency", "N/A") + perf_points_mpi = ( + _calc_perf_points_from_efficiency(mpi_eff, a_mpi) + if (has_mpi and has_seq) + else 0 + ) + # Display '—' instead of 0 when metrics are absent (efficiency not a percent) + if isinstance(mpi_eff, str) and mpi_eff.endswith("%"): + perf_points_mpi_display = perf_points_mpi + else: + perf_points_mpi_display = "—" + group_cells[0]["perf_points"] = perf_points_mpi + group_cells[0]["perf_points_display"] = perf_points_mpi_display + group_cells[1]["perf_points"] = 0 + # Recompute plagiarism penalty based on processes S maxima + try: + plag_coeff = float( + (cfg.get("copying", {}) or cfg.get("plagiarism", {})).get( + "coefficient", 0.0 + ) + ) + except Exception: + plag_coeff = 0.0 + p_mpi = ( + -plag_coeff * s_mpi if (has_mpi and group_cells[0].get("plagiarised")) else 0 + ) + p_seq = ( + -plag_coeff * s_seq if (has_seq and group_cells[1].get("plagiarised")) else 0 + ) + group_cells[0]["plagiarism_points"] = p_mpi + group_cells[1]["plagiarism_points"] = p_seq proc_groups.extend(group_cells) - # Sum points S + A + R with gating + # Sum points S + P + R + C (penalty negative) with gating s_inc = (s_mpi if has_mpi else 0) + (s_seq if has_seq else 0) - a_inc = a_mpi if (has_mpi and has_seq) else 0 + p_inc = perf_points_mpi r_inc = r_max if report_present else 0 - total_points_sum += s_inc + a_inc + r_inc + total_points_sum += s_inc + p_inc + r_inc + p_mpi + p_seq proc_r_values.append(r_inc) else: proc_group_headers.append({"type": "mpi", "task_label": f"task_{n}"}) @@ -564,25 +823,28 @@ def _build_cell(dir_name: str, ttype: str): name = "
".join(name_parts) row_label = name or row_label - # Choose variant from the first available task (1..3) - def _load_variant(dir_name: str): - import json - - info_path = tasks_dir / dir_name / "info.json" - if not info_path.exists(): - return "?" - try: - with open(info_path, "r") as f: - data = json.load(f) - return str(data.get("student", {}).get("variant_number", "?")) - except Exception: - return "?" - - for n in expected_numbers: - ent = num_to_dir.get(n) - if ent: - row_variant = _load_variant(ent[0]) - break + # Build three variants (one per task) based on student identity + row_variant = "?" + if target_identity: + parts = target_identity.split("|") + if len(parts) >= 4: + first, last, middle, group = parts[0], parts[1], parts[2], parts[3] + variants_render = [] + for n in expected_numbers: + vmax = _find_process_variants_max(cfg, n) + try: + v_idx = assign_variant( + surname=last, + name=first, + patronymic=middle, + group=group, + repo=f"{REPO_SALT}/processes/task-{n}", + num_variants=vmax, + ) + variants_render.append(str(v_idx + 1)) + except Exception: + variants_render.append("?") + row_variant = "
".join(variants_render) processes_rows = [ { "task": row_label, @@ -606,16 +868,75 @@ def _load_variant(dir_name: str): # Render tables generated_msk = datetime.now(ZoneInfo("Europe/Moscow")).strftime("%Y-%m-%d %H:%M:%S") table_template = env.get_template("index.html.j2") + threads_vmax = int((cfg.get("threads", {}) or {}).get("variants_max", 1)) + # Build display deadlines (use file values if present, fill missing with auto) + threads_order = task_types_threads + auto_threads_dl = _compute_display_deadlines_threads(threads_order) + dl_threads_out = {} + for t in threads_order: + base_date = auto_threads_dl.get(t) + # Default = 0 shift + shift_days = 0 + label = None + if deadlines_display_threads and t in deadlines_display_threads: + val = deadlines_display_threads.get(t) + if isinstance(val, int): + shift_days = val + else: + # try int-like string, else treat as explicit label + try: + shift_days = int(str(val).strip()) + except Exception: + label = str(val) + if label is None and isinstance(base_date, date): + vdate = base_date + timedelta(days=shift_days) + dl_threads_out[t] = _abbr(vdate) + else: + dl_threads_out[t] = label or "" + threads_html = table_template.render( - task_types=task_types_threads, rows=threads_rows, generated_msk=generated_msk + task_types=task_types_threads, + rows=threads_rows, + generated_msk=generated_msk, + repo_salt=REPO_SALT, + threads_variants_max=threads_vmax, + deadlines_threads=dl_threads_out, ) # Use dedicated template for processes table layout processes_template = env.get_template("processes.html.j2") + proc_vmaxes = [_find_process_variants_max(cfg, n) for n in expected_numbers] + # Build display deadlines for processes in task order (1..3) + auto_proc_dl = _compute_display_deadlines_processes(len(expected_numbers)) + proc_deadlines_list: list[str] = [] + for i, n in enumerate(expected_numbers): + base_date = auto_proc_dl[i] + shift_days = 0 + label = None + if deadlines_display_processes: + key = f"task_{n}" + val = deadlines_display_processes.get(key) or deadlines_display_processes.get(f"mpi_task_{n}") + if val is not None: + if isinstance(val, int): + shift_days = val + else: + try: + shift_days = int(str(val).strip()) + except Exception: + label = str(val) + if label is None and isinstance(base_date, date): + vdate = base_date + timedelta(days=shift_days) + proc_deadlines_list.append(_abbr(vdate)) + else: + proc_deadlines_list.append(label or "") + processes_html = processes_template.render( top_task_names=proc_top_headers, group_headers=proc_group_headers, rows=processes_rows, generated_msk=generated_msk, + repo_salt=REPO_SALT, + processes_variants_max=proc_vmaxes, + deadlines_processes=proc_deadlines_list, ) with open(output_path / "threads.html", "w") as f: @@ -664,8 +985,35 @@ def _slugify(text: str) -> str: eff_num_proc, deadlines_cfg, ) + # Rebuild deadline labels for this page + auto_threads_dl_g = _compute_display_deadlines_threads(threads_order) + dl_threads_out_g = {} + for t in threads_order: + base_date = auto_threads_dl_g.get(t) + shift_days = 0 + label = None + if deadlines_display_threads and t in deadlines_display_threads: + val = deadlines_display_threads.get(t) + if isinstance(val, int): + shift_days = val + else: + try: + shift_days = int(str(val).strip()) + except Exception: + label = str(val) + if label is None and isinstance(base_date, date): + vdate = base_date + timedelta(days=shift_days) + dl_threads_out_g[t] = _abbr(vdate) + else: + dl_threads_out_g[t] = label or "" + html_g = table_template.render( - task_types=task_types_threads, rows=rows_g, generated_msk=generated_msk + task_types=task_types_threads, + rows=rows_g, + generated_msk=generated_msk, + repo_salt=REPO_SALT, + threads_variants_max=threads_vmax, + deadlines_threads=dl_threads_out_g, ) with open(out_file, "w") as f: f.write(html_g) @@ -747,7 +1095,12 @@ def _id_key(stud: dict) -> str: task_points = sol_points is_cheated, plagiarism_points = ( check_plagiarism_and_calculate_penalty( - d, ttype, sol_points, plagiarism_cfg, cfg + d, + ttype, + sol_points, + plagiarism_cfg, + cfg, + semester="processes", ) ) task_points += plagiarism_points @@ -770,15 +1123,58 @@ def _id_key(stud: dict) -> str: "plagiarism_points": plagiarism_points, } ) - # Sum points by processes maxima with gating (+ report presence) + # Override displayed points to processes maxima and recompute P s_mpi_g, s_seq_g, a_max_g, r_max_g = _find_process_points(cfg, n) has_mpi_g = bool(directories[d].get("mpi")) has_seq_g = bool(directories[d].get("seq")) report_present_g = (tasks_dir / d / "report.md").exists() + base_idx = len(proc_groups_g) - 2 + if base_idx >= 0: + proc_groups_g[base_idx]["solution_points"] = ( + s_mpi_g if has_mpi_g else 0 + ) + proc_groups_g[base_idx + 1]["solution_points"] = ( + s_seq_g if has_seq_g else 0 + ) + # Performance for MPI cell + mpi_eff_g = proc_groups_g[base_idx].get("efficiency", "N/A") + perf_points_mpi_g = ( + _calc_perf_points_from_efficiency(mpi_eff_g, a_max_g) + if (has_mpi_g and has_seq_g) + else 0 + ) + if isinstance(mpi_eff_g, str) and mpi_eff_g.endswith("%"): + perf_points_mpi_display_g = perf_points_mpi_g + else: + perf_points_mpi_display_g = "—" + proc_groups_g[base_idx]["perf_points"] = perf_points_mpi_g + proc_groups_g[base_idx]["perf_points_display"] = perf_points_mpi_display_g + proc_groups_g[base_idx + 1]["perf_points"] = 0 + try: + plag_coeff_g = float( + (cfg.get("copying", {}) or cfg.get("plagiarism", {})).get( + "coefficient", 0.0 + ) + ) + except Exception: + plag_coeff_g = 0.0 + p_mpi_g = ( + -plag_coeff_g * s_mpi_g + if (has_mpi_g and proc_groups_g[base_idx].get("plagiarised")) + else 0 + ) + p_seq_g = ( + -plag_coeff_g * s_seq_g + if (has_seq_g and proc_groups_g[base_idx + 1].get("plagiarised")) + else 0 + ) + proc_groups_g[base_idx]["plagiarism_points"] = p_mpi_g + proc_groups_g[base_idx + 1]["plagiarism_points"] = p_seq_g + + # Sum points by processes S + P + R (and C penalties) s_inc_g = (s_mpi_g if has_mpi_g else 0) + (s_seq_g if has_seq_g else 0) - a_inc_g = a_max_g if (has_mpi_g and has_seq_g) else 0 r_inc_g = r_max_g if report_present_g else 0 - total_points_sum_g += s_inc_g + a_inc_g + r_inc_g + total_points_sum_g += s_inc_g + perf_points_mpi_g + r_inc_g + p_mpi_g + p_seq_g proc_r_values_g.append(r_inc_g) else: proc_top_headers_g.append(f"task-{n}") @@ -809,26 +1205,28 @@ def _id_key(stud: dict) -> str: nm = "
".join(nm_parts) row_label_g = nm or row_label_g - # Variant for group row - def _load_variant_g(dir_name: str): - import json - - info_path = tasks_dir / dir_name / "info.json" - if not info_path.exists(): - return "?" - try: - with open(info_path, "r") as f: - data = json.load(f) - return str(data.get("student", {}).get("variant_number", "?")) - except Exception: - return "?" - + # Build three variants (one per task) based on student identity row_variant_g = "?" - for n in [1, 2, 3]: - entry2 = num_to_dir_g.get(n) - if entry2: - row_variant_g = _load_variant_g(entry2[0]) - break + if target_identity_g: + parts = target_identity_g.split("|") + if len(parts) >= 4: + first, last, middle, group = parts[0], parts[1], parts[2], parts[3] + vrender = [] + for n in [1, 2, 3]: + vmax = _find_process_variants_max(cfg, n) + try: + v_idx = assign_variant( + surname=last, + name=first, + patronymic=middle, + group=group, + repo=f"{REPO_SALT}/processes/task-{n}", + num_variants=vmax, + ) + vrender.append(str(v_idx + 1)) + except Exception: + vrender.append("?") + row_variant_g = "
".join(vrender) rows_g = [ { @@ -841,11 +1239,39 @@ def _load_variant_g(dir_name: str): } ] + proc_vmaxes_g = [_find_process_variants_max(cfg, n) for n in [1, 2, 3]] + # Build display deadlines for processes group page + auto_proc_dl_g = _compute_display_deadlines_processes(3) + proc_deadlines_list_g: list[str] = [] + for i, n in enumerate([1, 2, 3]): + base_date = auto_proc_dl_g[i] + shift_days = 0 + label = None + if deadlines_display_processes: + key = f"task_{n}" + val = deadlines_display_processes.get(key) or deadlines_display_processes.get(f"mpi_task_{n}") + if val is not None: + if isinstance(val, int): + shift_days = val + else: + try: + shift_days = int(str(val).strip()) + except Exception: + label = str(val) + if label is None and isinstance(base_date, date): + vdate = base_date + timedelta(days=shift_days) + proc_deadlines_list_g.append(_abbr(vdate)) + else: + proc_deadlines_list_g.append(label or "") + html_g = processes_template.render( top_task_names=proc_top_headers_g, group_headers=proc_group_headers_g, rows=rows_g, generated_msk=generated_msk, + repo_salt=REPO_SALT, + processes_variants_max=proc_vmaxes_g, + deadlines_processes=proc_deadlines_list_g, ) with open(out_file, "w") as f: f.write(html_g) diff --git a/scoreboard/templates/index.html.j2 b/scoreboard/templates/index.html.j2 index c720b6164..37151c30c 100644 --- a/scoreboard/templates/index.html.j2 +++ b/scoreboard/templates/index.html.j2 @@ -8,33 +8,106 @@
Generated (MSK): {{ generated_msk }}
+
+
Variant Calculator (Threads)
+
+ + + + + + +
+ +
Name V{{ name }}{{ name }}Total
{{ header.type }}seqmpiR
{{ letter }}{{ letter }}
{{ row.task }} {{ row.variant }}{{ cell.solution_points }}{{ cell.acceleration }}{{ cell.efficiency }}{{ cell.deadline_points }}{{ cell.plagiarism_points }}{{ cell_seq.solution_points }}{{ cell_seq.deadline_points }}{{ cell_seq.plagiarism_points }}{{ cell_mpi.solution_points }}{{ cell_mpi.a_points if cell_mpi.a_points is defined else 0 }}{{ cell_mpi.deadline_points }}{{ cell_mpi.plagiarism_points }}{{ row.r_values[ns.gi] if row.r_values is defined else 0 }}{{ row.total }}
{% for type in task_types %} - + {% set span = 4 if type == 'seq' else 7 %} + {% endfor %} {% for type in task_types %} - {% for letter in ('S', 'A', 'E', 'D', 'P', 'R') %} - - {% endfor %} + {% if type == 'seq' %} + {% for letter in ('S', 'D', 'C', 'R') %} + + {% endfor %} + {% else %} + {% for letter in ('S', 'P', 'A', 'E', 'D', 'C', 'R') %} + + {% endfor %} + {% endif %} {% endfor %} {% for row in rows %} - {% for cell in row.types %} - - - - - - + {% for type in task_types %} + {% set cell = row.types[loop.index0] %} + {% if type == 'seq' %} + + + + + {% else %} + + + + + + + + {% endif %} {% endfor %} diff --git a/scoreboard/templates/menu_index.html.j2 b/scoreboard/templates/menu_index.html.j2 index 73532a379..73d125e98 100644 --- a/scoreboard/templates/menu_index.html.j2 +++ b/scoreboard/templates/menu_index.html.j2 @@ -30,7 +30,7 @@ } box.innerHTML = html || 'No groups'; } - window.addEventListener('DOMContentLoaded', () => setGroups('threads')); + window.addEventListener('DOMContentLoaded', () => setGroups('processes')); @@ -47,9 +47,10 @@ (S)olution - The correctness and completeness of the implemented solution.
(A)cceleration - The process of speeding up software to improve performance. Speedup = T(seq) / T(parallel)
(E)fficiency - Optimizing software speed-up by improving CPU utilization and resource management. Efficiency = Speedup / NumProcs * 100%
- (D)eadline - The timeliness of the submission in relation to the given deadline.
- (P)lagiarism - The originality of the work, ensuring no copied content from external sources. + (P)erformance - Points awarded based on efficiency thresholds (see docs).
+ (D)eadline - The timeliness of the submission in relation to the given deadline (due at 23:59 MSK on the shown date).
+ (C)opying - Penalty for detected copying cases.

- + diff --git a/scoreboard/templates/processes.html.j2 b/scoreboard/templates/processes.html.j2 index 47184654f..c39cf32e7 100644 --- a/scoreboard/templates/processes.html.j2 +++ b/scoreboard/templates/processes.html.j2 @@ -8,31 +8,94 @@
Generated (MSK): {{ generated_msk }}
+
+
Variant Calculator (Processes)
+
+ + + + + + +
+ +
Name V{{ type }} +
+ {{ type }} + {% if deadlines_threads %} + {{ deadlines_threads.get(type, '') }} + {% endif %} +
+
Total
{{ letter }}{{ letter }}{{ letter }}
{{ row.task }} {{ row.variant }}{{ cell.solution_points }}{{ cell.acceleration }}{{ cell.efficiency }}{{ cell.deadline_points }}{{ cell.plagiarism_points }}{{ cell.report }}{{ cell.solution_points }}{{ cell.deadline_points }}{{ cell.plagiarism_points }}{{ cell.report }}{{ cell.solution_points }}{{ cell.perf_points_display }}{{ cell.acceleration }}{{ cell.efficiency }}{{ cell.deadline_points }}{{ cell.plagiarism_points }}{{ cell.report }}{{ row.total }}
{% for name in top_task_names %} - {# For each task: seq (3 cols) + mpi (4 cols) + R (1 col) = 8 #} - + {# For each task: seq (3) + mpi (6) + R (1) = 10 #} + {% endfor %} {% for _ in top_task_names %} - + {% endfor %} {% for _ in top_task_names %} {# seq sub-columns (no A/E) #} - {% for letter in ('S', 'D', 'P') %} + {% for letter in ('S', 'D', 'C') %} {% endfor %} - {# mpi sub-columns (no E) #} - {% for letter in ('S', 'A', 'D', 'P') %} + {# mpi sub-columns include P (points) + A/E metrics #} + {% for letter in ('S', 'P', 'A', 'E', 'D', 'C') %} {% endfor %} {% endfor %} @@ -50,9 +113,11 @@ - {# mpi: S, A, D, P #} + {# mpi: S, P, A, E, D, C #} - + + + {% set ns.idx = ns.idx + 2 %} diff --git a/tasks/example_processes/info.json b/tasks/example_processes/info.json index 513436aac..de9442bb9 100644 --- a/tasks/example_processes/info.json +++ b/tasks/example_processes/info.json @@ -4,7 +4,6 @@ "last_name": "last_name_p", "middle_name": "middle_name_p", "group_number": "2222222_p", - "task_number": "1", - "variant_number": "23" + "task_number": "1" } } diff --git a/tasks/example_processes_2/common/include/common.hpp b/tasks/example_processes_2/common/include/common.hpp new file mode 100644 index 000000000..127d96c4c --- /dev/null +++ b/tasks/example_processes_2/common/include/common.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace nesterov_a_test_task_processes { + +using InType = int; +using OutType = int; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_2/data/pic.jpg b/tasks/example_processes_2/data/pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..34458023494a39d66880a98aa5dcad18eb14b249 GIT binary patch literal 15356 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 + +#include +#include + +#include "example_processes/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace nesterov_a_test_task_processes { + +NesterovATestTaskMPI::NesterovATestTaskMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool NesterovATestTaskMPI::ValidationImpl() { + return (GetInput() > 0) && (GetOutput() == 0); +} + +bool NesterovATestTaskMPI::PreProcessingImpl() { + GetOutput() = 2 * GetInput(); + return GetOutput() > 0; +} + +bool NesterovATestTaskMPI::RunImpl() { + auto input = GetInput(); + if (input == 0) { + return false; + } + + for (InType i = 0; i < GetInput(); i++) { + for (InType j = 0; j < GetInput(); j++) { + for (InType k = 0; k < GetInput(); k++) { + std::vector tmp(i + j + k, 1); + GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0); + GetOutput() -= i + j + k; + } + } + } + + const int num_threads = ppc::util::GetNumThreads(); + GetOutput() *= num_threads; + + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + if (rank == 0) { + GetOutput() /= num_threads; + } else { + int counter = 0; + for (int i = 0; i < num_threads; i++) { + counter++; + } + + if (counter != 0) { + GetOutput() /= counter; + } + } + + MPI_Barrier(MPI_COMM_WORLD); + return GetOutput() > 0; +} + +bool NesterovATestTaskMPI::PostProcessingImpl() { + GetOutput() -= GetInput(); + return GetOutput() > 0; +} + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_2/report.md b/tasks/example_processes_2/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_processes_2/seq/include/ops_seq.hpp b/tasks/example_processes_2/seq/include/ops_seq.hpp new file mode 100644 index 000000000..6a1cdd4ce --- /dev/null +++ b/tasks/example_processes_2/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "example_processes/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace nesterov_a_test_task_processes { + +class NesterovATestTaskSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit NesterovATestTaskSEQ(const InType& in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_2/seq/src/ops_seq.cpp b/tasks/example_processes_2/seq/src/ops_seq.cpp new file mode 100644 index 000000000..9b0b7b582 --- /dev/null +++ b/tasks/example_processes_2/seq/src/ops_seq.cpp @@ -0,0 +1,60 @@ +#include "example_processes/seq/include/ops_seq.hpp" + +#include +#include + +#include "example_processes/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace nesterov_a_test_task_processes { + +NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType& in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool NesterovATestTaskSEQ::ValidationImpl() { + return (GetInput() > 0) && (GetOutput() == 0); +} + +bool NesterovATestTaskSEQ::PreProcessingImpl() { + GetOutput() = 2 * GetInput(); + return GetOutput() > 0; +} + +bool NesterovATestTaskSEQ::RunImpl() { + if (GetInput() == 0) { + return false; + } + + for (InType i = 0; i < GetInput(); i++) { + for (InType j = 0; j < GetInput(); j++) { + for (InType k = 0; k < GetInput(); k++) { + std::vector tmp(i + j + k, 1); + GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0); + GetOutput() -= i + j + k; + } + } + } + + const int num_threads = ppc::util::GetNumThreads(); + GetOutput() *= num_threads; + + int counter = 0; + for (int i = 0; i < num_threads; i++) { + counter++; + } + + if (counter != 0) { + GetOutput() /= counter; + } + return GetOutput() > 0; +} + +bool NesterovATestTaskSEQ::PostProcessingImpl() { + GetOutput() -= GetInput(); + return GetOutput() > 0; +} + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_2/settings.json b/tasks/example_processes_2/settings.json new file mode 100644 index 000000000..b1a0d5257 --- /dev/null +++ b/tasks/example_processes_2/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/example_processes_2/tests/.clang-tidy b/tasks/example_processes_2/tests/.clang-tidy new file mode 100644 index 000000000..ef43b7aa8 --- /dev/null +++ b/tasks/example_processes_2/tests/.clang-tidy @@ -0,0 +1,13 @@ +InheritParentConfig: true + +Checks: > + -modernize-loop-convert, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-non-const-global-variables, + -misc-use-anonymous-namespace, + -modernize-use-std-print, + -modernize-type-traits + +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: 50 # Relaxed for tests diff --git a/tasks/example_processes_2/tests/functional/main.cpp b/tasks/example_processes_2/tests/functional/main.cpp new file mode 100644 index 000000000..d3f5f3795 --- /dev/null +++ b/tasks/example_processes_2/tests/functional/main.cpp @@ -0,0 +1,85 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "example_processes/common/include/common.hpp" +#include "example_processes/mpi/include/ops_mpi.hpp" +#include "example_processes/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace nesterov_a_test_task_processes { + +class NesterovARunFuncTestsProcesses : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void SetUp() override { + int width = -1; + int height = -1; + int channels = -1; + std::vector img; + // Read image + { + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example_processes, "pic.jpg"); + auto *data = stbi_load(abs_path.c_str(), &width, &height, &channels, 0); + if (data == nullptr) { + throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason())); + } + img = std::vector(data, data + (static_cast(width * height * channels))); + stbi_image_free(data); + if (std::cmp_not_equal(width, height)) { + throw std::runtime_error("width != height: "); + } + } + + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + input_data_ = width - height + std::min(std::accumulate(img.begin(), img.end(), 0), channels); + } + + bool CheckTestOutputData(OutType &output_data) final { + return (input_data_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_ = 0; +}; + +namespace { + +TEST_P(NesterovARunFuncTestsProcesses, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; + +const auto kTestTasksList = + std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = NesterovARunFuncTestsProcesses::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_2/tests/performance/main.cpp b/tasks/example_processes_2/tests/performance/main.cpp new file mode 100644 index 000000000..7946e1bf5 --- /dev/null +++ b/tasks/example_processes_2/tests/performance/main.cpp @@ -0,0 +1,40 @@ +#include + +#include "example_processes/common/include/common.hpp" +#include "example_processes/mpi/include/ops_mpi.hpp" +#include "example_processes/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace nesterov_a_test_task_processes { + +class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { + const int kCount_ = 200; + InType input_data_{}; + + void SetUp() override { + input_data_ = kCount_; + } + + bool CheckTestOutputData(OutType& output_data) final { + return input_data_ == output_data; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(ExampleRunPerfTestProcesses, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example_processes); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = ExampleRunPerfTestProcesses::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, ExampleRunPerfTestProcesses, kGtestValues, kPerfTestName); + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_3/common/include/common.hpp b/tasks/example_processes_3/common/include/common.hpp new file mode 100644 index 000000000..127d96c4c --- /dev/null +++ b/tasks/example_processes_3/common/include/common.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace nesterov_a_test_task_processes { + +using InType = int; +using OutType = int; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_3/data/pic.jpg b/tasks/example_processes_3/data/pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..34458023494a39d66880a98aa5dcad18eb14b249 GIT binary patch literal 15356 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 + +#include +#include + +#include "example_processes/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace nesterov_a_test_task_processes { + +NesterovATestTaskMPI::NesterovATestTaskMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool NesterovATestTaskMPI::ValidationImpl() { + return (GetInput() > 0) && (GetOutput() == 0); +} + +bool NesterovATestTaskMPI::PreProcessingImpl() { + GetOutput() = 2 * GetInput(); + return GetOutput() > 0; +} + +bool NesterovATestTaskMPI::RunImpl() { + auto input = GetInput(); + if (input == 0) { + return false; + } + + for (InType i = 0; i < GetInput(); i++) { + for (InType j = 0; j < GetInput(); j++) { + for (InType k = 0; k < GetInput(); k++) { + std::vector tmp(i + j + k, 1); + GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0); + GetOutput() -= i + j + k; + } + } + } + + const int num_threads = ppc::util::GetNumThreads(); + GetOutput() *= num_threads; + + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + if (rank == 0) { + GetOutput() /= num_threads; + } else { + int counter = 0; + for (int i = 0; i < num_threads; i++) { + counter++; + } + + if (counter != 0) { + GetOutput() /= counter; + } + } + + MPI_Barrier(MPI_COMM_WORLD); + return GetOutput() > 0; +} + +bool NesterovATestTaskMPI::PostProcessingImpl() { + GetOutput() -= GetInput(); + return GetOutput() > 0; +} + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_3/report.md b/tasks/example_processes_3/report.md new file mode 100644 index 000000000..e69de29bb diff --git a/tasks/example_processes_3/seq/include/ops_seq.hpp b/tasks/example_processes_3/seq/include/ops_seq.hpp new file mode 100644 index 000000000..6a1cdd4ce --- /dev/null +++ b/tasks/example_processes_3/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "example_processes/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace nesterov_a_test_task_processes { + +class NesterovATestTaskSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit NesterovATestTaskSEQ(const InType& in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_3/seq/src/ops_seq.cpp b/tasks/example_processes_3/seq/src/ops_seq.cpp new file mode 100644 index 000000000..9b0b7b582 --- /dev/null +++ b/tasks/example_processes_3/seq/src/ops_seq.cpp @@ -0,0 +1,60 @@ +#include "example_processes/seq/include/ops_seq.hpp" + +#include +#include + +#include "example_processes/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace nesterov_a_test_task_processes { + +NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType& in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool NesterovATestTaskSEQ::ValidationImpl() { + return (GetInput() > 0) && (GetOutput() == 0); +} + +bool NesterovATestTaskSEQ::PreProcessingImpl() { + GetOutput() = 2 * GetInput(); + return GetOutput() > 0; +} + +bool NesterovATestTaskSEQ::RunImpl() { + if (GetInput() == 0) { + return false; + } + + for (InType i = 0; i < GetInput(); i++) { + for (InType j = 0; j < GetInput(); j++) { + for (InType k = 0; k < GetInput(); k++) { + std::vector tmp(i + j + k, 1); + GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0); + GetOutput() -= i + j + k; + } + } + } + + const int num_threads = ppc::util::GetNumThreads(); + GetOutput() *= num_threads; + + int counter = 0; + for (int i = 0; i < num_threads; i++) { + counter++; + } + + if (counter != 0) { + GetOutput() /= counter; + } + return GetOutput() > 0; +} + +bool NesterovATestTaskSEQ::PostProcessingImpl() { + GetOutput() -= GetInput(); + return GetOutput() > 0; +} + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_3/settings.json b/tasks/example_processes_3/settings.json new file mode 100644 index 000000000..b1a0d5257 --- /dev/null +++ b/tasks/example_processes_3/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/example_processes_3/tests/.clang-tidy b/tasks/example_processes_3/tests/.clang-tidy new file mode 100644 index 000000000..ef43b7aa8 --- /dev/null +++ b/tasks/example_processes_3/tests/.clang-tidy @@ -0,0 +1,13 @@ +InheritParentConfig: true + +Checks: > + -modernize-loop-convert, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-non-const-global-variables, + -misc-use-anonymous-namespace, + -modernize-use-std-print, + -modernize-type-traits + +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: 50 # Relaxed for tests diff --git a/tasks/example_processes_3/tests/functional/main.cpp b/tasks/example_processes_3/tests/functional/main.cpp new file mode 100644 index 000000000..d3f5f3795 --- /dev/null +++ b/tasks/example_processes_3/tests/functional/main.cpp @@ -0,0 +1,85 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "example_processes/common/include/common.hpp" +#include "example_processes/mpi/include/ops_mpi.hpp" +#include "example_processes/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace nesterov_a_test_task_processes { + +class NesterovARunFuncTestsProcesses : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void SetUp() override { + int width = -1; + int height = -1; + int channels = -1; + std::vector img; + // Read image + { + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example_processes, "pic.jpg"); + auto *data = stbi_load(abs_path.c_str(), &width, &height, &channels, 0); + if (data == nullptr) { + throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason())); + } + img = std::vector(data, data + (static_cast(width * height * channels))); + stbi_image_free(data); + if (std::cmp_not_equal(width, height)) { + throw std::runtime_error("width != height: "); + } + } + + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + input_data_ = width - height + std::min(std::accumulate(img.begin(), img.end(), 0), channels); + } + + bool CheckTestOutputData(OutType &output_data) final { + return (input_data_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_ = 0; +}; + +namespace { + +TEST_P(NesterovARunFuncTestsProcesses, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; + +const auto kTestTasksList = + std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = NesterovARunFuncTestsProcesses::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_processes_3/tests/performance/main.cpp b/tasks/example_processes_3/tests/performance/main.cpp new file mode 100644 index 000000000..7946e1bf5 --- /dev/null +++ b/tasks/example_processes_3/tests/performance/main.cpp @@ -0,0 +1,40 @@ +#include + +#include "example_processes/common/include/common.hpp" +#include "example_processes/mpi/include/ops_mpi.hpp" +#include "example_processes/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace nesterov_a_test_task_processes { + +class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { + const int kCount_ = 200; + InType input_data_{}; + + void SetUp() override { + input_data_ = kCount_; + } + + bool CheckTestOutputData(OutType& output_data) final { + return input_data_ == output_data; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(ExampleRunPerfTestProcesses, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example_processes); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = ExampleRunPerfTestProcesses::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, ExampleRunPerfTestProcesses, kGtestValues, kPerfTestName); + +} // namespace nesterov_a_test_task_processes diff --git a/tasks/example_threads/info.json b/tasks/example_threads/info.json index cde6f792c..4df5e8c27 100644 --- a/tasks/example_threads/info.json +++ b/tasks/example_threads/info.json @@ -4,7 +4,6 @@ "last_name": "last_name_t", "middle_name": "middle_name_t", "group_number": "2222222_t", - "task_number": "1", - "variant_number": "23" + "task_number": "1" } } diff --git a/tasks/example_threads/report.md b/tasks/example_threads/report.md new file mode 100644 index 000000000..e69de29bb From 7f87f06cdedcdce8ffc552be485126d5df3774f0 Mon Sep 17 00:00:00 2001 From: Nesterov Alexander Date: Thu, 9 Oct 2025 23:21:03 +0200 Subject: [PATCH 12/18] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 68afc8704..b41003750 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,8 +33,8 @@ jobs: perf: needs: - ubuntu -# - mac -# - windows + - mac + - windows uses: ./.github/workflows/perf.yml pages: From f7b75440bb76fac5a250998ea5f8d76a3bb382c6 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 23:23:07 +0200 Subject: [PATCH 13/18] chore(pre-commit): autofix formatting and lint for changes since upstream/master --- scoreboard/assign_variant.py | 13 ++-- scoreboard/main.py | 71 ++++++++++++++----- .../seq/include/ops_seq.hpp | 2 +- tasks/example_processes_2/seq/src/ops_seq.cpp | 2 +- .../tests/performance/main.cpp | 2 +- .../seq/include/ops_seq.hpp | 2 +- tasks/example_processes_3/seq/src/ops_seq.cpp | 2 +- .../tests/performance/main.cpp | 2 +- 8 files changed, 66 insertions(+), 30 deletions(-) diff --git a/scoreboard/assign_variant.py b/scoreboard/assign_variant.py index a1ccb5ffa..feb021fa3 100644 --- a/scoreboard/assign_variant.py +++ b/scoreboard/assign_variant.py @@ -62,12 +62,12 @@ def _hash_int(key: str) -> int: def assign_variant( - surname: str, - name: str, - group: str, - repo: str, - patronymic: Optional[str] = "", - num_variants: int = 31, + surname: str, + name: str, + group: str, + repo: str, + patronymic: Optional[str] = "", + num_variants: int = 31, ) -> int: """ Deterministically returns a variant index in [0 .. num_variants-1] @@ -102,6 +102,7 @@ def assign_variant( # Minimal self-check when executed directly (no CLI arguments). if __name__ == "__main__": + def demo(): print("Demo: deterministic assignment\n") diff --git a/scoreboard/main.py b/scoreboard/main.py index d2d739d77..592895b25 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -2,7 +2,6 @@ from collections import defaultdict, Counter from datetime import datetime import csv -import math import argparse import subprocess import yaml @@ -33,7 +32,15 @@ try: from assign_variant import assign_variant except Exception: - def assign_variant(surname: str, name: str, group: str, repo: str, patronymic: str = "", num_variants: int = 1) -> int: + + def assign_variant( + surname: str, + name: str, + group: str, + repo: str, + patronymic: str = "", + num_variants: int = 1, + ) -> int: return 0 @@ -225,6 +232,7 @@ def _find_process_report_max(points_info, task_number: int) -> int: return 0 return 0 + def _find_process_points(points_info, task_number: int) -> tuple[int, int, int, int]: """Return (S_mpi, S_seq, A_mpi, R) maxima for a given process task ordinal (1..3). Supports both mapping and list-of-maps (per user's YAML example). @@ -233,6 +241,7 @@ def _find_process_points(points_info, task_number: int) -> tuple[int, int, int, key = f"mpi_task_{task_number}" for t in proc_tasks: if str(t.get("name")) == key: + def _extract(obj, k): if isinstance(obj, dict): return int(obj.get(k, 0)) @@ -269,6 +278,7 @@ def _find_process_variants_max(points_info, task_number: int) -> int: return 1 return 1 + def get_solution_points_and_style(task_type, status, cfg): """Get solution points and CSS style based on task type and status.""" max_sol_points = _find_max_solution(cfg, task_type) @@ -309,7 +319,9 @@ def check_plagiarism_and_calculate_penalty( and isinstance(plagiarism_cfg[semester], dict) ): inner = plagiarism_cfg[semester] - plag_map = (inner.get("copying") if "copying" in inner else inner.get("plagiarism", {})) or {} + plag_map = ( + inner.get("copying") if "copying" in inner else inner.get("plagiarism", {}) + ) or {} flagged_list = set(plag_map.get(task_type, []) or []) is_cheated = dir in flagged_list or clean_dir in flagged_list @@ -459,11 +471,11 @@ def _load_student_fields(dir_name: str): # Performance points P for non-seq types, based on efficiency perf_max = _find_performance_max(cfg, task_type) if task_type != "seq": - perf_points = _calc_perf_points_from_efficiency( - efficiency, perf_max - ) + perf_points = _calc_perf_points_from_efficiency(efficiency, perf_max) perf_points_display = ( - f"{perf_points:.2f}" if isinstance(efficiency, str) and efficiency.endswith("%") else "—" + f"{perf_points:.2f}" + if isinstance(efficiency, str) and efficiency.endswith("%") + else "—" ) else: perf_points = 0.0 @@ -494,7 +506,14 @@ def _load_student_fields(dir_name: str): if fields: last, first, middle, group = fields try: - v_idx = assign_variant(last, first, group, REPO_SALT, patronymic=middle, num_variants=threads_vmax) + v_idx = assign_variant( + last, + first, + group, + REPO_SALT, + patronymic=middle, + num_variants=threads_vmax, + ) variant = str(v_idx + 1) except Exception: variant = "?" @@ -535,8 +554,8 @@ def main(): if dl_file.exists(): with open(dl_file, "r") as f: dl_cfg = yaml.safe_load(f) or {} - deadlines_display_threads = (dl_cfg.get("threads") or {}) - deadlines_display_processes = (dl_cfg.get("processes") or {}) + deadlines_display_threads = dl_cfg.get("threads") or {} + deadlines_display_processes = dl_cfg.get("processes") or {} except Exception: pass @@ -778,10 +797,14 @@ def _build_cell(dir_name: str, ttype: str): except Exception: plag_coeff = 0.0 p_mpi = ( - -plag_coeff * s_mpi if (has_mpi and group_cells[0].get("plagiarised")) else 0 + -plag_coeff * s_mpi + if (has_mpi and group_cells[0].get("plagiarised")) + else 0 ) p_seq = ( - -plag_coeff * s_seq if (has_seq and group_cells[1].get("plagiarised")) else 0 + -plag_coeff * s_seq + if (has_seq and group_cells[1].get("plagiarised")) + else 0 ) group_cells[0]["plagiarism_points"] = p_mpi group_cells[1]["plagiarism_points"] = p_seq @@ -866,7 +889,9 @@ def _build_cell(dir_name: str, ttype: str): output_path.mkdir(parents=True, exist_ok=True) # Render tables - generated_msk = datetime.now(ZoneInfo("Europe/Moscow")).strftime("%Y-%m-%d %H:%M:%S") + generated_msk = datetime.now(ZoneInfo("Europe/Moscow")).strftime( + "%Y-%m-%d %H:%M:%S" + ) table_template = env.get_template("index.html.j2") threads_vmax = int((cfg.get("threads", {}) or {}).get("variants_max", 1)) # Build display deadlines (use file values if present, fill missing with auto) @@ -914,7 +939,9 @@ def _build_cell(dir_name: str, ttype: str): label = None if deadlines_display_processes: key = f"task_{n}" - val = deadlines_display_processes.get(key) or deadlines_display_processes.get(f"mpi_task_{n}") + val = deadlines_display_processes.get( + key + ) or deadlines_display_processes.get(f"mpi_task_{n}") if val is not None: if isinstance(val, int): shift_days = val @@ -1148,7 +1175,9 @@ def _id_key(stud: dict) -> str: else: perf_points_mpi_display_g = "—" proc_groups_g[base_idx]["perf_points"] = perf_points_mpi_g - proc_groups_g[base_idx]["perf_points_display"] = perf_points_mpi_display_g + proc_groups_g[base_idx]["perf_points_display"] = ( + perf_points_mpi_display_g + ) proc_groups_g[base_idx + 1]["perf_points"] = 0 try: plag_coeff_g = float( @@ -1165,7 +1194,9 @@ def _id_key(stud: dict) -> str: ) p_seq_g = ( -plag_coeff_g * s_seq_g - if (has_seq_g and proc_groups_g[base_idx + 1].get("plagiarised")) + if ( + has_seq_g and proc_groups_g[base_idx + 1].get("plagiarised") + ) else 0 ) proc_groups_g[base_idx]["plagiarism_points"] = p_mpi_g @@ -1174,7 +1205,9 @@ def _id_key(stud: dict) -> str: # Sum points by processes S + P + R (and C penalties) s_inc_g = (s_mpi_g if has_mpi_g else 0) + (s_seq_g if has_seq_g else 0) r_inc_g = r_max_g if report_present_g else 0 - total_points_sum_g += s_inc_g + perf_points_mpi_g + r_inc_g + p_mpi_g + p_seq_g + total_points_sum_g += ( + s_inc_g + perf_points_mpi_g + r_inc_g + p_mpi_g + p_seq_g + ) proc_r_values_g.append(r_inc_g) else: proc_top_headers_g.append(f"task-{n}") @@ -1249,7 +1282,9 @@ def _id_key(stud: dict) -> str: label = None if deadlines_display_processes: key = f"task_{n}" - val = deadlines_display_processes.get(key) or deadlines_display_processes.get(f"mpi_task_{n}") + val = deadlines_display_processes.get( + key + ) or deadlines_display_processes.get(f"mpi_task_{n}") if val is not None: if isinstance(val, int): shift_days = val diff --git a/tasks/example_processes_2/seq/include/ops_seq.hpp b/tasks/example_processes_2/seq/include/ops_seq.hpp index 6a1cdd4ce..f264b4fd7 100644 --- a/tasks/example_processes_2/seq/include/ops_seq.hpp +++ b/tasks/example_processes_2/seq/include/ops_seq.hpp @@ -10,7 +10,7 @@ class NesterovATestTaskSEQ : public BaseTask { static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { return ppc::task::TypeOfTask::kSEQ; } - explicit NesterovATestTaskSEQ(const InType& in); + explicit NesterovATestTaskSEQ(const InType &in); private: bool ValidationImpl() override; diff --git a/tasks/example_processes_2/seq/src/ops_seq.cpp b/tasks/example_processes_2/seq/src/ops_seq.cpp index 9b0b7b582..2599c518e 100644 --- a/tasks/example_processes_2/seq/src/ops_seq.cpp +++ b/tasks/example_processes_2/seq/src/ops_seq.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { -NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType& in) { +NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; GetOutput() = 0; diff --git a/tasks/example_processes_2/tests/performance/main.cpp b/tasks/example_processes_2/tests/performance/main.cpp index 7946e1bf5..30e13e39e 100644 --- a/tasks/example_processes_2/tests/performance/main.cpp +++ b/tasks/example_processes_2/tests/performance/main.cpp @@ -15,7 +15,7 @@ class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests Date: Thu, 9 Oct 2025 23:31:38 +0200 Subject: [PATCH 14/18] reduce performance test iteration count to 50 across processes tests --- tasks/example_processes/tests/performance/main.cpp | 2 +- tasks/example_processes_2/tests/performance/main.cpp | 2 +- tasks/example_processes_3/tests/performance/main.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks/example_processes/tests/performance/main.cpp b/tasks/example_processes/tests/performance/main.cpp index 7946e1bf5..90580c2d4 100644 --- a/tasks/example_processes/tests/performance/main.cpp +++ b/tasks/example_processes/tests/performance/main.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { - const int kCount_ = 200; + const int kCount_ = 50; InType input_data_{}; void SetUp() override { diff --git a/tasks/example_processes_2/tests/performance/main.cpp b/tasks/example_processes_2/tests/performance/main.cpp index 30e13e39e..e5c942e2e 100644 --- a/tasks/example_processes_2/tests/performance/main.cpp +++ b/tasks/example_processes_2/tests/performance/main.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { - const int kCount_ = 200; + const int kCount_ = 50; InType input_data_{}; void SetUp() override { diff --git a/tasks/example_processes_3/tests/performance/main.cpp b/tasks/example_processes_3/tests/performance/main.cpp index 30e13e39e..e5c942e2e 100644 --- a/tasks/example_processes_3/tests/performance/main.cpp +++ b/tasks/example_processes_3/tests/performance/main.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { - const int kCount_ = 200; + const int kCount_ = 50; InType input_data_{}; void SetUp() override { From 49b52f75dc71bbf34c912fe10048361bfe46ec4c Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 23:33:35 +0200 Subject: [PATCH 15/18] scoreboard: make generation robust across Python versions; add PyYAML dep and zoneinfo fallback\n\n- Add PyYAML and conditional backports.zoneinfo to requirements\n- Avoid hard dependency on zoneinfo at import time; use fallback\n- Use MSK helper in deadlines/timestamp to prevent import errors\n- No functional change to table content except timestamp format --- scoreboard/main.py | 39 ++++++++++++++++++++++++++++--------- scoreboard/requirements.txt | 2 ++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/scoreboard/main.py b/scoreboard/main.py index 592895b25..25c1206d0 100644 --- a/scoreboard/main.py +++ b/scoreboard/main.py @@ -7,10 +7,18 @@ import yaml import shutil from jinja2 import Environment, FileSystemLoader -from zoneinfo import ZoneInfo import logging import sys +# Try ZoneInfo from stdlib, then from backports, else fall back to naive time +try: + from zoneinfo import ZoneInfo # type: ignore +except Exception: # pragma: no cover - fallback for Python < 3.9 + try: + from backports.zoneinfo import ZoneInfo # type: ignore + except Exception: # Last resort: define a stub + ZoneInfo = None # type: ignore + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") logger = logging.getLogger(__name__) @@ -44,6 +52,16 @@ def assign_variant( return 0 +def _now_msk(): + """Return current datetime in MSK if tz support is available, else local time.""" + try: + if ZoneInfo is not None: + return datetime.now(ZoneInfo("Europe/Moscow")) + except Exception: + pass + return datetime.now() + + def _read_tasks_type(task_dir: Path) -> str | None: """Read tasks_type from settings.json in the task directory (if present).""" settings_path = task_dir / "settings.json" @@ -561,7 +579,6 @@ def main(): # Helper: compute evenly spaced dates for current semester (MSK) from datetime import date, timedelta - from zoneinfo import ZoneInfo as _ZoneInfo import calendar def _abbr(day: date) -> str: @@ -616,15 +633,21 @@ def _evenly_spaced_dates(n: int, start: date, end: date) -> list[date]: return res def _compute_display_deadlines_threads(order: list[str]) -> dict[str, date]: - # Threads = Spring semester - today = datetime.now(_ZoneInfo("Europe/Moscow")).date() + # Threads = Spring semester (prefer MSK; fallback to local time) + try: + today = _now_msk().date() + except Exception: + today = datetime.now().date() s, e = _spring_bounds(today) ds = _evenly_spaced_dates(len(order), s, e) return {t: d for t, d in zip(order, ds)} def _compute_display_deadlines_processes(n_items: int) -> list[date]: - # Processes = Autumn semester - today = datetime.now(_ZoneInfo("Europe/Moscow")).date() + # Processes = Autumn semester (prefer MSK; fallback to local time) + try: + today = _now_msk().date() + except Exception: + today = datetime.now().date() s, e = _autumn_bounds(today) ds = _evenly_spaced_dates(n_items, s, e) return ds @@ -889,9 +912,7 @@ def _build_cell(dir_name: str, ttype: str): output_path.mkdir(parents=True, exist_ok=True) # Render tables - generated_msk = datetime.now(ZoneInfo("Europe/Moscow")).strftime( - "%Y-%m-%d %H:%M:%S" - ) + generated_msk = _now_msk().strftime("%Y-%m-%d %H:%M:%S") table_template = env.get_template("index.html.j2") threads_vmax = int((cfg.get("threads", {}) or {}).get("variants_max", 1)) # Build display deadlines (use file values if present, fill missing with auto) diff --git a/scoreboard/requirements.txt b/scoreboard/requirements.txt index e3a1fcb25..d86cf7650 100644 --- a/scoreboard/requirements.txt +++ b/scoreboard/requirements.txt @@ -1 +1,3 @@ Jinja2>=3.0 +PyYAML>=6.0 +backports.zoneinfo; python_version < "3.9" From 025ca86c0d32a9a02ddddda6f177b15457d4018d Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Thu, 9 Oct 2025 23:35:06 +0200 Subject: [PATCH 16/18] chore: enforce consistent pointer/reference formatting across codebase --- modules/performance/include/performance.hpp | 10 +++---- modules/performance/tests/perf_tests.cpp | 16 ++++++------ modules/runners/include/runners.hpp | 10 +++---- modules/runners/src/runners.cpp | 14 +++++----- modules/task/tests/task_tests.cpp | 12 ++++----- modules/util/include/func_test_util.hpp | 26 +++++++++---------- modules/util/include/perf_test_util.hpp | 16 ++++++------ modules/util/include/util.hpp | 8 +++--- modules/util/src/util.cpp | 6 ++--- tasks/common/runners/functional.cpp | 2 +- tasks/common/runners/performance.cpp | 2 +- .../example_processes/seq/include/ops_seq.hpp | 2 +- tasks/example_processes/seq/src/ops_seq.cpp | 2 +- .../tests/performance/main.cpp | 2 +- .../tests/functional/main.cpp | 8 +++--- .../tests/performance/main.cpp | 8 +++--- .../tests/functional/main.cpp | 8 +++--- .../tests/performance/main.cpp | 8 +++--- tasks/example_threads/omp/include/ops_omp.hpp | 2 +- tasks/example_threads/omp/src/ops_omp.cpp | 2 +- tasks/example_threads/seq/include/ops_seq.hpp | 2 +- tasks/example_threads/seq/src/ops_seq.cpp | 2 +- tasks/example_threads/stl/include/ops_stl.hpp | 2 +- tasks/example_threads/tbb/include/ops_tbb.hpp | 2 +- .../tests/performance/main.cpp | 2 +- 25 files changed, 87 insertions(+), 87 deletions(-) diff --git a/modules/performance/include/performance.hpp b/modules/performance/include/performance.hpp index 8a378c838..6b74462b0 100644 --- a/modules/performance/include/performance.hpp +++ b/modules/performance/include/performance.hpp @@ -39,12 +39,12 @@ template class Perf { public: // Init performance analysis with an initialized task and initialized data - explicit Perf(const ppc::task::TaskPtr& task_ptr) : task_(task_ptr) { + explicit Perf(const ppc::task::TaskPtr &task_ptr) : task_(task_ptr) { task_ptr->GetStateOfTesting() = ppc::task::StateOfTesting::kPerf; } // Check performance of full task's pipeline: PreProcessing() -> // Validation() -> Run() -> PostProcessing() - void PipelineRun(const PerfAttr& perf_attr) { + void PipelineRun(const PerfAttr &perf_attr) { perf_results_.type_of_running = PerfResults::TypeOfRunning::kPipeline; CommonRun(perf_attr, [&] { @@ -55,7 +55,7 @@ class Perf { }, perf_results_); } // Check performance of task's Run() function - void TaskRun(const PerfAttr& perf_attr) { + void TaskRun(const PerfAttr &perf_attr) { perf_results_.type_of_running = PerfResults::TypeOfRunning::kTaskRun; task_->Validation(); @@ -69,7 +69,7 @@ class Perf { task_->PostProcessing(); } // Print results for automation checkers - void PrintPerfStatistic(const std::string& test_id) const { + void PrintPerfStatistic(const std::string &test_id) const { std::string type_test_name; if (perf_results_.type_of_running == PerfResults::TypeOfRunning::kTaskRun) { type_test_name = "task_run"; @@ -106,7 +106,7 @@ class Perf { private: PerfResults perf_results_; std::shared_ptr> task_; - static void CommonRun(const PerfAttr& perf_attr, const std::function& pipeline, PerfResults& perf_results) { + static void CommonRun(const PerfAttr &perf_attr, const std::function &pipeline, PerfResults &perf_results) { auto begin = perf_attr.current_timer(); for (uint64_t i = 0; i < perf_attr.num_running; i++) { pipeline(); diff --git a/modules/performance/tests/perf_tests.cpp b/modules/performance/tests/perf_tests.cpp index 5394c4769..9a4ce9e36 100644 --- a/modules/performance/tests/perf_tests.cpp +++ b/modules/performance/tests/perf_tests.cpp @@ -25,7 +25,7 @@ namespace ppc::test { template class TestPerfTask : public ppc::task::Task { public: - explicit TestPerfTask(const InType& in) { + explicit TestPerfTask(const InType &in) { this->GetInput() = in; } @@ -53,7 +53,7 @@ class TestPerfTask : public ppc::task::Task { template class FakePerfTask : public TestPerfTask { public: - explicit FakePerfTask(const InType& in) : TestPerfTask(in) {} + explicit FakePerfTask(const InType &in) : TestPerfTask(in) {} bool RunImpl() override { std::this_thread::sleep_for(std::chrono::seconds(11)); @@ -164,7 +164,7 @@ TEST(PerfTests, CheckPerfTaskFloat) { struct ParamTestCase { PerfResults::TypeOfRunning input; std::string expected_output; - friend void PrintTo(const ParamTestCase& param, std::ostream* os) { + friend void PrintTo(const ParamTestCase ¶m, std::ostream *os) { *os << "{ input = " << static_cast(param.input) << ", expected = " << param.expected_output << " }"; } }; @@ -172,7 +172,7 @@ struct ParamTestCase { class GetStringParamNameParamTest : public ::testing::TestWithParam {}; TEST_P(GetStringParamNameParamTest, ReturnsExpectedString) { - const auto& param = GetParam(); + const auto ¶m = GetParam(); EXPECT_EQ(GetStringParamName(param.input), param.expected_output); } @@ -180,7 +180,7 @@ INSTANTIATE_TEST_SUITE_P(ParamTests, GetStringParamNameParamTest, ::testing::Values(ParamTestCase{PerfResults::TypeOfRunning::kTaskRun, "task_run"}, ParamTestCase{PerfResults::TypeOfRunning::kPipeline, "pipeline"}, ParamTestCase{PerfResults::TypeOfRunning::kNone, "none"}), - [](const ::testing::TestParamInfo& info) { + [](const ::testing::TestParamInfo &info) { return info.param.expected_output; }); @@ -188,7 +188,7 @@ struct TaskTypeTestCase { TypeOfTask type; std::string expected; std::string label; - friend void PrintTo(const TaskTypeTestCase& param, std::ostream* os) { + friend void PrintTo(const TaskTypeTestCase ¶m, std::ostream *os) { *os << "{ type = " << static_cast(param.type) << ", expected = " << param.expected << ", label = " << param.label << " }"; } @@ -217,7 +217,7 @@ class GetStringTaskTypeTest : public ::testing::TestWithParam }; TEST_P(GetStringTaskTypeTest, ReturnsExpectedString) { - const auto& param = GetParam(); + const auto ¶m = GetParam(); EXPECT_EQ(GetStringTaskType(param.type, temp_path), param.expected) << "Failed on: " << param.label; } @@ -236,7 +236,7 @@ TEST(GetStringTaskTypeStandaloneTest, ThrowsIfFileMissing) { TEST(GetStringTaskTypeStandaloneTest, ExceptionMessageContainsPath) { const std::string missing_path = "non_existent_settings.json"; - EXPECT_THROW(try { GetStringTaskType(TypeOfTask::kSEQ, missing_path); } catch (const std::runtime_error& e) { + EXPECT_THROW(try { GetStringTaskType(TypeOfTask::kSEQ, missing_path); } catch (const std::runtime_error &e) { EXPECT_NE(std::string(e.what()).find(missing_path), std::string::npos); throw; }, diff --git a/modules/runners/include/runners.hpp b/modules/runners/include/runners.hpp index 56a46f1e0..b957e5323 100644 --- a/modules/runners/include/runners.hpp +++ b/modules/runners/include/runners.hpp @@ -13,7 +13,7 @@ class UnreadMessagesDetector : public ::testing::EmptyTestEventListener { public: UnreadMessagesDetector() = default; /// @brief Called by GTest after a test ends. Checks for unread messages. - void OnTestEnd(const ::testing::TestInfo& /*test_info*/) override; + void OnTestEnd(const ::testing::TestInfo & /*test_info*/) override; private: }; @@ -26,9 +26,9 @@ class WorkerTestFailurePrinter : public ::testing::EmptyTestEventListener { /// @param base A shared pointer to another GTest event listener. explicit WorkerTestFailurePrinter(std::shared_ptr<::testing::TestEventListener> base) : base_(std::move(base)) {} /// @brief Called after a test ends. Passes call base listener and print failures with rank. - void OnTestEnd(const ::testing::TestInfo& test_info) override; + void OnTestEnd(const ::testing::TestInfo &test_info) override; /// @brief Called when a test part fails. Prints MPI rank info along with the failure. - void OnTestPartResult(const ::testing::TestPartResult& test_part_result) override; + void OnTestPartResult(const ::testing::TestPartResult &test_part_result) override; private: /// @brief Prints the MPI rank of the current process to stderr. @@ -41,12 +41,12 @@ class WorkerTestFailurePrinter : public ::testing::EmptyTestEventListener { /// @param argv Argument vector. /// @return Exit code from RUN_ALL_TESTS or MPI error code if initialization/ /// finalization fails. -int Init(int argc, char** argv); +int Init(int argc, char **argv); /// @brief Initializes the testing environment only for gtest. /// @param argc Argument count. /// @param argv Argument vector. /// @return Exit code from RUN_ALL_TESTS. -int SimpleInit(int argc, char** argv); +int SimpleInit(int argc, char **argv); } // namespace ppc::runners diff --git a/modules/runners/src/runners.cpp b/modules/runners/src/runners.cpp index 0925349f5..e13852874 100644 --- a/modules/runners/src/runners.cpp +++ b/modules/runners/src/runners.cpp @@ -15,7 +15,7 @@ namespace ppc::runners { -void UnreadMessagesDetector::OnTestEnd(const ::testing::TestInfo& /*test_info*/) { +void UnreadMessagesDetector::OnTestEnd(const ::testing::TestInfo & /*test_info*/) { int rank = -1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); @@ -42,7 +42,7 @@ void UnreadMessagesDetector::OnTestEnd(const ::testing::TestInfo& /*test_info*/) MPI_Barrier(MPI_COMM_WORLD); } -void WorkerTestFailurePrinter::OnTestEnd(const ::testing::TestInfo& test_info) { +void WorkerTestFailurePrinter::OnTestEnd(const ::testing::TestInfo &test_info) { if (test_info.result()->Passed()) { return; } @@ -50,7 +50,7 @@ void WorkerTestFailurePrinter::OnTestEnd(const ::testing::TestInfo& test_info) { base_->OnTestEnd(test_info); } -void WorkerTestFailurePrinter::OnTestPartResult(const ::testing::TestPartResult& test_part_result) { +void WorkerTestFailurePrinter::OnTestPartResult(const ::testing::TestPartResult &test_part_result) { if (test_part_result.passed() || test_part_result.skipped()) { return; } @@ -75,7 +75,7 @@ int RunAllTests() { } } // namespace -int Init(int argc, char** argv) { +int Init(int argc, char **argv) { const int init_res = MPI_Init(&argc, &argv); if (init_res != MPI_SUCCESS) { std::cerr << std::format("[ ERROR ] MPI_Init failed with code {}", init_res) << '\n'; @@ -88,11 +88,11 @@ int Init(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - auto& listeners = ::testing::UnitTest::GetInstance()->listeners(); + auto &listeners = ::testing::UnitTest::GetInstance()->listeners(); int rank = -1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank != 0 && (argc < 2 || argv[1] != std::string("--print-workers"))) { - auto* listener = listeners.Release(listeners.default_result_printer()); + auto *listener = listeners.Release(listeners.default_result_printer()); listeners.Append(new WorkerTestFailurePrinter(std::shared_ptr<::testing::TestEventListener>(listener))); } listeners.Append(new UnreadMessagesDetector()); @@ -108,7 +108,7 @@ int Init(int argc, char** argv) { return status; } -int SimpleInit(int argc, char** argv) { +int SimpleInit(int argc, char **argv) { // Limit the number of threads in TBB tbb::global_control control(tbb::global_control::max_allowed_parallelism, ppc::util::GetNumThreads()); diff --git a/modules/task/tests/task_tests.cpp b/modules/task/tests/task_tests.cpp index 99ffc9175..f7170e745 100644 --- a/modules/task/tests/task_tests.cpp +++ b/modules/task/tests/task_tests.cpp @@ -41,7 +41,7 @@ namespace ppc::test { template class TestTask : public ppc::task::Task { public: - explicit TestTask(const InType& in) { + explicit TestTask(const InType &in) { this->GetInput() = in; } @@ -69,7 +69,7 @@ class TestTask : public ppc::task::Task { template class FakeSlowTask : public TestTask { public: - explicit FakeSlowTask(const InType& in) : TestTask(in) {} + explicit FakeSlowTask(const InType &in) : TestTask(in) {} bool RunImpl() override { std::this_thread::sleep_for(std::chrono::seconds(2)); @@ -230,7 +230,7 @@ TEST(TaskTest, TaskDestructorThrowsIfStageIncomplete) { { std::vector in(20, 1); struct LocalTask : Task, int32_t> { - explicit LocalTask(const std::vector& in) { + explicit LocalTask(const std::vector &in) { this->GetInput() = in; } bool ValidationImpl() override { @@ -256,7 +256,7 @@ TEST(TaskTest, TaskDestructorThrowsIfEmpty) { { std::vector in(20, 1); struct LocalTask : Task, int32_t> { - explicit LocalTask(const std::vector& in) { + explicit LocalTask(const std::vector &in) { this->GetInput() = in; } bool ValidationImpl() override { @@ -279,7 +279,7 @@ TEST(TaskTest, TaskDestructorThrowsIfEmpty) { TEST(TaskTest, InternalTimeTestThrowsIfTimeoutExceeded) { struct SlowTask : Task, int32_t> { - explicit SlowTask(const std::vector& in) { + explicit SlowTask(const std::vector &in) { this->GetInput() = in; } bool ValidationImpl() override { @@ -346,6 +346,6 @@ TEST(TaskTest, PostProcessingThrowsIfCalledBeforeRun) { EXPECT_THROW(task->PostProcessing(), std::runtime_error); } -int main(int argc, char** argv) { +int main(int argc, char **argv) { return ppc::runners::SimpleInit(argc, argv); } diff --git a/modules/util/include/func_test_util.hpp b/modules/util/include/func_test_util.hpp index 1aac8d67f..9a4e71327 100644 --- a/modules/util/include/func_test_util.hpp +++ b/modules/util/include/func_test_util.hpp @@ -36,7 +36,7 @@ template /// @tparam TestType Type of the test case or parameter. class BaseRunFuncTests : public ::testing::TestWithParam> { public: - virtual bool CheckTestOutputData(OutType& output_data) = 0; + virtual bool CheckTestOutputData(OutType &output_data) = 0; /// @brief Provides input data for the task. /// @return Initialized input data. virtual InType GetTestInputData() = 0; @@ -48,7 +48,7 @@ class BaseRunFuncTests : public ::testing::TestWithParam - static std::string PrintFuncTestName(const GTestFuncParam& info) { + static std::string PrintFuncTestName(const GTestFuncParam &info) { RequireStaticInterface(); TestType test_param = std::get(ppc::util::GTestParamIndex::kTestParams)>(info.param); return std::get(GTestParamIndex::kNameTest)>(info.param) + "_" + @@ -57,7 +57,7 @@ class BaseRunFuncTests : public ::testing::TestWithParam test_param) { - const std::string& test_name = std::get(GTestParamIndex::kNameTest)>(test_param); + const std::string &test_name = std::get(GTestParamIndex::kNameTest)>(test_param); ValidateTestName(test_name); @@ -73,16 +73,16 @@ class BaseRunFuncTests : public ::testing::TestWithParam& test_param) { + void InitializeAndRunTask(const FuncTestParam &test_param) { task_ = std::get(GTestParamIndex::kTaskGetter)>(test_param)(GetTestInputData()); ExecuteTaskPipeline(); } @@ -110,18 +110,18 @@ class BaseRunFuncTests : public ::testing::TestWithParam -auto ExpandToValuesImpl(const Tuple& t, std::index_sequence /*unused*/) { +auto ExpandToValuesImpl(const Tuple &t, std::index_sequence /*unused*/) { return ::testing::Values(std::get(t)...); } template -auto ExpandToValues(const Tuple& t) { +auto ExpandToValues(const Tuple &t) { constexpr std::size_t kN = std::tuple_size_v; return ExpandToValuesImpl(t, std::make_index_sequence{}); } template -auto GenTaskTuplesImpl(const SizesContainer& sizes, const std::string& settings_path, +auto GenTaskTuplesImpl(const SizesContainer &sizes, const std::string &settings_path, std::index_sequence /*unused*/) { return std::make_tuple(std::make_tuple(ppc::task::TaskGetter, std::string(GetNamespace()) + "_" + @@ -130,13 +130,13 @@ auto GenTaskTuplesImpl(const SizesContainer& sizes, const std::string& settings_ } template -auto TaskListGenerator(const SizesContainer& sizes, const std::string& settings_path) { +auto TaskListGenerator(const SizesContainer &sizes, const std::string &settings_path) { return GenTaskTuplesImpl(sizes, settings_path, std::make_index_sequence>>{}); } template -constexpr auto AddFuncTask(const SizesContainer& sizes, const std::string& settings_path) { +constexpr auto AddFuncTask(const SizesContainer &sizes, const std::string &settings_path) { return TaskListGenerator(sizes, settings_path); } diff --git a/modules/util/include/perf_test_util.hpp b/modules/util/include/perf_test_util.hpp index 3c4abec75..827f501d7 100644 --- a/modules/util/include/perf_test_util.hpp +++ b/modules/util/include/perf_test_util.hpp @@ -34,18 +34,18 @@ template class BaseRunPerfTests : public ::testing::TestWithParam> { public: /// @brief Generates a readable name for the performance test case. - static std::string CustomPerfTestName(const ::testing::TestParamInfo>& info) { + static std::string CustomPerfTestName(const ::testing::TestParamInfo> &info) { return ppc::performance::GetStringParamName( std::get(GTestParamIndex::kTestParams)>(info.param)) + "_" + std::get(GTestParamIndex::kNameTest)>(info.param); } protected: - virtual bool CheckTestOutputData(OutType& output_data) = 0; + virtual bool CheckTestOutputData(OutType &output_data) = 0; /// @brief Supplies input data for performance testing. virtual InType GetTestInputData() = 0; - virtual void SetPerfAttributes(ppc::performance::PerfAttr& perf_attrs) { + virtual void SetPerfAttributes(ppc::performance::PerfAttr &perf_attrs) { if (task_->GetDynamicTypeOfTask() == ppc::task::TypeOfTask::kMPI || task_->GetDynamicTypeOfTask() == ppc::task::TypeOfTask::kALL) { const double t0 = GetTimeMPI(); @@ -67,7 +67,7 @@ class BaseRunPerfTests : public ::testing::TestWithParam& perf_test_param) { + void ExecuteTest(const PerfTestParam &perf_test_param) { auto task_getter = std::get(GTestParamIndex::kTaskGetter)>(perf_test_param); auto test_name = std::get(GTestParamIndex::kNameTest)>(perf_test_param); auto mode = std::get(GTestParamIndex::kTestParams)>(perf_test_param); @@ -105,7 +105,7 @@ class BaseRunPerfTests : public ::testing::TestWithParam -auto MakePerfTaskTuples(const std::string& settings_path) { +auto MakePerfTaskTuples(const std::string &settings_path) { const auto name = std::string(GetNamespace()) + "_" + ppc::task::GetStringTaskType(TaskType::GetStaticTypeOfTask(), settings_path); @@ -116,18 +116,18 @@ auto MakePerfTaskTuples(const std::string& settings_path) { } template -auto TupleToGTestValuesImpl(const Tuple& tup, std::index_sequence /*unused*/) { +auto TupleToGTestValuesImpl(const Tuple &tup, std::index_sequence /*unused*/) { return ::testing::Values(std::get(tup)...); } template -auto TupleToGTestValues(Tuple&& tup) { +auto TupleToGTestValues(Tuple &&tup) { constexpr size_t kSize = std::tuple_size_v>; return TupleToGTestValuesImpl(std::forward(tup), std::make_index_sequence{}); } template -auto MakeAllPerfTasks(const std::string& settings_path) { +auto MakeAllPerfTasks(const std::string &settings_path) { return std::tuple_cat(MakePerfTaskTuples(settings_path)...); } diff --git a/modules/util/include/util.hpp b/modules/util/include/util.hpp index a69d7afbe..362c45882 100644 --- a/modules/util/include/util.hpp +++ b/modules/util/include/util.hpp @@ -55,7 +55,7 @@ class DestructorFailureFlag { enum class GTestParamIndex : uint8_t { kTaskGetter, kNameTest, kTestParams }; -std::string GetAbsoluteTaskPath(const std::string& id_path, const std::string& relative_path); +std::string GetAbsoluteTaskPath(const std::string &id_path, const std::string &relative_path); int GetNumThreads(); int GetNumProc(); double GetTaskMaxTime(); @@ -66,13 +66,13 @@ std::string GetNamespace() { std::string name = typeid(T).name(); #ifdef __GNUC__ int status = 0; - std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), - std::free}; + std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + std::free}; name = (status == 0) ? demangled.get() : name; #endif #ifdef _MSC_VER const std::string prefixes[] = {"class ", "struct ", "enum ", "union "}; - for (const auto& prefix : prefixes) { + for (const auto &prefix : prefixes) { if (name.starts_with(prefix)) { name = name.substr(prefix.size()); break; diff --git a/modules/util/src/util.cpp b/modules/util/src/util.cpp index 97a777281..34c065388 100644 --- a/modules/util/src/util.cpp +++ b/modules/util/src/util.cpp @@ -8,14 +8,14 @@ namespace { -std::string GetAbsolutePath(const std::string& relative_path) { +std::string GetAbsolutePath(const std::string &relative_path) { std::filesystem::path path = std::filesystem::path(PPC_PATH_TO_PROJECT) / "tasks" / relative_path; return path.string(); } } // namespace -std::string ppc::util::GetAbsoluteTaskPath(const std::string& id_path, const std::string& relative_path) { +std::string ppc::util::GetAbsoluteTaskPath(const std::string &id_path, const std::string &relative_path) { std::filesystem::path task_relative = std::filesystem::path(id_path) / "data" / relative_path; return GetAbsolutePath(task_relative.string()); } @@ -60,7 +60,7 @@ constexpr std::array kMpiEnvVars = { "HYDRA_CONTROL_FD", "PMIX_RANK", "SLURM_PROCID", "MSMPI_RANK", "MSMPI_LOCALRANK"}; bool ppc::util::IsUnderMpirun() { - return std::ranges::any_of(kMpiEnvVars, [&](const auto& env_var) { + return std::ranges::any_of(kMpiEnvVars, [&](const auto &env_var) { const auto mpi_env = env::get(env_var); return static_cast(mpi_env.has_value()); }); diff --git a/tasks/common/runners/functional.cpp b/tasks/common/runners/functional.cpp index 7eb563375..c32e6e9d1 100644 --- a/tasks/common/runners/functional.cpp +++ b/tasks/common/runners/functional.cpp @@ -4,7 +4,7 @@ #include "runners/include/runners.hpp" #include "util/include/util.hpp" -int main(int argc, char** argv) { +int main(int argc, char **argv) { if (ppc::util::IsUnderMpirun()) { return ppc::runners::Init(argc, argv); } diff --git a/tasks/common/runners/performance.cpp b/tasks/common/runners/performance.cpp index 3fd54c746..a4b6c0e2f 100644 --- a/tasks/common/runners/performance.cpp +++ b/tasks/common/runners/performance.cpp @@ -1,5 +1,5 @@ #include "runners/include/runners.hpp" -int main(int argc, char** argv) { +int main(int argc, char **argv) { return ppc::runners::Init(argc, argv); } diff --git a/tasks/example_processes/seq/include/ops_seq.hpp b/tasks/example_processes/seq/include/ops_seq.hpp index 6a1cdd4ce..f264b4fd7 100644 --- a/tasks/example_processes/seq/include/ops_seq.hpp +++ b/tasks/example_processes/seq/include/ops_seq.hpp @@ -10,7 +10,7 @@ class NesterovATestTaskSEQ : public BaseTask { static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { return ppc::task::TypeOfTask::kSEQ; } - explicit NesterovATestTaskSEQ(const InType& in); + explicit NesterovATestTaskSEQ(const InType &in); private: bool ValidationImpl() override; diff --git a/tasks/example_processes/seq/src/ops_seq.cpp b/tasks/example_processes/seq/src/ops_seq.cpp index 9b0b7b582..2599c518e 100644 --- a/tasks/example_processes/seq/src/ops_seq.cpp +++ b/tasks/example_processes/seq/src/ops_seq.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { -NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType& in) { +NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; GetOutput() = 0; diff --git a/tasks/example_processes/tests/performance/main.cpp b/tasks/example_processes/tests/performance/main.cpp index 90580c2d4..e5c942e2e 100644 --- a/tasks/example_processes/tests/performance/main.cpp +++ b/tasks/example_processes/tests/performance/main.cpp @@ -15,7 +15,7 @@ class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { +class NesterovARunFuncTestsProcesses2 : public ppc::util::BaseRunFuncTests { public: static std::string PrintTestParam(const TestType &test_param) { return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); @@ -64,7 +64,7 @@ class NesterovARunFuncTestsProcesses : public ppc::util::BaseRunFuncTests; +const auto kPerfTestName = NesterovARunFuncTestsProcesses2::PrintFuncTestName; -INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses, kGtestValues, kPerfTestName); +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses2, kGtestValues, kPerfTestName); } // namespace diff --git a/tasks/example_processes_2/tests/performance/main.cpp b/tasks/example_processes_2/tests/performance/main.cpp index e5c942e2e..5e495d072 100644 --- a/tasks/example_processes_2/tests/performance/main.cpp +++ b/tasks/example_processes_2/tests/performance/main.cpp @@ -7,7 +7,7 @@ namespace nesterov_a_test_task_processes { -class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { +class ExampleRunPerfTestProcesses2 : public ppc::util::BaseRunPerfTests { const int kCount_ = 50; InType input_data_{}; @@ -24,7 +24,7 @@ class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { +class NesterovARunFuncTestsProcesses3 : public ppc::util::BaseRunFuncTests { public: static std::string PrintTestParam(const TestType &test_param) { return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); @@ -64,7 +64,7 @@ class NesterovARunFuncTestsProcesses : public ppc::util::BaseRunFuncTests; +const auto kPerfTestName = NesterovARunFuncTestsProcesses3::PrintFuncTestName; -INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses, kGtestValues, kPerfTestName); +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses3, kGtestValues, kPerfTestName); } // namespace diff --git a/tasks/example_processes_3/tests/performance/main.cpp b/tasks/example_processes_3/tests/performance/main.cpp index e5c942e2e..b900e6509 100644 --- a/tasks/example_processes_3/tests/performance/main.cpp +++ b/tasks/example_processes_3/tests/performance/main.cpp @@ -7,7 +7,7 @@ namespace nesterov_a_test_task_processes { -class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { +class ExampleRunPerfTestProcesses3 : public ppc::util::BaseRunPerfTests { const int kCount_ = 50; InType input_data_{}; @@ -24,7 +24,7 @@ class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests Date: Fri, 10 Oct 2025 00:08:26 +0200 Subject: [PATCH 17/18] increase performance test iterations to 100; adjust threshold for performance time validation to 0.001 seconds --- scripts/create_perf_table.py | 8 ++++---- tasks/example_processes/tests/performance/main.cpp | 2 +- tasks/example_processes_2/tests/performance/main.cpp | 2 +- tasks/example_processes_3/tests/performance/main.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/create_perf_table.py b/scripts/create_perf_table.py index 92fd0b6a3..0e5a67f60 100644 --- a/scripts/create_perf_table.py +++ b/scripts/create_perf_table.py @@ -74,8 +74,8 @@ task_name = old_result[0][1] perf_type = old_result[0][2] perf_time = float(old_result[0][3]) - if perf_time < 0.1: - msg = f"Performance time = {perf_time} < 0.1 second : for {task_type} - {task_name} - {perf_type} \n" + if perf_time < 0.001: + msg = f"Performance time = {perf_time} < 0.001 second : for {task_type} - {task_name} - {perf_type} \n" raise Exception(msg) result_tables[perf_type][task_name][task_type] = perf_time elif len(new_result): @@ -87,8 +87,8 @@ perf_time = float(new_result[0][4]) task_name = f"example_{task_category}" - if perf_time < 0.1: - msg = f"Performance time = {perf_time} < 0.1 second : for {task_type} - {task_name} - {perf_type} \n" + if perf_time < 0.001: + msg = f"Performance time = {perf_time} < 0.001 second : for {task_type} - {task_name} - {perf_type} \n" raise Exception(msg) if task_name in result_tables[perf_type]: diff --git a/tasks/example_processes/tests/performance/main.cpp b/tasks/example_processes/tests/performance/main.cpp index e5c942e2e..5d9a4c712 100644 --- a/tasks/example_processes/tests/performance/main.cpp +++ b/tasks/example_processes/tests/performance/main.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { class ExampleRunPerfTestProcesses : public ppc::util::BaseRunPerfTests { - const int kCount_ = 50; + const int kCount_ = 100; InType input_data_{}; void SetUp() override { diff --git a/tasks/example_processes_2/tests/performance/main.cpp b/tasks/example_processes_2/tests/performance/main.cpp index 5e495d072..5852490d7 100644 --- a/tasks/example_processes_2/tests/performance/main.cpp +++ b/tasks/example_processes_2/tests/performance/main.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { class ExampleRunPerfTestProcesses2 : public ppc::util::BaseRunPerfTests { - const int kCount_ = 50; + const int kCount_ = 100; InType input_data_{}; void SetUp() override { diff --git a/tasks/example_processes_3/tests/performance/main.cpp b/tasks/example_processes_3/tests/performance/main.cpp index b900e6509..26cb70ca3 100644 --- a/tasks/example_processes_3/tests/performance/main.cpp +++ b/tasks/example_processes_3/tests/performance/main.cpp @@ -8,7 +8,7 @@ namespace nesterov_a_test_task_processes { class ExampleRunPerfTestProcesses3 : public ppc::util::BaseRunPerfTests { - const int kCount_ = 50; + const int kCount_ = 100; InType input_data_{}; void SetUp() override { From f5fc04d63c854448de7cd648f53f376d5e9993be Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Fri, 10 Oct 2025 12:02:14 +0200 Subject: [PATCH 18/18] update namespace and file paths for `example_processes` to version-specific directories; no functional changes --- .../common/include/common.hpp | 4 ++-- .../example_processes_2/mpi/include/ops_mpi.hpp | 6 +++--- tasks/example_processes_2/mpi/src/ops_mpi.cpp | 8 ++++---- .../example_processes_2/seq/include/ops_seq.hpp | 6 +++--- tasks/example_processes_2/seq/src/ops_seq.cpp | 8 ++++---- .../tests/functional/main.cpp | 16 ++++++++-------- .../tests/performance/main.cpp | 12 ++++++------ .../common/include/common.hpp | 4 ++-- .../example_processes_3/mpi/include/ops_mpi.hpp | 6 +++--- tasks/example_processes_3/mpi/src/ops_mpi.cpp | 8 ++++---- .../example_processes_3/seq/include/ops_seq.hpp | 6 +++--- tasks/example_processes_3/seq/src/ops_seq.cpp | 8 ++++---- .../tests/functional/main.cpp | 16 ++++++++-------- .../tests/performance/main.cpp | 12 ++++++------ 14 files changed, 60 insertions(+), 60 deletions(-) diff --git a/tasks/example_processes_2/common/include/common.hpp b/tasks/example_processes_2/common/include/common.hpp index 127d96c4c..145054df0 100644 --- a/tasks/example_processes_2/common/include/common.hpp +++ b/tasks/example_processes_2/common/include/common.hpp @@ -5,11 +5,11 @@ #include "task/include/task.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { using InType = int; using OutType = int; using TestType = std::tuple; using BaseTask = ppc::task::Task; -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_2/mpi/include/ops_mpi.hpp b/tasks/example_processes_2/mpi/include/ops_mpi.hpp index 02b612af1..febfb30ee 100644 --- a/tasks/example_processes_2/mpi/include/ops_mpi.hpp +++ b/tasks/example_processes_2/mpi/include/ops_mpi.hpp @@ -1,9 +1,9 @@ #pragma once -#include "example_processes/common/include/common.hpp" +#include "example_processes_2/common/include/common.hpp" #include "task/include/task.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { class NesterovATestTaskMPI : public BaseTask { public: @@ -19,4 +19,4 @@ class NesterovATestTaskMPI : public BaseTask { bool PostProcessingImpl() override; }; -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_2/mpi/src/ops_mpi.cpp b/tasks/example_processes_2/mpi/src/ops_mpi.cpp index 84cafe1f5..d3570f350 100644 --- a/tasks/example_processes_2/mpi/src/ops_mpi.cpp +++ b/tasks/example_processes_2/mpi/src/ops_mpi.cpp @@ -1,14 +1,14 @@ -#include "example_processes/mpi/include/ops_mpi.hpp" +#include "example_processes_2/mpi/include/ops_mpi.hpp" #include #include #include -#include "example_processes/common/include/common.hpp" +#include "example_processes_2/common/include/common.hpp" #include "util/include/util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { NesterovATestTaskMPI::NesterovATestTaskMPI(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); @@ -69,4 +69,4 @@ bool NesterovATestTaskMPI::PostProcessingImpl() { return GetOutput() > 0; } -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_2/seq/include/ops_seq.hpp b/tasks/example_processes_2/seq/include/ops_seq.hpp index f264b4fd7..ac1ad6944 100644 --- a/tasks/example_processes_2/seq/include/ops_seq.hpp +++ b/tasks/example_processes_2/seq/include/ops_seq.hpp @@ -1,9 +1,9 @@ #pragma once -#include "example_processes/common/include/common.hpp" +#include "example_processes_2/common/include/common.hpp" #include "task/include/task.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { class NesterovATestTaskSEQ : public BaseTask { public: @@ -19,4 +19,4 @@ class NesterovATestTaskSEQ : public BaseTask { bool PostProcessingImpl() override; }; -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_2/seq/src/ops_seq.cpp b/tasks/example_processes_2/seq/src/ops_seq.cpp index 2599c518e..ea4a0c629 100644 --- a/tasks/example_processes_2/seq/src/ops_seq.cpp +++ b/tasks/example_processes_2/seq/src/ops_seq.cpp @@ -1,12 +1,12 @@ -#include "example_processes/seq/include/ops_seq.hpp" +#include "example_processes_2/seq/include/ops_seq.hpp" #include #include -#include "example_processes/common/include/common.hpp" +#include "example_processes_2/common/include/common.hpp" #include "util/include/util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); @@ -57,4 +57,4 @@ bool NesterovATestTaskSEQ::PostProcessingImpl() { return GetOutput() > 0; } -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_2/tests/functional/main.cpp b/tasks/example_processes_2/tests/functional/main.cpp index 2709f91ba..ff3442fda 100644 --- a/tasks/example_processes_2/tests/functional/main.cpp +++ b/tasks/example_processes_2/tests/functional/main.cpp @@ -12,13 +12,13 @@ #include #include -#include "example_processes/common/include/common.hpp" -#include "example_processes/mpi/include/ops_mpi.hpp" -#include "example_processes/seq/include/ops_seq.hpp" +#include "example_processes_2/common/include/common.hpp" +#include "example_processes_2/mpi/include/ops_mpi.hpp" +#include "example_processes_2/seq/include/ops_seq.hpp" #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { class NesterovARunFuncTestsProcesses2 : public ppc::util::BaseRunFuncTests { public: @@ -34,7 +34,7 @@ class NesterovARunFuncTestsProcesses2 : public ppc::util::BaseRunFuncTests img; // Read image { - std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example_processes, "pic.jpg"); + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example_processes_2, "pic.jpg"); auto *data = stbi_load(abs_path.c_str(), &width, &height, &channels, 0); if (data == nullptr) { throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason())); @@ -71,8 +71,8 @@ TEST_P(NesterovARunFuncTestsProcesses2, MatmulFromPic) { const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; const auto kTestTasksList = - std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes), - ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes)); + std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes_2), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes_2)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); @@ -82,4 +82,4 @@ INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses2, kGtest } // namespace -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_2/tests/performance/main.cpp b/tasks/example_processes_2/tests/performance/main.cpp index 5852490d7..23036173c 100644 --- a/tasks/example_processes_2/tests/performance/main.cpp +++ b/tasks/example_processes_2/tests/performance/main.cpp @@ -1,11 +1,11 @@ #include -#include "example_processes/common/include/common.hpp" -#include "example_processes/mpi/include/ops_mpi.hpp" -#include "example_processes/seq/include/ops_seq.hpp" +#include "example_processes_2/common/include/common.hpp" +#include "example_processes_2/mpi/include/ops_mpi.hpp" +#include "example_processes_2/seq/include/ops_seq.hpp" #include "util/include/perf_test_util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_2 { class ExampleRunPerfTestProcesses2 : public ppc::util::BaseRunPerfTests { const int kCount_ = 100; @@ -29,7 +29,7 @@ TEST_P(ExampleRunPerfTestProcesses2, RunPerfModes) { } const auto kAllPerfTasks = - ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example_processes); + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example_processes_2); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); @@ -37,4 +37,4 @@ const auto kPerfTestName = ExampleRunPerfTestProcesses2::CustomPerfTestName; INSTANTIATE_TEST_SUITE_P(RunModeTests, ExampleRunPerfTestProcesses2, kGtestValues, kPerfTestName); -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_2 diff --git a/tasks/example_processes_3/common/include/common.hpp b/tasks/example_processes_3/common/include/common.hpp index 127d96c4c..ac343ab8f 100644 --- a/tasks/example_processes_3/common/include/common.hpp +++ b/tasks/example_processes_3/common/include/common.hpp @@ -5,11 +5,11 @@ #include "task/include/task.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { using InType = int; using OutType = int; using TestType = std::tuple; using BaseTask = ppc::task::Task; -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3 diff --git a/tasks/example_processes_3/mpi/include/ops_mpi.hpp b/tasks/example_processes_3/mpi/include/ops_mpi.hpp index 02b612af1..080e96585 100644 --- a/tasks/example_processes_3/mpi/include/ops_mpi.hpp +++ b/tasks/example_processes_3/mpi/include/ops_mpi.hpp @@ -1,9 +1,9 @@ #pragma once -#include "example_processes/common/include/common.hpp" +#include "example_processes_3/common/include/common.hpp" #include "task/include/task.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { class NesterovATestTaskMPI : public BaseTask { public: @@ -19,4 +19,4 @@ class NesterovATestTaskMPI : public BaseTask { bool PostProcessingImpl() override; }; -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3 diff --git a/tasks/example_processes_3/mpi/src/ops_mpi.cpp b/tasks/example_processes_3/mpi/src/ops_mpi.cpp index 84cafe1f5..7cef9dbb6 100644 --- a/tasks/example_processes_3/mpi/src/ops_mpi.cpp +++ b/tasks/example_processes_3/mpi/src/ops_mpi.cpp @@ -1,14 +1,14 @@ -#include "example_processes/mpi/include/ops_mpi.hpp" +#include "example_processes_3/mpi/include/ops_mpi.hpp" #include #include #include -#include "example_processes/common/include/common.hpp" +#include "example_processes_3/common/include/common.hpp" #include "util/include/util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { NesterovATestTaskMPI::NesterovATestTaskMPI(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); @@ -69,4 +69,4 @@ bool NesterovATestTaskMPI::PostProcessingImpl() { return GetOutput() > 0; } -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3 diff --git a/tasks/example_processes_3/seq/include/ops_seq.hpp b/tasks/example_processes_3/seq/include/ops_seq.hpp index f264b4fd7..5a7b33677 100644 --- a/tasks/example_processes_3/seq/include/ops_seq.hpp +++ b/tasks/example_processes_3/seq/include/ops_seq.hpp @@ -1,9 +1,9 @@ #pragma once -#include "example_processes/common/include/common.hpp" +#include "example_processes_3/common/include/common.hpp" #include "task/include/task.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { class NesterovATestTaskSEQ : public BaseTask { public: @@ -19,4 +19,4 @@ class NesterovATestTaskSEQ : public BaseTask { bool PostProcessingImpl() override; }; -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3 diff --git a/tasks/example_processes_3/seq/src/ops_seq.cpp b/tasks/example_processes_3/seq/src/ops_seq.cpp index 2599c518e..1db5c7340 100644 --- a/tasks/example_processes_3/seq/src/ops_seq.cpp +++ b/tasks/example_processes_3/seq/src/ops_seq.cpp @@ -1,12 +1,12 @@ -#include "example_processes/seq/include/ops_seq.hpp" +#include "example_processes_3/seq/include/ops_seq.hpp" #include #include -#include "example_processes/common/include/common.hpp" +#include "example_processes_3/common/include/common.hpp" #include "util/include/util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { NesterovATestTaskSEQ::NesterovATestTaskSEQ(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); @@ -57,4 +57,4 @@ bool NesterovATestTaskSEQ::PostProcessingImpl() { return GetOutput() > 0; } -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3 diff --git a/tasks/example_processes_3/tests/functional/main.cpp b/tasks/example_processes_3/tests/functional/main.cpp index a42a7ab80..99e5a5b15 100644 --- a/tasks/example_processes_3/tests/functional/main.cpp +++ b/tasks/example_processes_3/tests/functional/main.cpp @@ -12,13 +12,13 @@ #include #include -#include "example_processes/common/include/common.hpp" -#include "example_processes/mpi/include/ops_mpi.hpp" -#include "example_processes/seq/include/ops_seq.hpp" +#include "example_processes_3/common/include/common.hpp" +#include "example_processes_3/mpi/include/ops_mpi.hpp" +#include "example_processes_3/seq/include/ops_seq.hpp" #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { class NesterovARunFuncTestsProcesses3 : public ppc::util::BaseRunFuncTests { public: @@ -34,7 +34,7 @@ class NesterovARunFuncTestsProcesses3 : public ppc::util::BaseRunFuncTests img; // Read image { - std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example_processes, "pic.jpg"); + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_example_processes_3, "pic.jpg"); auto *data = stbi_load(abs_path.c_str(), &width, &height, &channels, 0); if (data == nullptr) { throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason())); @@ -71,8 +71,8 @@ TEST_P(NesterovARunFuncTestsProcesses3, MatmulFromPic) { const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; const auto kTestTasksList = - std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes), - ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes)); + std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes_3), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_example_processes_3)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); @@ -82,4 +82,4 @@ INSTANTIATE_TEST_SUITE_P(PicMatrixTests, NesterovARunFuncTestsProcesses3, kGtest } // namespace -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3 diff --git a/tasks/example_processes_3/tests/performance/main.cpp b/tasks/example_processes_3/tests/performance/main.cpp index 26cb70ca3..cae4b67d9 100644 --- a/tasks/example_processes_3/tests/performance/main.cpp +++ b/tasks/example_processes_3/tests/performance/main.cpp @@ -1,11 +1,11 @@ #include -#include "example_processes/common/include/common.hpp" -#include "example_processes/mpi/include/ops_mpi.hpp" -#include "example_processes/seq/include/ops_seq.hpp" +#include "example_processes_3/common/include/common.hpp" +#include "example_processes_3/mpi/include/ops_mpi.hpp" +#include "example_processes_3/seq/include/ops_seq.hpp" #include "util/include/perf_test_util.hpp" -namespace nesterov_a_test_task_processes { +namespace nesterov_a_test_task_processes_3 { class ExampleRunPerfTestProcesses3 : public ppc::util::BaseRunPerfTests { const int kCount_ = 100; @@ -29,7 +29,7 @@ TEST_P(ExampleRunPerfTestProcesses3, RunPerfModes) { } const auto kAllPerfTasks = - ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example_processes); + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_example_processes_3); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); @@ -37,4 +37,4 @@ const auto kPerfTestName = ExampleRunPerfTestProcesses3::CustomPerfTestName; INSTANTIATE_TEST_SUITE_P(RunModeTests, ExampleRunPerfTestProcesses3, kGtestValues, kPerfTestName); -} // namespace nesterov_a_test_task_processes +} // namespace nesterov_a_test_task_processes_3
Name V{{ name }} +
+ {{ name }} + {% if deadlines_processes %} + {{ deadlines_processes[loop.index0] }} + {% endif %} +
+
Total
seqmpimpi R
{{ letter }}{{ letter }}{{ cell_seq.solution_points }} {{ cell_seq.deadline_points }} {{ cell_seq.plagiarism_points }}{{ cell_mpi.solution_points }}{{ cell_mpi.a_points if cell_mpi.a_points is defined else 0 }}{{ (cell_mpi.perf_points_display if cell_mpi.perf_points_display is defined else cell_mpi.perf_points) }}{{ cell_mpi.acceleration }}{{ cell_mpi.efficiency }} {{ cell_mpi.deadline_points }} {{ cell_mpi.plagiarism_points }}