diff --git a/CHANGELOG.md b/CHANGELOG.md index 88f7a128f7f27092fed4cfc2bbb3cee900c92fcd..6b5d65653470e656f6e9788c36ed503e3939bb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - each non basic JuMonC plugin gets an automatic option to disable that plugin - standart function to add parameter to REST-API call (json_schema), to return a json schema describing the expected normal result for this path +- allow changes to the MPI_sleep_timer using the CMD parameters and the REST-API ### Changed - rename from JuMonC to jumonc for package name diff --git a/doc/CMD/Parameters.md b/doc/CMD/Parameters.md index d98b94392e0689fd28d04e0e39972d0c5b25ce50..d136fffc3799e18d9cebabcb55474f8ae340f201 100644 --- a/doc/CMD/Parameters.md +++ b/doc/CMD/Parameters.md @@ -10,6 +10,7 @@ usage: jumonc [-h] [--log-level {'ERROR','WARN','INFO','DEBUG'}] [--log-stdout] [--log-prefix LOG_PREFIX] [--max-worker-threads MAX_WORKER_THREADS] + [--mpi_sleep_timer MPI_SLEEP_TIMER] [--only-choosen-rest-api-version] [--pending-tasks-soft-limit PENDING_TASKS_SOFT_LIMIT] [--plugin-paths [PLUGIN_PATHS ...]] [-p [1024-65535]] @@ -36,6 +37,7 @@ usage: jumonc [-h] | |`--log-stdout` | |If used log to stdout, otherwise to stderr | | |`--log-prefix` |`` |Set a prefix that will be prefaced to every logging output | | |`--max-worker-threads` |`4` |Limits the number of worker threads that work on the actual tasks at once | +| |`--mpi_sleep_timer` |`0.005` |Allow the CPU to idle before checking for new MPI communication, time in seconds between checks. Reduces CPU load but increases latency for requests | | |`--only-choosen-rest-api-version` | |If set will only provide one version of the api links | | |`--pending-tasks-soft-limit` |`100` |Limits tasks being added by the REST-API, to not have more than PENDING-TASKS-SOFT-LIMIT tasks waiting | | |`--plugin-paths` |`[]` |Paths to jumonc plugins, multiple values allowed | @@ -103,6 +105,10 @@ Set a prefix that will be prefaced to every logging output ### `--max-worker-threads` (Default: 4) Limits the number of worker threads that work on the actual tasks at once +### `--mpi_sleep_timer` (Default: 0.005) +Allow the CPU to idle before checking for new MPI communication, time in +seconds between checks. Reduces CPU load but increases latency for requests + ### `--only-choosen-rest-api-version` If set will only provide one version of the api links diff --git a/jumonc/handlers/settings.py b/jumonc/handlers/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..78d304b3f951da61d4ef76dea2bb40789faa4822 --- /dev/null +++ b/jumonc/handlers/settings.py @@ -0,0 +1,62 @@ +import logging +from typing import Dict +from typing import List +from typing import Union + +from flask import jsonify +from flask import make_response +from flask import request +from flask import Response + +from jumonc import settings +from jumonc.authentication import scopes +from jumonc.authentication.check import check_auth +from jumonc.handlers.base import api_version_path +from jumonc.handlers.base import check_version +from jumonc.handlers.base import RESTAPI +from jumonc.tasks import Settings + +logger = logging.getLogger(__name__) + +links: List[Dict[str, Union[bool, str, List[Dict[str, str]]]]] = [] + +settings_path = "/settings" + +@RESTAPI.route(api_version_path + settings_path, methods=["GET"]) +@check_version +@check_auth(scopes["see_links"]) +def returnCPULinks(version: int) -> Response: + logger.debug("Accessed /v%i/settings/", version) + return make_response(jsonify(sorted(links, key=lambda dic: dic['link'])), 200) + +def registerRestApiPaths(version: int) -> Dict[str, Union[bool, str, List[Dict[str, str]]]]: + registerSettingsLinks(version) + return { + "link": "/v" + str(version) + settings_path, + "isOptional": False, + "description": "Gather and change JuMonC parameters", + "parameters": [ + {"name": "token", + "description": "Supply a token that shows you are allowed to access this link (or login once using /login)"}] + } + + +def registerSettingsLinks(version: int) -> None: + registerMPISleepLink(version) + + +def registerMPISleepLink(version:int) -> None: + logger.debug("Register \"/v%s%s/mpi_sleep_timer\"", str(version), settings_path) + + @RESTAPI.route(api_version_path + settings_path + "/mpi_sleep_timer", methods=["GET"]) + @check_version + @check_auth(scopes["full"]) + def MPISleepTimer(version: int) -> Response: + logger.debug("Accessed /v%i/settings/mpi_sleep_timer/", version) + + sleep_time = request.args.get('sleep_time', default = None, type = float) + if sleep_time is not None: + Settings.setMPISleep(sleep_time) + return make_response(jsonify({"Update MPI_sleep_timer": settings.MPI_sleep_timer}), 200) + + return make_response(jsonify({"MPI_sleep_timer": settings.MPI_sleep_timer}), 200) diff --git a/jumonc/helpers/cmdArguments.py b/jumonc/helpers/cmdArguments.py index 6274445ab1e4bab44f5582d8c0002d4a81ae5242..6c8d08bb5f7962d14b05e54f9614f0c7bd4f7d4c 100644 --- a/jumonc/helpers/cmdArguments.py +++ b/jumonc/helpers/cmdArguments.py @@ -65,12 +65,12 @@ def setupParser() -> argparse.ArgumentParser: action='store_true') parser.add_argument("--INIT-FILE".lower() , dest="INIT_FILE", - help=(("Path and filename to an init file, to overwrite settings. This file will be imported and allows abitrary code execution. " + help=("Path and filename to an init file, to overwrite settings. This file will be imported and allows abitrary code execution. " "Only use trustworthy sources for this init file. " "The settings from the init File take priority over all CMD arguments and will not be verified for correctness. " "All possible values (with their default values): " "https://gitlab.jsc.fz-juelich.de/coec/jumonc/-/blob/main/jumonc/settings/__init__.py " - "An example init file can be seen at: https://gitlab.jsc.fz-juelich.de/coec/jumonc/-/blob/main/doc/CMD/init_jumonc.py ")), + "An example init file can be seen at: https://gitlab.jsc.fz-juelich.de/coec/jumonc/-/blob/main/doc/CMD/init_jumonc.py "), default=None, type=str) parser.add_argument("--LOCAL-ONLY".lower(), @@ -104,6 +104,12 @@ def setupParser() -> argparse.ArgumentParser: help="Limits the number of worker threads that work on the actual tasks at once", default=4, type=int) + parser.add_argument("--MPI_SLEEP_TIMER".lower(), + dest="MPI_SLEEP_TIMER", + help=("Allow the CPU to idle before checking for new MPI communication, time in seconds between checks. " + "Reduces CPU load but increases latency for requests"), + default=0.005, + type=float) parser.add_argument("--ONLY-CHOOSEN-REST-API-VERSION".lower(), dest="ONLY_CHOOSEN_REST_API_VERSION", help="If set will only provide one version of the api links", @@ -204,7 +210,6 @@ def evaluateArgs(parser: argparse.ArgumentParser, args: List[str]) -> None: # After adding plugin arguments, reparse addPluginArgs(parser) - #parsed = parser.parse_args(args) parsed, unknown = parser.parse_known_args(args) logger.debug("Argument parsing, first pass") @@ -217,7 +222,13 @@ def evaluateArgs(parser: argparse.ArgumentParser, args: List[str]) -> None: evaluateThreadingArgs(parsed) evaluateSecurityArgs(parsed) evaluateSchedulingArgs(parsed) + # After adding plugin arguments, reparse evaluteInitFile(parsed) + + parsed, unknown = parser.parse_known_args(args) + logger.debug("Argument parsing, first pass") + logger.debug("Parsed: %s", str(parsed)) + logger.debug("Unknown: %s", str(unknown)) evaluatePluginArgs(parsed) @@ -367,6 +378,16 @@ def evaluateMiscellaneousArgs(parsed:argparse.Namespace) -> None: settings.PLUGIN_PATHS.extend(parsed.PLUGIN_PATHS) logger.info("Set PLUGIN_PATHS to %s", str(settings.PLUGIN_PATHS)) + + if parsed.MPI_SLEEP_TIMER > 0.0: + settings.MPI_reduce_idle_CPU_load = True + settings.MPI_sleep_timer = parsed.MPI_SLEEP_TIMER + logger.info("Set MPI_SLEEP_TIMER to %s", str(settings.MPI_sleep_timer)) + else: + settings.MPI_reduce_idle_CPU_load = False + settings.MPI_sleep_timer = 0.0 + logger.info("Disabled MPI_SLEEP") + diff --git a/jumonc/settings/__init__.py b/jumonc/settings/__init__.py index 280167b1c886513c4e2e162bc687c6ac6b463a90..a2c9b66144205ba6460b61feb77156e28352f265 100644 --- a/jumonc/settings/__init__.py +++ b/jumonc/settings/__init__.py @@ -59,4 +59,4 @@ SCHEDULE_TASKS:List[str] = [] MPI_reduce_idle_CPU_load = True -MPI_sleep_timer=0.001 +MPI_sleep_timer=0.005 diff --git a/jumonc/tasks/Settings.py b/jumonc/tasks/Settings.py new file mode 100644 index 0000000000000000000000000000000000000000..020eb550bd0d502919b1c2c8476d6d326fbe09af --- /dev/null +++ b/jumonc/tasks/Settings.py @@ -0,0 +1,11 @@ +import logging + +from jumonc import settings +from jumonc.tasks.mpi_helper import multi_node_information + + +logger = logging.getLogger(__name__) + +@multi_node_information() +def setMPISleep(sleep_time: float) -> None: + settings.MPI_sleep_timer = sleep_time