diff --git a/discopop_library/ConfigProvider/__main__.py b/discopop_library/ConfigProvider/__main__.py index be19f3402..2bd5c187f 100644 --- a/discopop_library/ConfigProvider/__main__.py +++ b/discopop_library/ConfigProvider/__main__.py @@ -14,18 +14,14 @@ def parse_args() -> ConfigProviderArguments: """Parse the arguments passed to the discopop_config_provider""" parser = ArgumentParser(description="DiscoPoP Config Provider") - # all flags that are not considered stable should be added to the experimental_parser - experimental_parser = parser.add_argument_group( - "EXPERIMENTAL", - "Arguments for experimental features. Experimental arguments may or may not be removed or changed in the future.", - ) # fmt: off - parser.add_argument("-b", "--dp-build-dir", action="store_true", + mutually_exclusive = parser.add_mutually_exclusive_group() + mutually_exclusive.add_argument("-b", "--dp-build-dir", action="store_true", help="Return the path to the DiscoPoP build directory") - parser.add_argument("-s", "--dp-source-dir", action="store_true", + mutually_exclusive.add_argument("-s", "--dp-source-dir", action="store_true", help="Return the path to the DiscoPoP source directory") - parser.add_argument("--llvm-bin-dir", action="store_true", + mutually_exclusive.add_argument("--llvm-bin-dir", action="store_true", help="Return the path to the LLVM bin directory") # EXPERIMENTAL FLAGS: # fmt: on @@ -39,10 +35,10 @@ def parse_args() -> ConfigProviderArguments: ) -def main() -> str: +def main() -> None: arguments = parse_args() retval = run(arguments) - return retval + print(retval) if __name__ == "__main__": diff --git a/discopop_library/ConfigProvider/config_provider.py b/discopop_library/ConfigProvider/config_provider.py index f1e7de7b9..ea7b83ae1 100644 --- a/discopop_library/ConfigProvider/config_provider.py +++ b/discopop_library/ConfigProvider/config_provider.py @@ -6,7 +6,6 @@ # the 3-Clause BSD License. See the LICENSE file in the package base # directory for details. -from typing import cast from discopop_library.ConfigProvider.ConfigProviderArguments import ConfigProviderArguments from discopop_library.ConfigProvider.assets.build_config import DP_BUILD, DP_SOURCE, LLVM_BIN_DIR # type: ignore @@ -15,10 +14,10 @@ def run(arguments: ConfigProviderArguments) -> str: """Returns the contents of the written build_config.txt""" if arguments.return_dp_build_dir: - return cast(str, DP_BUILD) # type: ignore + return DP_BUILD # type: ignore elif arguments.return_dp_source_dir: - return cast(str, DP_SOURCE) # type: ignore + return DP_SOURCE # type: ignore elif arguments.return_llvm_bin_dir: - return cast(str, LLVM_BIN_DIR) # type: ignore + return LLVM_BIN_DIR # type: ignore else: raise ValueError("No valid operation for execution configuration: \n" + str(arguments)) diff --git a/discopop_library/HostpotLoader/hostpot_loader.py b/discopop_library/HostpotLoader/hostpot_loader.py index 768a17efb..3fdf20295 100644 --- a/discopop_library/HostpotLoader/hostpot_loader.py +++ b/discopop_library/HostpotLoader/hostpot_loader.py @@ -29,6 +29,8 @@ def run(arguments: HotspotLoaderArguments) -> Dict[HotspotType, List[Tuple[FILEI if not os.path.exists(os.path.join(os.getcwd(), "hotspot_detection")): return result_dict + if not os.path.exists(os.path.join(os.getcwd(), "hotspot_detection", "Hotspots.json")): + return result_dict with open(os.path.join(os.getcwd(), "hotspot_detection", "Hotspots.json"), "r") as f: hotspots = json.load(f) diff --git a/discopop_library/discopop_optimizer/OptimizerArguments.py b/discopop_library/discopop_optimizer/OptimizerArguments.py index fc4bb01f3..1c36e1899 100644 --- a/discopop_library/discopop_optimizer/OptimizerArguments.py +++ b/discopop_library/discopop_optimizer/OptimizerArguments.py @@ -16,8 +16,6 @@ class OptimizerArguments(object): verbose: bool interactive: bool - exhaustive: bool - evolutionary: Optional[List[str]] doall_microbench_file: str reduction_microbench_file: str allow_nested_parallelism: bool @@ -25,16 +23,11 @@ class OptimizerArguments(object): system_configuration_path: str check_called_function_for_nested_parallelism: bool profiling: bool - greedy: bool - optimization: bool pruning_level: int + optimization_level: int + optimization_level_2_parameters: str def __post_init__(self): - # fix correct optimization method - if not self.exhaustive: - if self.evolutionary == None: - self.greedy = True - self.__validate() def __validate(self): @@ -49,4 +42,9 @@ def __validate(self): # check pruning level values if self.pruning_level not in [0, 1, 2]: raise ValueError("Unsupported pruning level: ", self.pruning_level) + + # check optimization level + if self.optimization_level not in [0, 1, 2, 3]: + raise ValueError("Unknown optimization level requested: ", self.optimization_level) + pass diff --git a/discopop_library/discopop_optimizer/__main__.py b/discopop_library/discopop_optimizer/__main__.py index 7007c0dec..deb199147 100644 --- a/discopop_library/discopop_optimizer/__main__.py +++ b/discopop_library/discopop_optimizer/__main__.py @@ -23,16 +23,12 @@ def parse_args() -> OptimizerArguments: # fmt: off parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output.") - parser.add_argument("-x", "--exhaustive", action="store_true", - help="Enable exhaustive search. By default, a greedy search is performed.") - parser.add_argument("-e", "--evolutionary", type=str, default=None, nargs=2, metavar=("population_size", "generations"), - help="Enable evolutionary search. By default, a greedy search is performed.") - parser.add_argument("-g", "--greedy", action="store_true", - help="Enable greedy search. (Default)") - parser.add_argument("-o", "--optimize", action="store_true", - help="Enable optimization.") parser.add_argument("-p", "--pruning-level", type=int, default=0, help="Program path pruning aggressiveness. 0: no pruning. 1: prune to most likely path. 2: prune to paths that cover 80%% of observed decisions per path split.") + parser.add_argument("-o", type=int, default=0, help="Optimization level: 0 -> no optimization. 1 -> greedy. 2 -> evolutionary. 3 -> exhaustive") + + parser.add_argument("-opt-2-params", type=str, default=None, nargs=2, metavar=("population_size", "generations"), help="Configure parameters of the evolutionary optimization (-o2). Default: 50 5") + parser.add_argument( "--doall-microbench-file", type=str, default="None", help="Do-All microbenchmark results" @@ -65,18 +61,16 @@ def parse_args() -> OptimizerArguments: return OptimizerArguments( verbose=arguments.verbose, interactive=arguments.interactive, - exhaustive=arguments.exhaustive, - evolutionary=arguments.evolutionary, doall_microbench_file=arguments.doall_microbench_file, reduction_microbench_file=arguments.reduction_microbench_file, allow_nested_parallelism=arguments.allow_nested_parallelism, plot=arguments.plot, system_configuration_path=arguments.system_configuration, check_called_function_for_nested_parallelism=arguments.check_called_function_for_nested_parallelism, - profiling=arguments.profiling, - greedy=arguments.greedy, - optimization=arguments.optimize, + profiling=arguments.profiling, pruning_level=arguments.pruning_level, + optimization_level=arguments.o, + optimization_level_2_parameters=arguments.opt_2_params, ) diff --git a/discopop_library/discopop_optimizer/optimization/evolutionary_algorithm.py b/discopop_library/discopop_optimizer/optimization/evolutionary_algorithm.py index 6fe2e780d..65928f487 100644 --- a/discopop_library/discopop_optimizer/optimization/evolutionary_algorithm.py +++ b/discopop_library/discopop_optimizer/optimization/evolutionary_algorithm.py @@ -42,11 +42,11 @@ def perform_evolutionary_search( arguments: OptimizerArguments, optimizer_dir: str, ) -> Optional[OptimizerOutputPattern]: - if arguments.evolutionary is None: - raise ValueError("Invalid arguments for evolutionary search: " + str(arguments.evolutionary)) + if arguments.optimization_level_2_parameters is None: + arguments.optimization_level_2_parameters = ["50", "5"] ### SETTINGS - population_size = int(arguments.evolutionary[0]) - generations = int(arguments.evolutionary[1]) + population_size = int(arguments.optimization_level_2_parameters[0]) + generations = int(arguments.optimization_level_2_parameters[1]) selection_strength = 0.85 # 0.8 --> 80% of the population will be selected for the next generation crossovers = int(population_size / 10) mutations = int(population_size / 10) diff --git a/discopop_library/discopop_optimizer/optimizer.py b/discopop_library/discopop_optimizer/optimizer.py index b185f785f..e96479af5 100644 --- a/discopop_library/discopop_optimizer/optimizer.py +++ b/discopop_library/discopop_optimizer/optimizer.py @@ -199,27 +199,24 @@ def run(arguments: OptimizerArguments): if node_id != node_data.node_id: node_data.node_id = node_id - if arguments.optimization: - # get values for free symbols - initialize_free_symbol_ranges_and_distributions(experiment, arguments, system) + # get values for free symbols + initialize_free_symbol_ranges_and_distributions(experiment, arguments, system) - if arguments.verbose: - print("# SUBSTITUTIONS:") - for key in experiment.substitutions: - print("#", key, " ->", experiment.substitutions[key]) - print() + if arguments.verbose: + print("# SUBSTITUTIONS:") + for key in experiment.substitutions: + print("#", key, " ->", experiment.substitutions[key]) + print() + # apply optimization steps if requested + best_configuration = None + if arguments.optimization_level != 0: # calculate options for easy access available_decisions = get_available_decisions_for_functions(experiment.optimization_graph, arguments) - # calculate costs for all combinations of decisions - if arguments.exhaustive: - best_configuration = evaluate_all_decision_combinations( - experiment, available_decisions, arguments, optimizer_dir - ) - elif arguments.greedy: + if arguments.optimization_level == 1: best_configuration = greedy_search(experiment, available_decisions, arguments, optimizer_dir) - elif arguments.evolutionary != None: + elif arguments.optimization_level == 2: # perform evolutionary search best_configuration = perform_evolutionary_search( experiment, @@ -227,13 +224,18 @@ def run(arguments: OptimizerArguments): arguments, optimizer_dir, ) + # calculate costs for all combinations of decisions + elif arguments.optimization_level == 3: + best_configuration = evaluate_all_decision_combinations( + experiment, available_decisions, arguments, optimizer_dir + ) else: - raise ValueError("No optimization method specified!") + raise ValueError("No valid optimization method specified: " + str(arguments.optimization_level)) - if best_configuration is not None: - best_configuration = optimize_updates(experiment, best_configuration, arguments) - # append the configuration to the list of patterns - experiment.detection_result.patterns.optimizer_output.append(best_configuration) + if best_configuration is not None: + best_configuration = optimize_updates(experiment, best_configuration, arguments) + # append the configuration to the list of patterns + experiment.detection_result.patterns.optimizer_output.append(best_configuration) if arguments.profiling: experiment.profile.disable() # type: ignore diff --git a/discopop_library/global_data/version/VERSION b/discopop_library/global_data/version/VERSION index 94ff29cc4..944880fa1 100644 --- a/discopop_library/global_data/version/VERSION +++ b/discopop_library/global_data/version/VERSION @@ -1 +1 @@ -3.1.1 +3.2.0 diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index dbe7e1060..fdcc6ed6c 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -17,3 +17,20 @@ configure_file(CMAKE_wrapper.sh CMAKE_wrapper.sh COPYONLY) configure_file(MPI_CC_wrapper.sh MPI_CC_wrapper.sh COPYONLY) configure_file(MPI_CXX_wrapper.sh MPI_CXX_wrapper.sh COPYONLY) configure_file(MPI_LINKER_wrapper.sh MPI_LINKER_wrapper.sh COPYONLY) + +# create symlinks to files +set(DP_LOCAL_BIN_DIR "$ENV{HOME}/.local/bin") +if(EXISTS ${DP_LOCAL_BIN_DIR}) + message(STATUS "Creating symlink ${DP_LOCAL_BIN_DIR}/discopop_cc to ${CMAKE_CURRENT_BINARY_DIR}/CC_wrapper.sh") + execute_process(COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/CC_wrapper.sh ${DP_LOCAL_BIN_DIR}/discopop_cc) + + message(STATUS "Creating symlink ${DP_LOCAL_BIN_DIR}/discopop_cxx to ${CMAKE_CURRENT_BINARY_DIR}/CXX_wrapper.sh") + execute_process(COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/CXX_wrapper.sh ${DP_LOCAL_BIN_DIR}/discopop_cxx) + + message(STATUS "Creating symlink ${DP_LOCAL_BIN_DIR}/discopop_cmake to ${CMAKE_CURRENT_BINARY_DIR}/CMAKE_wrapper.sh") + execute_process(COMMAND ln -sf ${CMAKE_CURRENT_BINARY_DIR}/CMAKE_wrapper.sh ${DP_LOCAL_BIN_DIR}/discopop_cmake) +else() + message(WARNING "Creation of symlinks discopop_cc to ${CMAKE_CURRENT_BINARY_DIR}/CC_wrapper.sh not possible. Please create it manually.") + message(WARNING "Creation of symlinks discopop_cxx to ${CMAKE_CURRENT_BINARY_DIR}/CXX_wrapper.sh not possible. Please create it manually.") + message(WARNING "Creation of symlinks discopop_cmake to ${CMAKE_CURRENT_BINARY_DIR}/CMAKE_wrapper.sh not possible. Please create it manually.") +endif() diff --git a/test/optimizer/loop_collapse/negative/simple_1/test.py b/test/optimizer/loop_collapse/negative/simple_1/test.py index d6f6c2e62..f7a314c32 100644 --- a/test/optimizer/loop_collapse/negative/simple_1/test.py +++ b/test/optimizer/loop_collapse/negative/simple_1/test.py @@ -33,7 +33,7 @@ def test(self): # execute DiscoPoP analysis os.chdir(".discopop") os.system("discopop_explorer") - os.system("discopop_optimizer -x") + os.system("discopop_optimizer -o3") os.chdir("..") # validate results self.validate_results(current_dir, src_dir) diff --git a/test/optimizer/loop_collapse/positive/simple_1/test.py b/test/optimizer/loop_collapse/positive/simple_1/test.py index 8666f6b75..b9fd8aad8 100644 --- a/test/optimizer/loop_collapse/positive/simple_1/test.py +++ b/test/optimizer/loop_collapse/positive/simple_1/test.py @@ -33,7 +33,7 @@ def test(self): # execute DiscoPoP analysis os.chdir(".discopop") os.system("discopop_explorer") - os.system("discopop_optimizer -x") + os.system("discopop_optimizer -o3") os.chdir("..") # validate results self.validate_results(current_dir, src_dir)