How can you define the MPI integer data type with the C/C++ uint_fast32_t
or any other integer bit that uses the fast
feature.
Let's assume we work with an MPI implementation conforming to MPI standard version 2.2 or later.
MPI 2.2 and later define signed integer datatypes MPI_INT8_T
, MPI_INT16_T
, MPI_INT32_T
, MPI_INT64_T
(corresponding to C int8_t
, int16_t
, int32_t
, and int64_t
), and unsigned integer datatypes MPI_UINT8_T
, MPI_UINT16_T
, MPI_UINT32_T
, and MPI_UINT64_T
(corresponding to C uint8_t
, uint16_t
, uint32_t
, and uint64_t
).
This means that you can use these specific-size integer types directly in MPI in C.
The situation with minimum-width (int_leastN_t
, uint_leastN_t
) and fastest minimum-width (int_fastN_t
, uint_fastN_t
) integer types is different. A language-lawyer will tell you that you cannot really use these types with MPI, because the C or MPI standards do not provide a clean way to use them.
In practice, the situation is much simpler. All existing C implementations supporting <stdint.h>
types typedef the minimum-width and fastest minimum-width integer types to types compatible with the exact-width types.
Personally, I would create a header file, say extra-mpi-types.h
, that includes the appropriate header file, say
/* extra_mpi_types.h */
#ifndef EXTRA_MPI_TYPES_H
/* Use build-time generated file */
#include <extra_mpi_types_internal.h>
#endif /* EXTRA_MPI_TYPES_H */
where extra_mpi_types_internal.h
is generated at build time by compiling and running something like
/* type_generator.c */
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
static inline const char *mpi_type_name(const char *const name, const size_t size, const int is_signed)
{
if (is_signed) {
if (size == sizeof (int8_t)) return "MPI_INT8_T";
if (size == sizeof (int16_t)) return "MPI_INT16_T";
if (size == sizeof (int32_t)) return "MPI_INT32_T";
if (size == sizeof (int64_t)) return "MPI_INT64_T";
} else {
if (size == sizeof (uint8_t)) return "MPI_UINT8_T";
if (size == sizeof (uint16_t)) return "MPI_UINT16_T";
if (size == sizeof (uint32_t)) return "MPI_UINT32_T";
if (size == sizeof (uint64_t)) return "MPI_UINT64_T";
}
fprintf(stderr, "%s: Unsupported %s integer type.\n", name, (is_signed) ? "signed" : "unsigned");
exit(EXIT_FAILURE);
}
static void define(const char *const mpiname, const char *const typename, const size_t typesize, const int is_signed)
{
printf("#ifndef %s\n", mpiname);
printf("# define %s %s\n", mpiname, mpi_type_name(typename, typesize, is_signed));
printf("#endif\n");
}
#define DEFINE_SIGNED(mpitype, type) define(#mpitype, #type, sizeof (type), 1)
#define DEFINE_UNSIGNED(mpitype, type) define(#mpitype, #type, sizeof (type), 0)
int main(void)
{
printf("/* This is an autogenerated header file: do not modify. */\n\n");
DEFINE_SIGNED(MPI_INT_LEAST8_T, int_least8_t);
DEFINE_SIGNED(MPI_INT_LEAST16_T, int_least16_t);
DEFINE_SIGNED(MPI_INT_LEAST32_T, int_least32_t);
DEFINE_SIGNED(MPI_INT_LEAST64_T, int_least64_t);
DEFINE_UNSIGNED(MPI_UINT_LEAST8_T, uint_least8_t);
DEFINE_UNSIGNED(MPI_UINT_LEAST16_T, uint_least16_t);
DEFINE_UNSIGNED(MPI_UINT_LEAST32_T, uint_least32_t);
DEFINE_UNSIGNED(MPI_UINT_LEAST64_T, uint_least64_t);
DEFINE_SIGNED(MPI_INT_FAST8_T, int_fast8_t);
DEFINE_SIGNED(MPI_INT_FAST16_T, int_fast16_t);
DEFINE_SIGNED(MPI_INT_FAST32_T, int_fast32_t);
DEFINE_SIGNED(MPI_INT_FAST64_T, int_fast64_t);
DEFINE_UNSIGNED(MPI_UINT_FAST8_T, uint_fast8_t);
DEFINE_UNSIGNED(MPI_UINT_FAST16_T, uint_fast16_t);
DEFINE_UNSIGNED(MPI_UINT_FAST32_T, uint_fast32_t);
DEFINE_UNSIGNED(MPI_UINT_FAST64_T, uint_fast64_t);
return EXIT_SUCCESS;
}
redirecting its output to extra_mpi_types_internal.h
. Note that this depends only on the C implementation, and not on the MPI implementation at all. This only finds out which fixed-width integer types match the minimum-width or minimum-width fast integer types.
On x86-64 Linux, this will generate
/* This is an autogenerated header file: do not modify. */
#ifndef MPI_INT_LEAST8_T
# define MPI_INT_LEAST8_T MPI_INT8_T
#endif
#ifndef MPI_INT_LEAST16_T
# define MPI_INT_LEAST16_T MPI_INT16_T
#endif
#ifndef MPI_INT_LEAST32_T
# define MPI_INT_LEAST32_T MPI_INT32_T
#endif
#ifndef MPI_INT_LEAST64_T
# define MPI_INT_LEAST64_T MPI_INT64_T
#endif
#ifndef MPI_UINT_LEAST8_T
# define MPI_UINT_LEAST8_T MPI_UINT8_T
#endif
#ifndef MPI_UINT_LEAST16_T
# define MPI_UINT_LEAST16_T MPI_UINT16_T
#endif
#ifndef MPI_UINT_LEAST32_T
# define MPI_UINT_LEAST32_T MPI_UINT32_T
#endif
#ifndef MPI_UINT_LEAST64_T
# define MPI_UINT_LEAST64_T MPI_UINT64_T
#endif
#ifndef MPI_INT_FAST8_T
# define MPI_INT_FAST8_T MPI_INT8_T
#endif
#ifndef MPI_INT_FAST16_T
# define MPI_INT_FAST16_T MPI_INT64_T
#endif
#ifndef MPI_INT_FAST32_T
# define MPI_INT_FAST32_T MPI_INT64_T
#endif
#ifndef MPI_INT_FAST64_T
# define MPI_INT_FAST64_T MPI_INT64_T
#endif
#ifndef MPI_UINT_FAST8_T
# define MPI_UINT_FAST8_T MPI_UINT8_T
#endif
#ifndef MPI_UINT_FAST16_T
# define MPI_UINT_FAST16_T MPI_UINT64_T
#endif
#ifndef MPI_UINT_FAST32_T
# define MPI_UINT_FAST32_T MPI_UINT64_T
#endif
#ifndef MPI_UINT_FAST64_T
# define MPI_UINT_FAST64_T MPI_UINT64_T
#endif
If you use a Makefile to organize your product, I would use something like
CC := mpicc
CFLAGS := -Wall -O2
LDFLAGS := -lmpi
all: your-main-program
clean:
@rm -f *.o extra_mpi_types_internal.h
type-generator: type-generator.c
$(CC) $(CFLAGS) $^ -o $@
extra_mpi_types_internal.h: type-generator
./type-generator > $@
%.o: %.c extra_mpi_types_internal.h
$(CC) $(CFLAGS) $< -c -o $@
your-main-program: all.o needed.o object.o files.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
although this approach does mean that you cannot cross-compile MPI programs for a different architecture.
Alternatively, you can use pre-defined compiler macros to determine the OS, hardware architecture, and C library used, to include a pre-prepared header file with the correct macro definitions:
/* extra_mpi_types.h */
#ifndef EXTRA_MPI_TYPES_H
#if defined(__linux__)
#if defined(__amd64__)
#include <extra-linux-amd64.h>
#elif defined(__i386__)
#include <extra-linux-x86.h>
#elif defined(__aarch64__)
#include <extra-linux-arm64.h>
#elif defined(__ARM_ARCH_4T__)
#include <extra-linux-arm-4t.h>
#else
#error "Unsupported Linux hardware architecture"
#endif
#elif defined(_WIN64)
#include <extra-win64.h>
#elif defined(_WIN32)
#include <extra-win32.h>
#else
#error Unsupported operating system.
#endif
#endif /* EXTRA_MPI_TYPES_H */
The contents for each of the above files (or rather, the architectures and operating systems as needed), can be either discovered using a C program like above, or by examining the C compiler and library header files.