From c49859617f7f3b0fd525af37a1c3a7544cc110d0 Mon Sep 17 00:00:00 2001 From: Anna Elizabeth Woodard Date: Thu, 12 Sep 2019 12:38:00 -0400 Subject: [PATCH 1/5] Add resource hints for Condor provider Part of fix for #942. --- parsl/providers/condor/condor.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/parsl/providers/condor/condor.py b/parsl/providers/condor/condor.py index cfdf5066c4..0e7ffc3a61 100644 --- a/parsl/providers/condor/condor.py +++ b/parsl/providers/condor/condor.py @@ -2,6 +2,7 @@ import os import re import time +import math from parsl.channels import LocalChannel from parsl.utils import RepresentationMixin @@ -35,6 +36,12 @@ class CondorProvider(RepresentationMixin, ClusterProvider): :class:`~parsl.channels.SSHInteractiveLoginChannel`. nodes_per_block : int Nodes to provision per block. + cores_per_node : int + Specify the number of cores to provision per node. If set to None, executors + will assume all cores on the node are available for computation. Default is None. + mem_per_node : float + Specify the real memory to provision per node in GB. If set to None, no + explicit request to the scheduler will be made. Default is None. init_blocks : int Number of blocks to provision at time of initialization min_blocks : int @@ -67,6 +74,8 @@ class CondorProvider(RepresentationMixin, ClusterProvider): def __init__(self, channel=LocalChannel(), nodes_per_block=1, + cores_per_node=None, + mem_per_node=None, init_blocks=1, min_blocks=0, max_blocks=10, @@ -93,6 +102,8 @@ def __init__(self, launcher, cmd_timeout=cmd_timeout) self.provisioned_blocks = 0 + self.cores_per_node = cores_per_node + self.mem_per_node = mem_per_node self.environment = environment if environment is not None else {} for key, value in self.environment.items(): @@ -104,8 +115,8 @@ def __init__(self, pass self.project = project - self.scheduler_options = scheduler_options - self.worker_init = worker_init + self.scheduler_options = scheduler_options + '\n' + self.worker_init = worker_init + '\n' self.requirements = requirements self.transfer_input_files = transfer_input_files @@ -187,6 +198,16 @@ def submit(self, command, tasks_per_node, job_name="parsl.auto"): job_name = "parsl.{0}.{1}".format(job_name, time.time()) + scheduler_options = self.scheduler_options + worker_init = self.worker_init + if self.mem_per_node is not None: + scheduler_options += 'RequestMemory = {}'.format(self.mem_per_node * 1024) + worker_init += 'export PARSL_MEMORY_GB={}\n'.format(self.mem_per_node) + if self.cores_per_node is not None: + cpus_per_task = math.floor(self.cores_per_node / tasks_per_node) + scheduler_options += 'RequestCpus = {}'.format(cpus_per_task) + worker_init += 'export PARSL_CORES={}\n'.format(cpus_per_task) + script_path = "{0}/{1}.submit".format(self.script_dir, job_name) script_path = os.path.abspath(script_path) userscript_path = "{0}/{1}.script".format(self.script_dir, job_name) From 4d3507b3bca0daf48caa07fd95b100d4c9a2dd89 Mon Sep 17 00:00:00 2001 From: Anna Elizabeth Woodard Date: Thu, 12 Sep 2019 15:43:32 -0400 Subject: [PATCH 2/5] Use modified scheduler_options and worker_init --- parsl/providers/condor/condor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parsl/providers/condor/condor.py b/parsl/providers/condor/condor.py index 0e7ffc3a61..794d4b51fa 100644 --- a/parsl/providers/condor/condor.py +++ b/parsl/providers/condor/condor.py @@ -220,8 +220,8 @@ def submit(self, command, tasks_per_node, job_name="parsl.auto"): job_config["submit_script_dir"] = self.channel.script_dir job_config["project"] = self.project job_config["nodes"] = self.nodes_per_block - job_config["scheduler_options"] = self.scheduler_options - job_config["worker_init"] = self.worker_init + job_config["scheduler_options"] = scheduler_options + job_config["worker_init"] = worker_init job_config["user_script"] = command job_config["tasks_per_node"] = tasks_per_node job_config["requirements"] = self.requirements From dbefca117ba9a9a37d14c7d9254ff6fe00b37aff Mon Sep 17 00:00:00 2001 From: Anna Elizabeth Woodard Date: Thu, 12 Sep 2019 18:46:20 -0400 Subject: [PATCH 3/5] Ask for slots of size `cores_per_node` --- parsl/providers/condor/condor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parsl/providers/condor/condor.py b/parsl/providers/condor/condor.py index 794d4b51fa..c3e446969e 100644 --- a/parsl/providers/condor/condor.py +++ b/parsl/providers/condor/condor.py @@ -204,9 +204,8 @@ def submit(self, command, tasks_per_node, job_name="parsl.auto"): scheduler_options += 'RequestMemory = {}'.format(self.mem_per_node * 1024) worker_init += 'export PARSL_MEMORY_GB={}\n'.format(self.mem_per_node) if self.cores_per_node is not None: - cpus_per_task = math.floor(self.cores_per_node / tasks_per_node) - scheduler_options += 'RequestCpus = {}'.format(cpus_per_task) - worker_init += 'export PARSL_CORES={}\n'.format(cpus_per_task) + scheduler_options += 'RequestCpus = {}'.format(self.cores_per_node) + worker_init += 'export PARSL_CORES={}\n'.format(self.cores_per_node) script_path = "{0}/{1}.submit".format(self.script_dir, job_name) script_path = os.path.abspath(script_path) From 8165c4f4cbc6edbc1f429b987b5c77aa46ce3cae Mon Sep 17 00:00:00 2001 From: Anna Elizabeth Woodard Date: Thu, 12 Sep 2019 21:06:34 -0400 Subject: [PATCH 4/5] Fix flake8 --- parsl/providers/condor/condor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/parsl/providers/condor/condor.py b/parsl/providers/condor/condor.py index c3e446969e..10e09cf7fa 100644 --- a/parsl/providers/condor/condor.py +++ b/parsl/providers/condor/condor.py @@ -2,7 +2,6 @@ import os import re import time -import math from parsl.channels import LocalChannel from parsl.utils import RepresentationMixin From bbf2d7abeb9361268f00d429dc98ea34d5326bf9 Mon Sep 17 00:00:00 2001 From: Anna Elizabeth Woodard Date: Sun, 15 Sep 2019 11:31:43 -0400 Subject: [PATCH 5/5] Change cores_per_node -> cores_per_slot This is more consistent with Condor parlance. Also: add missing newlines. --- parsl/providers/condor/condor.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/parsl/providers/condor/condor.py b/parsl/providers/condor/condor.py index 10e09cf7fa..2defd77f11 100644 --- a/parsl/providers/condor/condor.py +++ b/parsl/providers/condor/condor.py @@ -35,11 +35,11 @@ class CondorProvider(RepresentationMixin, ClusterProvider): :class:`~parsl.channels.SSHInteractiveLoginChannel`. nodes_per_block : int Nodes to provision per block. - cores_per_node : int - Specify the number of cores to provision per node. If set to None, executors + cores_per_slot : int + Specify the number of cores to provision per slot. If set to None, executors will assume all cores on the node are available for computation. Default is None. - mem_per_node : float - Specify the real memory to provision per node in GB. If set to None, no + mem_per_slot : float + Specify the real memory to provision per slot in GB. If set to None, no explicit request to the scheduler will be made. Default is None. init_blocks : int Number of blocks to provision at time of initialization @@ -73,8 +73,8 @@ class CondorProvider(RepresentationMixin, ClusterProvider): def __init__(self, channel=LocalChannel(), nodes_per_block=1, - cores_per_node=None, - mem_per_node=None, + cores_per_slot=None, + mem_per_slot=None, init_blocks=1, min_blocks=0, max_blocks=10, @@ -101,8 +101,12 @@ def __init__(self, launcher, cmd_timeout=cmd_timeout) self.provisioned_blocks = 0 - self.cores_per_node = cores_per_node - self.mem_per_node = mem_per_node + self.cores_per_slot = cores_per_slot + self.mem_per_slot = mem_per_slot + + # To Parsl, Condor slots should be treated equivalently to nodes + self.cores_per_node = cores_per_slot + self.mem_per_node = mem_per_slot self.environment = environment if environment is not None else {} for key, value in self.environment.items(): @@ -199,12 +203,12 @@ def submit(self, command, tasks_per_node, job_name="parsl.auto"): scheduler_options = self.scheduler_options worker_init = self.worker_init - if self.mem_per_node is not None: - scheduler_options += 'RequestMemory = {}'.format(self.mem_per_node * 1024) - worker_init += 'export PARSL_MEMORY_GB={}\n'.format(self.mem_per_node) - if self.cores_per_node is not None: - scheduler_options += 'RequestCpus = {}'.format(self.cores_per_node) - worker_init += 'export PARSL_CORES={}\n'.format(self.cores_per_node) + if self.mem_per_slot is not None: + scheduler_options += 'RequestMemory = {}\n'.format(self.mem_per_slot * 1024) + worker_init += 'export PARSL_MEMORY_GB={}\n'.format(self.mem_per_slot) + if self.cores_per_slot is not None: + scheduler_options += 'RequestCpus = {}\n'.format(self.cores_per_slot) + worker_init += 'export PARSL_CORES={}\n'.format(self.cores_per_slot) script_path = "{0}/{1}.submit".format(self.script_dir, job_name) script_path = os.path.abspath(script_path)