Berkeley UPC - Unified Parallel C |
The UPC Specification does not address language interoperability issues, and so the interfaces described here should be considered Berkeley UPC extensions.
NOTE: if -pthreads is used to generate UPC executables, differences emerge between C and UPC global variables, and function calls become the only interface from other languages into UPC. See the Programming Hybrid Applications with Pthreads section.
The 'main()' function can live either in UPC code, or in an object written in another language. If 'main' is in a UPC file, the UPC runtime is bootstrapped normally, and no special logic is needed at program exit.
If 'main()' does not live in UPC code, 'bupc_init()' or 'bupc_init_reentrant()' must be called at startup to bootstrap the Berkeley UPC runtime, and 'bupc_exit()' should be used at program exit. These functions are available by #including <bupc_extern.h>:
#include <bupc_extern.h> int main(int argc, char** argv) { int exitcode = 0; bupc_init(&argc, &argv); ... rest of program... bupc_exit(exitcode); }The 'bupc_init_reentrant()' function must be used when '-pthreads' is used. It also supports non-pthreaded applications, and so it is preferred for portability.
The call to 'bupc_init()' (or 'bupc_init_reentrant()') should be the first statement in 'main()'. The semantics of any code appearing before it is implementation-defined (for example, it is undefined how many threads of control will run that code, or whether stdin/stdout/stderr are functional). Similarly, no code should follow a call to 'bupc_exit()': it is not defined if such code will even be reached (hint: it won't). See the comments in <bupc_extern.h> for more information.
If regular 'exit()' (or a return from main) is performed from non-UPC code, instead of 'bupc_exit()', the result is as if 'upc_global_exit()' had been called--i.e., program termination will occur immediately, for all threads, without the final UPC barrier that occurs during normal UPC program termination. Calling '_exit' is strongly discouraged--program behavior is undefined in this case, and even process cleanup is not guaranteed (i.e. zombie processes may be left behind).
The path needed to include <bupc_extern.h> is always available by calling 'upcc -print-include-dir'. Thus a C file that #includes <bupc_extern.h> can be portably compiled with
gcc -I`upcc -print-include-dir` -c main.c
Finally, whenever main() is not declared within a .upc file, the call to link UPC and non-UPC objects together must use the '--extern-main' flag:
upcc foo.upc upcc --extern-main foo.o main.o
To have C code refer to a UPC variable or call a UPC function, have the C file #include a header file that declares the UPC variables/functions (none of which may contain any UPC-specific constructs, like 'strict'), and then the C code can refer to them normally. You may use the '__UPC__' macro (always defined during UPC compilation) to hide UPC-specific constructs:
#ifndef MY_UPC_HEADER_H #define MY_UPC_HEADER_H /* Variables/Routines exported to C routines */ extern int foo; int get_bar(); void set_bar(int newval); /* UPC-specific declarations */ #ifdef __UPC__ extern shared int bar; extern strict shared long myarray[THREADS * 2]; #endif /* __UPC__ */ #endif /* MY_UPC_HEADER_H */
NOTE: if -pthreads is used to generate UPC executables, differences emerge between C and UPC global variables, and function calls become the only interface from other languages into UPC. See the Programming Hybrid Applications with Pthreads section.
You can compile C code with any C compiler that is ABI-compatible with the one used by Berkeley UPC (use 'upcc -version' to see which compiler upcc uses). Then pass both your C and UPC object files (and/or any C libraries you wish to use) to upcc to link:
upcc -c foo.upc gcc -c bar.c upcc foo.o bar.o -lm upcrun -n 2 a.out
#ifndef MY_UPC_HEADER_H #define MY_UPC_HEADER_H /* Variables/Routines exported to C/C++ routines */ #ifdef __cplusplus extern "C" { #endif extern int foo; int get_bar(); void set_bar(int newval); #ifdef __cplusplus } /* end "extern" */ #endif /* UPC-specific declarations */ #ifdef __UPC__ extern shared int bar; extern strict shared long myarray[THREADS * 2]; #endif /* __UPC__ */ #endif /* MY_UPC_HEADER_H */
Similarly, a C++ header that is also #included by UPC code would need to use the same extern "C" wrapper around any symbols intended for use by UPC, and any C++-specific constructs would need to be hidden from UPC by putting them within an '#ifdef __cplusplus' block.
NOTE: if -pthreads is used to generate UPC executables, differences emerge between C and UPC global variables, and function calls become the only interface from other languages into UPC. See the Programming Hybrid Applications with Pthreads section.
You can compile C++ code with any C++ compiler that is ABI-compatible with the C compiler used by Berkeley UPC. At link time, 'upcc' additionally needs to be pointed at the C++ linker that should be used for the final link step: use the '-link-with' flag for this:
upcc -c foo.upc g++ -c bar.cc upcc -link-with=g++ foo.o bar.o -lsomeC++library upcrun -n 2 a.out
Exceptions:
NOTE: if -pthreads is used to generate UPC executables, differences emerge between C and UPC global variables, and function calls become the only interface from other languages into UPC. See the Programming Hybrid Applications with Pthreads section.
As with C objects, you should generally be able to simply pass FORTRAN objects/libraries to the upcc linker:
upcc -c foo.upc f77 -c bar.f upcc foo.o bar.o -lsomeFortranLibrary upcrun -n 2 a.outNote: different systems use different methods for linking together C and FORTRAN. You may need to consult your system documentation. You may select a different linker for upcc to use via the '-link-with' flag, and/or pass any linker-specific flags needed via upcc's '-Wl,' flag. See the upcc man page.
Note: at present, compiling mixed MPI/UPC applications requires that the Berkeley UPC runtime be configured and built with 'CC' and 'MPI_CC' set to a C MPI compiler (and 'CXX' set to a C++ MPI compiler, unless '--disable-udp' is passed). Such a runtime will always use an MPI compiler to compile UPC programs, even those which do not use MPI: you may thus wish to keep a separate runtime installation specifically for compiling MPI/UPC programs. Finally, MPI interoperability is not provided for all systems and network types, and '-pthreads' is not supported. See the 'INSTALL.TXT' document in the runtime for more information.
UPC files which contain calls to MPI functions must '#include <mpi.h>', and must be compiled with the '-uses-mpi' flag. If any objects in a UPC application contain calls to MPI, the flag must be passed to the upcc at link time:
upcc -uses-mpi -c foo.upc mpicc -c bar.c upcc -uses-mpi foo.o bar.o
On some systems, '-lmpi' must also be passed to 'upcc' in order for MPI symbols to be resolved. If C++ MPI objects are linked into a UPC application, the '-link-with' flag must be additionaly used at link time to point upcc at a C++ MPI compiler to use for linking.
upcc -uses-mpi -c foo.upc mpic++ -c bar.cc upcc -uses-mpi -link-with=mpic++ foo.o bar.o
When '-uses-mpi' is used to link a UPC application, the 'MPI_Init()' and 'MPI_Finalize()' calls are handled by the UPC runtime--these calls should not appear in client code. If 'main()' exists in a non-UPC file, calls to 'bupc_init()' and 'bupc_exit()' should replace any calls to the MPI init/finalize functions.
Certain UPC network types (notably 'mpi', 'ibv' and 'ofi') may use MPI under the covers. For this reason, non-UPC MPI objects should be compiled with the same MPI compiler family as is used by upcc. To see the MPI C compiler used by upcc, use 'upcc -print-mpicc'.
Both MPI and UPC cause network communication. At present, the network traffic generated by MPI is not coordinated with that generated by UPC code. As a result, it is quite easy to cause network deadlock when mixing MPI and UPC, unless the following protocol is strictly observed:
upcc -c upc.upc gcc -c c.c g++ -c c++.cc mpiCC -c mpi.cc f77 -c fortran.f upcc -uses-mpi -link-with=mpiCC upc.o c.o c++.o mpi.o fortran.o -lFortranLib -lC++lib -lClib upcrun -n 2 a.out
The result of this is that when '-pthreads' is used, C/C++/FORTRAN code can not refer to UPC global variables. However, UPC functions which have a C interface (no UPC-specific constructs) can still be called. Note, though, that UPC functions should only be called from pthreads that were created by the UPC runtime: pthreads created by user calls to 'pthread_create' cannot safely call UPC routines.
When '-pthreads' is used in a hybrid application, any non-UPC code linked into the application must be thread-safe (i.e. capable of being referenced by multiple threads at the same time), if you plan to call it concurrently from more than one UPC thread.
Many MPI libraries are not safe to use with pthreads, and those that are often require special initialization. At present Berkeley UPC does not support hybrid MPI/UPC applications with '-pthreads', but support may be added for reentrant MPI libraries in the future.
See the Using Pthreads section of the UPC User's Manual for more details on '-pthreads' usage.
This page last modified on Friday, 28-Oct-2022 15:49:55 PDT