Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace parallel easyconfig parameter by maxparallel #4398

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1890,7 +1890,7 @@ def skip_extensions_parallel(self, exts_filter):
exts_cnt = len(self.ext_instances)
cmds = [resolve_exts_filter_template(exts_filter, ext) for ext in self.ext_instances]

with ThreadPoolExecutor(max_workers=self.cfg['parallel']) as thread_pool:
with ThreadPoolExecutor(max_workers=self.cfg.parallel) as thread_pool:

# list of command to run asynchronously
async_cmds = [thread_pool.submit(run_shell_cmd, cmd, stdin=stdin, hidden=True, fail_on_error=False,
Expand Down Expand Up @@ -2020,7 +2020,7 @@ def install_extensions_parallel(self, install=True):
"""
self.log.info("Installing extensions in parallel...")

thread_pool = ThreadPoolExecutor(max_workers=self.cfg['parallel'])
thread_pool = ThreadPoolExecutor(max_workers=self.cfg.parallel)

running_exts = []
installed_ext_names = []
Expand Down Expand Up @@ -2082,7 +2082,7 @@ def update_exts_progress_bar_helper(running_exts, progress_size):

for _ in range(max_iter):

if not (exts_queue and len(running_exts) < self.cfg['parallel']):
if not (exts_queue and len(running_exts) < self.cfg.parallel):
break

# check whether extension at top of the queue is ready to install
Expand Down Expand Up @@ -2321,19 +2321,12 @@ def set_parallel(self):
"""Set 'parallel' easyconfig parameter to determine how many cores can/should be used for parallel builds."""
# set level of parallelism for build
par = build_option('parallel')
cfg_par = self.cfg['parallel']
if cfg_par is None:
if par is not None:
self.log.debug("Desired parallelism specified via 'parallel' build option: %s", par)
elif par is None:
par = cfg_par
self.log.debug("Desired parallelism specified via 'parallel' easyconfig parameter: %s", par)
else:
par = min(int(par), int(cfg_par))
self.log.debug("Desired parallelism: minimum of 'parallel' build option/easyconfig parameter: %s", par)

par = det_parallelism(par, maxpar=self.cfg['maxparallel'])
self.log.info("Setting parallelism: %s" % par)
self.cfg['parallel'] = par
self.cfg.parallel = par

def remove_module_file(self):
"""Remove module file (if it exists), and check for ghost installation directory (and deal with it)."""
Expand Down
2 changes: 0 additions & 2 deletions easybuild/framework/easyconfig/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@
'installopts': ['', 'Extra options for installation', BUILD],
'maxparallel': [16, 'Max degree of parallelism', BUILD],
'module_only': [False, 'Only generate module file', BUILD],
'parallel': [None, ('Degree of parallelism for e.g. make (default: based on the number of '
'cores, active cpuset and restrictions in ulimit)'), BUILD],
'patches': [[], "List of patches to apply", BUILD],
'prebuildopts': ['', 'Extra options pre-passed to build command.', BUILD],
'preconfigopts': ['', 'Extra options pre-passed to configure.', BUILD],
Expand Down
20 changes: 19 additions & 1 deletion easybuild/framework/easyconfig/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ def new_ec_method(self, key, *args, **kwargs):
key, ver = DEPRECATED_EASYCONFIG_PARAMETERS[depr_key]
_log.deprecated("Easyconfig parameter '%s' is deprecated, use '%s' instead" % (depr_key, key), ver)
elif key in REPLACED_PARAMETERS:
_log.nosupport("Easyconfig parameter '%s' is replaced by '%s'" % (key, REPLACED_PARAMETERS[key]), '2.0')
newkey, ver = REPLACED_PARAMETERS[key]
_log.nosupport("Easyconfig parameter '%s' is replaced by '%s'" % (key, newkey), ver)
return ec_method(self, key, *args, **kwargs)

return new_ec_method
Expand Down Expand Up @@ -499,6 +500,9 @@ def __init__(self, path, extra_options=None, build_specs=None, validate=True, hi
self.iterate_options = []
self.iterating = False

# Storage for parallel property. Mark as unset initially
self._parallel = None

# parse easyconfig file
self.build_specs = build_specs
self.parse()
Expand Down Expand Up @@ -1212,6 +1216,20 @@ def all_dependencies(self):

return self._all_dependencies

@property
def parallel(self):
"""Number of parallel jobs to be used for building etc."""
if self._parallel is None:
raise EasyBuildError("Parallelism in EasyConfig not set yet. "
"Need to call the easyblocks set_parallel first.")
return self._parallel

@parallel.setter
def parallel(self, value):
# Update backstorage and template value
self._parallel = value
self.template_values['parallel'] = value

def dump(self, fp, always_overwrite=True, backup=False, explicit_toolchains=False):
"""
Dump this easyconfig to file, with the given filename.
Expand Down
2 changes: 1 addition & 1 deletion easybuild/framework/easyconfig/format/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
['preconfigopts', 'configopts'],
['prebuildopts', 'buildopts'],
['preinstallopts', 'installopts'],
['parallel', 'maxparallel'],
['maxparallel'],
]
LAST_PARAMS = ['exts_default_options', 'exts_list',
'sanity_check_paths', 'sanity_check_commands',
Expand Down
8 changes: 5 additions & 3 deletions easybuild/framework/easyconfig/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@

# replaced easyconfig parameters, and their replacements
REPLACED_PARAMETERS = {
'license': 'license_file',
'makeopts': 'buildopts',
'premakeopts': 'prebuildopts',
# <old_param>: (<new_param>, <removed_in_version>),
'license': ('license_file', '2.0'),
'makeopts': ('buildopts', '2.0'),
'parallel': ('maxparallel', '5.0'),
'premakeopts': ('prebuildopts', '2.0'),
}

_log = fancylogger.getLogger('easyconfig.parser', fname=False)
Expand Down
3 changes: 1 addition & 2 deletions easybuild/framework/easyconfig/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
'bitbucket_account',
'github_account',
'name',
'parallel',
'version',
'versionsuffix',
'versionprefix',
Expand Down Expand Up @@ -108,7 +107,7 @@
'software_commit': "Git commit id to use for the software as specified by --software-commit command line option",
'sysroot': "Location root directory of system, prefix for standard paths like /usr/lib and /usr/include"
"as specify by the --sysroot configuration option",

'parallel': "Degree of parallelism for e.g. make",
}

# constant templates that can be used in easyconfigs
Expand Down
2 changes: 1 addition & 1 deletion easybuild/scripts/mk_tmpl_easyblock_for.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def build_step(self):
comp_fam = comp_map[self.toolchain.comp_family()]

# enable parallel build
par = self.cfg['parallel']
par = self.cfg.parallel
cmd = "build command --parallel %%d --compiler-family %%s" %% (par, comp_fam)
run_shell_cmd(cmd)

Expand Down
2 changes: 2 additions & 0 deletions easybuild/tools/systemtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,8 @@ def get_default_parallelism():
raise EasyBuildError("Specified level of parallelism '%s' is not an integer value: %s", par, err)

if maxpar is not None and maxpar < par:
if maxpar is False:
maxpar = 1
_log.info("Limiting parallelism from %s to %s", par, maxpar)
par = maxpar

Expand Down
65 changes: 45 additions & 20 deletions test/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2219,58 +2219,83 @@ def test_parallel(self):
toy_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
toytxt = read_file(toy_ec)

handle, toy_ec_error = tempfile.mkstemp(prefix='easyblock_test_file_', suffix='.eb')
os.close(handle)
write_file(toy_ec_error, toytxt + "\nparallel = 123")

handle, toy_ec1 = tempfile.mkstemp(prefix='easyblock_test_file_', suffix='.eb')
os.close(handle)
write_file(toy_ec1, toytxt + "\nparallel = 13")
write_file(toy_ec1, toytxt + "\nmaxparallel = None")

handle, toy_ec2 = tempfile.mkstemp(prefix='easyblock_test_file_', suffix='.eb')
os.close(handle)
write_file(toy_ec2, toytxt + "\nparallel = 123\nmaxparallel = 67")
write_file(toy_ec2, toytxt + "\nmaxparallel = 6")

handle, toy_ec3 = tempfile.mkstemp(prefix='easyblock_test_file_', suffix='.eb')
os.close(handle)
write_file(toy_ec3, toytxt + "\nparallel = False")
write_file(toy_ec3, toytxt + "\nmaxparallel = False")

import easybuild.tools.systemtools as st
auto_parallel = 15
st.det_parallelism._default_parallelism = auto_parallel

# 'parallel' easyconfig parameter specified is an error
self.assertRaises(EasyBuildError, EasyConfig, toy_ec_error)
self.assertErrorRegex(EasyBuildError, "Easyconfig parameter 'parallel' is replaced by 'maxparallel'",
EasyConfig, toy_ec_error)

# default: parallelism is derived from # available cores + ulimit
# Note that maxparallel has a default of 16, so we need a lower auto_paralle value here
test_eb = EasyBlock(EasyConfig(toy_ec))
test_eb.check_readiness_step()
self.assertTrue(isinstance(test_eb.cfg['parallel'], int) and test_eb.cfg['parallel'] > 0)
self.assertEqual(test_eb.cfg.parallel, auto_parallel)

# only 'parallel' easyconfig parameter specified (no 'parallel' build option)
test_eb = EasyBlock(EasyConfig(toy_ec1))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], 13)
auto_parallel = 128 # Don't limit by available CPU cores mock
st.det_parallelism._default_parallelism = auto_parallel

# both 'parallel' and 'maxparallel' easyconfig parameters specified (no 'parallel' build option)
# only 'maxparallel' easyconfig parameter specified (no 'parallel' build option)
test_eb = EasyBlock(EasyConfig(toy_ec2))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], 67)
self.assertEqual(test_eb.cfg.parallel, 6)

# make sure 'parallel = False' is not overriden (no 'parallel' build option)
# make sure 'maxparallel = False' is treated as 1 (no 'parallel' build option)
test_eb = EasyBlock(EasyConfig(toy_ec3))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], False)
self.assertEqual(test_eb.cfg.parallel, 1)

# only 'parallel' build option specified
init_config(build_options={'parallel': '9', 'validate': False})
init_config(build_options={'parallel': '13', 'validate': False})
test_eb = EasyBlock(EasyConfig(toy_ec))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], 9)
self.assertEqual(test_eb.cfg.parallel, 13)

# both 'parallel' build option and easyconfig parameter specified (no 'maxparallel')
# 'maxparallel = None' is the same as unset
test_eb = EasyBlock(EasyConfig(toy_ec1))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], 9)
self.assertEqual(test_eb.cfg.parallel, 13)

# both 'parallel' and 'maxparallel' easyconfig parameters specified + 'parallel' build option
# 'maxparallel' easyconfig parameter with 'parallel' build option
test_eb = EasyBlock(EasyConfig(toy_ec2))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], 9)
self.assertEqual(test_eb.cfg.parallel, 6)

# make sure 'parallel = False' is not overriden (with 'parallel' build option)
# make sure 'maxparallel = False' is treated as 1 (with 'parallel' build option)
test_eb = EasyBlock(EasyConfig(toy_ec3))
test_eb.check_readiness_step()
self.assertEqual(test_eb.cfg['parallel'], 0)
self.assertEqual(test_eb.cfg.parallel, 1)

# Template updated correctly
test_eb.cfg['buildopts'] = '-j %(parallel)s'
self.assertEqual(test_eb.cfg['buildopts'], '-j 1')
# Might be done in an easyblock step
test_eb.cfg.parallel = 42
self.assertEqual(test_eb.cfg['buildopts'], '-j 42')
# Unaffected by build settings
test_eb.cfg.parallel = 421337
self.assertEqual(test_eb.cfg['buildopts'], '-j 421337')

# Reset mocked value
del st.det_parallelism._default_parallelism

def test_guess_start_dir(self):
"""Test guessing the start dir."""
Expand Down
14 changes: 5 additions & 9 deletions test/framework/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ def test_tweaking(self):
'version = "3.14"',
'toolchain = {"name": "GCC", "version": "4.6.3"}',
'patches = %s',
'parallel = 1',
'maxparallel = 1',
'keepsymlinks = True',
]) % str(patches)
self.prep()
Expand All @@ -694,7 +694,7 @@ def test_tweaking(self):
# It should be possible to overwrite values with True/False/None as they often have special meaning
'runtest': 'False',
'hidden': 'True',
'parallel': 'None', # Good example: parallel=None means "Auto detect"
'maxparallel': 'None', # Good example: maxparallel=None means "unlimitted"
# Adding new options (added only by easyblock) should also be possible
# and in case the string "True/False/None" is really wanted it is possible to quote it first
'test_none': '"False"',
Expand All @@ -711,7 +711,7 @@ def test_tweaking(self):
self.assertEqual(eb['patches'], new_patches)
self.assertIs(eb['runtest'], False)
self.assertIs(eb['hidden'], True)
self.assertIsNone(eb['parallel'])
self.assertIsNone(eb['maxparallel'])
self.assertEqual(eb['test_none'], 'False')
self.assertEqual(eb['test_bool'], 'True')
self.assertEqual(eb['test_123'], 'None')
Expand Down Expand Up @@ -1931,8 +1931,7 @@ def test_alternative_easyconfig_parameters(self):

def test_deprecated_easyconfig_parameters(self):
"""Test handling of deprecated easyconfig parameters."""
os.environ.pop('EASYBUILD_DEPRECATED')
easybuild.tools.build_log.CURRENT_VERSION = self.orig_current_version
self.allow_deprecated_behaviour()
init_config()

test_ecs_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'easyconfigs', 'test_ecs')
Expand Down Expand Up @@ -3517,7 +3516,6 @@ def test_template_constant_dict(self):
'namelower': 'gzip',
'nameletter': 'g',
'nameletterlower': 'g',
'parallel': None,
'rpath_enabled': rpath,
'software_commit': '',
'sysroot': '',
Expand Down Expand Up @@ -3556,7 +3554,6 @@ def test_template_constant_dict(self):

res = template_constant_dict(ec)
res.pop('arch')
expected['parallel'] = 12
self.assertEqual(res, expected)

toy_ec = os.path.join(test_ecs_dir, 't', 'toy', 'toy-0.0-deps.eb')
Expand Down Expand Up @@ -3598,7 +3595,6 @@ def test_template_constant_dict(self):
'toolchain_name': 'system',
'toolchain_version': 'system',
'nameletterlower': 't',
'parallel': None,
'pymajver': '3',
'pyminver': '7',
'pyshortver': '3.7',
Expand Down Expand Up @@ -3634,7 +3630,7 @@ def test_template_constant_dict(self):
ec = EasyConfigParser(filename=test_ec).get_config_dict()

expected['module_name'] = None
for key in ('bitbucket_account', 'github_account', 'parallel', 'versionprefix'):
for key in ('bitbucket_account', 'github_account', 'versionprefix'):
del expected[key]

dep_names = [x[0] for x in ec['dependencies']]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
# ('Tcl', '8.6.4'),
]

parallel = 1
maxparallel = 1

sanity_check_paths = {
'files': ['bin/sqlite3', 'include/sqlite3ext.h', 'include/sqlite3.h', 'lib/libsqlite3.a', 'lib/libsqlite3.so'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
# ('Tcl', '8.6.4'),
]

parallel = 1
maxparallel = 1

sanity_check_paths = {
'files': ['bin/sqlite3', 'include/sqlite3ext.h', 'include/sqlite3.h', 'lib/libsqlite3.a', 'lib/libsqlite3.so'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
# ('Tcl', '8.6.4'),
]

parallel = 1
maxparallel = 1

sanity_check_paths = {
'files': ['bin/sqlite3', 'include/sqlite3ext.h', 'include/sqlite3.h', 'lib/libsqlite3.a', 'lib/libsqlite3.so'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ builddependencies = [('CMake','2.8.10')]
dependencies = [(local_blaslib, local_blasver, '')]

# parallel build tends to fail, so disabling it
parallel = 1
maxparallel = 1

moduleclass = 'numlib'
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ versionsuffix = "-%s-%s" % (local_blaslib, local_blasver)
dependencies = [(local_blaslib, local_blasver, '')]

# parallel build tends to fail, so disabling it
parallel = 1
maxparallel = 1

moduleclass = 'numlib'
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ versionsuffix = "-%s-%s" % (local_blaslib, local_blasver)
dependencies = [(local_blaslib, local_blasver)]

# parallel build tends to fail, so disabling it
parallel = 1
maxparallel = 1

moduleclass = 'numlib'
8 changes: 8 additions & 0 deletions test/framework/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,14 @@ def allow_deprecated_behaviour(self):
os.environ.pop('EASYBUILD_DEPRECATED', None)
eb_build_log.CURRENT_VERSION = self.orig_current_version

@contextmanager
def temporarily_allow_deprecated_behaviour(self):
self.allow_deprecated_behaviour()
try:
yield
finally:
self.disallow_deprecated_behaviour()

@contextmanager
def log_to_testlogfile(self):
"""Context manager class to capture log output in self.logfile for the scope used. Clears the file first"""
Expand Down
Loading