Compiler search uses a pool of workers (#10190)

- spack.compilers.find_compilers now uses a multiprocess.pool.ThreadPool to execute
  system commands for the detection of compiler versions.

- A few memoized functions have been introduced to avoid poking the filesystem multiple
  times for the same results.

- Performance is much improved, and Spack no longer fork-bombs the system when doing a `compiler find`
This commit is contained in:
Massimiliano Culpo
2019-06-07 18:57:26 +02:00
committed by Todd Gamblin
parent 9c1c50fb76
commit 6d56d45454
17 changed files with 501 additions and 300 deletions

View File

@@ -21,7 +21,7 @@
import six
from llnl.util import tty
from llnl.util.lang import dedupe
from llnl.util.lang import dedupe, memoized
from spack.util.executable import Executable
__all__ = [
@@ -1351,3 +1351,63 @@ def find_libraries(libraries, root, shared=True, recursive=False):
libraries = ['{0}.{1}'.format(lib, suffix) for lib in libraries]
return LibraryList(find(root, libraries, recursive))
@memoized
def can_access_dir(path):
"""Returns True if the argument is an accessible directory.
Args:
path: path to be tested
Returns:
True if ``path`` is an accessible directory, else False
"""
return os.path.isdir(path) and os.access(path, os.R_OK | os.X_OK)
@memoized
def files_in(*search_paths):
"""Returns all the files in paths passed as arguments.
Caller must ensure that each path in ``search_paths`` is a directory.
Args:
*search_paths: directories to be searched
Returns:
List of (file, full_path) tuples with all the files found.
"""
files = []
for d in filter(can_access_dir, search_paths):
files.extend(filter(
lambda x: os.path.isfile(x[1]),
[(f, os.path.join(d, f)) for f in os.listdir(d)]
))
return files
def search_paths_for_executables(*path_hints):
"""Given a list of path hints returns a list of paths where
to search for an executable.
Args:
*path_hints (list of paths): list of paths taken into
consideration for a search
Returns:
A list containing the real path of every existing directory
in `path_hints` and its `bin` subdirectory if it exists.
"""
executable_paths = []
for path in path_hints:
if not os.path.isdir(path):
continue
executable_paths.append(path)
bin_dir = os.path.join(path, 'bin')
if os.path.isdir(bin_dir):
executable_paths.append(bin_dir)
return executable_paths

View File

@@ -8,25 +8,9 @@
than multiprocessing.Pool.apply() can. For example, apply() will fail
to pickle functions if they're passed indirectly as parameters.
"""
from multiprocessing import Process, Pipe, Semaphore, Value
from multiprocessing import Semaphore, Value
__all__ = ['spawn', 'parmap', 'Barrier']
def spawn(f):
def fun(pipe, x):
pipe.send(f(x))
pipe.close()
return fun
def parmap(f, elements):
pipe = [Pipe() for x in elements]
proc = [Process(target=spawn(f), args=(c, x))
for x, (p, c) in zip(elements, pipe)]
[p.start() for p in proc]
[p.join() for p in proc]
return [p.recv() for (p, c) in pipe]
__all__ = ['Barrier']
class Barrier:

View File

@@ -3,7 +3,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from datetime import datetime
from __future__ import unicode_literals
import fcntl
import os
import struct
@@ -11,6 +12,8 @@
import termios
import textwrap
import traceback
import six
from datetime import datetime
from six import StringIO
from six.moves import input
@@ -155,7 +158,7 @@ def msg(message, *args, **kwargs):
cwrite("@*b{%s==>} %s%s" % (
st_text, get_timestamp(), cescape(message)))
for arg in args:
print(indent + str(arg))
print(indent + six.text_type(arg))
def info(message, *args, **kwargs):
@@ -172,17 +175,17 @@ def info(message, *args, **kwargs):
if _stacktrace:
st_text = process_stacktrace(st_countback)
cprint("@%s{%s==>} %s%s" % (
format, st_text, get_timestamp(), cescape(str(message))),
stream=stream)
format, st_text, get_timestamp(), cescape(six.text_type(message))
), stream=stream)
for arg in args:
if wrap:
lines = textwrap.wrap(
str(arg), initial_indent=indent, subsequent_indent=indent,
break_long_words=break_long_words)
six.text_type(arg), initial_indent=indent,
subsequent_indent=indent, break_long_words=break_long_words)
for line in lines:
stream.write(line + '\n')
else:
stream.write(indent + str(arg) + '\n')
stream.write(indent + six.text_type(arg) + '\n')
def verbose(message, *args, **kwargs):
@@ -204,7 +207,7 @@ def error(message, *args, **kwargs):
kwargs.setdefault('format', '*r')
kwargs.setdefault('stream', sys.stderr)
info("Error: " + str(message), *args, **kwargs)
info("Error: " + six.text_type(message), *args, **kwargs)
def warn(message, *args, **kwargs):
@@ -213,7 +216,7 @@ def warn(message, *args, **kwargs):
kwargs.setdefault('format', '*Y')
kwargs.setdefault('stream', sys.stderr)
info("Warning: " + str(message), *args, **kwargs)
info("Warning: " + six.text_type(message), *args, **kwargs)
def die(message, *args, **kwargs):
@@ -237,7 +240,7 @@ def get_number(prompt, **kwargs):
while number is None:
msg(prompt, newline=False)
ans = input()
if ans == str(abort):
if ans == six.text_type(abort):
return None
if ans:
@@ -303,7 +306,7 @@ def hline(label=None, **kwargs):
cols -= 2
cols = min(max_width, cols)
label = str(label)
label = six.text_type(label)
prefix = char * 2 + " "
suffix = " " + (cols - len(prefix) - clen(label)) * char

View File

@@ -6,7 +6,7 @@
"""
Routines for printing columnar output. See ``colify()`` for more information.
"""
from __future__ import division
from __future__ import division, unicode_literals
import os
import sys

View File

@@ -59,10 +59,14 @@
To output an @, use '@@'. To output a } inside braces, use '}}'.
"""
from __future__ import unicode_literals
import re
import sys
from contextlib import contextmanager
import six
class ColorParseError(Exception):
"""Raised when a color format fails to parse."""
@@ -244,7 +248,7 @@ def cescape(string):
Returns:
(str): the string with color codes escaped
"""
string = str(string)
string = six.text_type(string)
string = string.replace('@', '@@')
string = string.replace('}', '}}')
return string

View File

@@ -5,6 +5,8 @@
"""Utility classes for logging the output of blocks of code.
"""
from __future__ import unicode_literals
import multiprocessing
import os
import re