Skip to content
This repository was archived by the owner on Jan 31, 2025. It is now read-only.

Commit e8633b8

Browse files
committed
PyOP2 compiler updates
1 parent 01cfcd0 commit e8633b8

File tree

1 file changed

+84
-88
lines changed

1 file changed

+84
-88
lines changed

pyop3/target.py

+84-88
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def _check_hashes(x, y, datatype):
125125

126126

127127
def set_default_compiler(compiler):
128-
"""Set the PyOP2 default compiler, globally.
128+
"""Set the PyOP2 default compiler, globally over COMM_WORLD.
129129
130130
:arg compiler: String with name or path to compiler executable
131131
OR a subclass of the Compiler class
@@ -147,66 +147,73 @@ def set_default_compiler(compiler):
147147
)
148148

149149

150-
def sniff_compiler(exe):
150+
def sniff_compiler(exe, comm=mpi.COMM_WORLD):
151151
"""Obtain the correct compiler class by calling the compiler executable.
152152
153153
:arg exe: String with name or path to compiler executable
154+
:arg comm: Comm over which we want to determine the compiler type
154155
:returns: A compiler class
155156
"""
156-
try:
157-
output = subprocess.run(
158-
[exe, "--version"],
159-
stdout=subprocess.PIPE,
160-
stderr=subprocess.PIPE,
161-
check=True,
162-
encoding="utf-8",
163-
).stdout
164-
except (subprocess.CalledProcessError, UnicodeDecodeError):
165-
output = ""
166-
167-
# Find the name of the compiler family
168-
if output.startswith("gcc") or output.startswith("g++"):
169-
name = "GNU"
170-
elif output.startswith("clang"):
171-
name = "clang"
172-
elif output.startswith("Apple LLVM") or output.startswith("Apple clang"):
173-
name = "clang"
174-
elif output.startswith("icc"):
175-
name = "Intel"
176-
elif "Cray" in output.split("\n")[0]:
177-
# Cray is more awkward eg:
178-
# Cray clang version 11.0.4 (<some_hash>)
179-
# gcc (GCC) 9.3.0 20200312 (Cray Inc.)
180-
name = "Cray"
181-
else:
182-
name = "unknown"
183-
184-
# Set the compiler instance based on the platform (and architecture)
185-
if sys.platform.find("linux") == 0:
186-
if name == "Intel":
187-
compiler = LinuxIntelCompiler
188-
elif name == "GNU":
189-
compiler = LinuxGnuCompiler
190-
elif name == "clang":
191-
compiler = LinuxClangCompiler
192-
elif name == "Cray":
193-
compiler = LinuxCrayCompiler
157+
compiler = None
158+
if comm.rank == 0:
159+
# Note:
160+
# Sniffing compiler for very large numbers of MPI ranks is
161+
# expensive so we do this on one rank and broadcast
162+
try:
163+
output = subprocess.run(
164+
[exe, "--version"],
165+
stdout=subprocess.PIPE,
166+
stderr=subprocess.PIPE,
167+
check=True,
168+
encoding="utf-8"
169+
).stdout
170+
except (subprocess.CalledProcessError, UnicodeDecodeError):
171+
output = ""
172+
173+
# Find the name of the compiler family
174+
if output.startswith("gcc") or output.startswith("g++"):
175+
name = "GNU"
176+
elif output.startswith("clang"):
177+
name = "clang"
178+
elif output.startswith("Apple LLVM") or output.startswith("Apple clang"):
179+
name = "clang"
180+
elif output.startswith("icc"):
181+
name = "Intel"
182+
elif "Cray" in output.split("\n")[0]:
183+
# Cray is more awkward eg:
184+
# Cray clang version 11.0.4 (<some_hash>)
185+
# gcc (GCC) 9.3.0 20200312 (Cray Inc.)
186+
name = "Cray"
194187
else:
195-
compiler = AnonymousCompiler
196-
elif sys.platform.find("darwin") == 0:
197-
if name == "clang":
198-
machine = platform.uname().machine
199-
if machine == "arm64":
200-
compiler = MacClangARMCompiler
201-
elif machine == "x86_64":
202-
compiler = MacClangCompiler
203-
elif name == "GNU":
204-
compiler = MacGNUCompiler
188+
name = "unknown"
189+
190+
# Set the compiler instance based on the platform (and architecture)
191+
if sys.platform.find("linux") == 0:
192+
if name == "Intel":
193+
compiler = LinuxIntelCompiler
194+
elif name == "GNU":
195+
compiler = LinuxGnuCompiler
196+
elif name == "clang":
197+
compiler = LinuxClangCompiler
198+
elif name == "Cray":
199+
compiler = LinuxCrayCompiler
200+
else:
201+
compiler = AnonymousCompiler
202+
elif sys.platform.find("darwin") == 0:
203+
if name == "clang":
204+
machine = platform.uname().machine
205+
if machine == "arm64":
206+
compiler = MacClangARMCompiler
207+
elif machine == "x86_64":
208+
compiler = MacClangCompiler
209+
elif name == "GNU":
210+
compiler = MacGNUCompiler
211+
else:
212+
compiler = AnonymousCompiler
205213
else:
206214
compiler = AnonymousCompiler
207-
else:
208-
compiler = AnonymousCompiler
209-
return compiler
215+
216+
return comm.bcast(compiler, 0)
210217

211218

212219
class Compiler(ABC):
@@ -243,8 +250,8 @@ class Compiler(ABC):
243250
def __init__(
244251
self, extra_compiler_flags=(), extra_linker_flags=(), cpp=False, comm=None
245252
):
246-
# Get compiler version ASAP since it is used in __repr__
247-
self.sniff_compiler_version()
253+
# Set compiler version ASAP since it is used in __repr__
254+
self.version = None
248255

249256
self._extra_compiler_flags = tuple(extra_compiler_flags)
250257
self._extra_linker_flags = tuple(extra_linker_flags)
@@ -255,6 +262,7 @@ def __init__(
255262
# Compilation communicators are reference counted on the PyOP2 comm
256263
self.pcomm = mpi.internal_comm(comm, self)
257264
self.comm = mpi.compilation_comm(self.pcomm, self)
265+
self.sniff_compiler_version()
258266

259267
def __repr__(self):
260268
return f"<{self._name} compiler, version {self.version or 'unknown'}>"
@@ -307,23 +315,28 @@ def sniff_compiler_version(self, cpp=False):
307315
:arg cpp: If set to True will use the C++ compiler rather than
308316
the C compiler to determine the version number.
309317
"""
318+
# Note:
319+
# Sniffing the compiler version for very large numbers of
320+
# MPI ranks is expensive
310321
exe = self.cxx if cpp else self.cc
311-
self.version = None
312-
# `-dumpversion` is not sufficient to get the whole version string (for some compilers),
313-
# but other compilers do not implement `-dumpfullversion`!
314-
for dumpstring in ["-dumpfullversion", "-dumpversion"]:
315-
try:
316-
output = subprocess.run(
317-
[exe, dumpstring],
318-
stdout=subprocess.PIPE,
319-
stderr=subprocess.PIPE,
320-
check=True,
321-
encoding="utf-8",
322-
).stdout
323-
self.version = Version(output)
324-
break
325-
except (subprocess.CalledProcessError, UnicodeDecodeError, InvalidVersion):
326-
continue
322+
version = None
323+
if self.comm.rank == 0:
324+
# `-dumpversion` is not sufficient to get the whole version string (for some compilers),
325+
# but other compilers do not implement `-dumpfullversion`!
326+
for dumpstring in ["-dumpfullversion", "-dumpversion"]:
327+
try:
328+
output = subprocess.run(
329+
[exe, dumpstring],
330+
stdout=subprocess.PIPE,
331+
stderr=subprocess.PIPE,
332+
check=True,
333+
encoding="utf-8"
334+
).stdout
335+
version = Version(output)
336+
break
337+
except (subprocess.CalledProcessError, UnicodeDecodeError, InvalidVersion):
338+
continue
339+
self.version = self.comm.bcast(version, 0)
327340

328341
@property
329342
def bugfix_cflags(self):
@@ -539,23 +552,6 @@ class LinuxGnuCompiler(Compiler):
539552
_optflags = ("-march=native", "-O3", "-ffast-math")
540553
_debugflags = ("-O0", "-g")
541554

542-
def sniff_compiler_version(self, cpp=False):
543-
super(LinuxGnuCompiler, self).sniff_compiler_version()
544-
if self.version >= Version("7.0"):
545-
try:
546-
# gcc-7 series only spits out patch level on dumpfullversion.
547-
exe = self.cxx if cpp else self.cc
548-
output = subprocess.run(
549-
[exe, "-dumpfullversion"],
550-
stdout=subprocess.PIPE,
551-
stderr=subprocess.PIPE,
552-
check=True,
553-
encoding="utf-8",
554-
).stdout
555-
self.version = Version(output)
556-
except (subprocess.CalledProcessError, UnicodeDecodeError, InvalidVersion):
557-
pass
558-
559555
@property
560556
def bugfix_cflags(self):
561557
"""Flags to work around bugs in compilers."""

0 commit comments

Comments
 (0)