@@ -125,7 +125,7 @@ def _check_hashes(x, y, datatype):
125
125
126
126
127
127
def set_default_compiler (compiler ):
128
- """Set the PyOP2 default compiler, globally.
128
+ """Set the PyOP2 default compiler, globally over COMM_WORLD .
129
129
130
130
:arg compiler: String with name or path to compiler executable
131
131
OR a subclass of the Compiler class
@@ -147,66 +147,73 @@ def set_default_compiler(compiler):
147
147
)
148
148
149
149
150
- def sniff_compiler (exe ):
150
+ def sniff_compiler (exe , comm = mpi . COMM_WORLD ):
151
151
"""Obtain the correct compiler class by calling the compiler executable.
152
152
153
153
:arg exe: String with name or path to compiler executable
154
+ :arg comm: Comm over which we want to determine the compiler type
154
155
:returns: A compiler class
155
156
"""
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"
194
187
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
205
213
else :
206
214
compiler = AnonymousCompiler
207
- else :
208
- compiler = AnonymousCompiler
209
- return compiler
215
+
216
+ return comm .bcast (compiler , 0 )
210
217
211
218
212
219
class Compiler (ABC ):
@@ -243,8 +250,8 @@ class Compiler(ABC):
243
250
def __init__ (
244
251
self , extra_compiler_flags = (), extra_linker_flags = (), cpp = False , comm = None
245
252
):
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
248
255
249
256
self ._extra_compiler_flags = tuple (extra_compiler_flags )
250
257
self ._extra_linker_flags = tuple (extra_linker_flags )
@@ -255,6 +262,7 @@ def __init__(
255
262
# Compilation communicators are reference counted on the PyOP2 comm
256
263
self .pcomm = mpi .internal_comm (comm , self )
257
264
self .comm = mpi .compilation_comm (self .pcomm , self )
265
+ self .sniff_compiler_version ()
258
266
259
267
def __repr__ (self ):
260
268
return f"<{ self ._name } compiler, version { self .version or 'unknown' } >"
@@ -307,23 +315,28 @@ def sniff_compiler_version(self, cpp=False):
307
315
:arg cpp: If set to True will use the C++ compiler rather than
308
316
the C compiler to determine the version number.
309
317
"""
318
+ # Note:
319
+ # Sniffing the compiler version for very large numbers of
320
+ # MPI ranks is expensive
310
321
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 )
327
340
328
341
@property
329
342
def bugfix_cflags (self ):
@@ -539,23 +552,6 @@ class LinuxGnuCompiler(Compiler):
539
552
_optflags = ("-march=native" , "-O3" , "-ffast-math" )
540
553
_debugflags = ("-O0" , "-g" )
541
554
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
-
559
555
@property
560
556
def bugfix_cflags (self ):
561
557
"""Flags to work around bugs in compilers."""
0 commit comments