In this page provides some simple examples and snippets to help you get started with GPI-2 and better understand some of its core concepts.
To get an impression of how a GPI-2 program looks like, the following example is the universal Hello World program.
/* GPI-2 (GASPI) header */ #include <GASPI.h> void success_or_exit ( const char* file, const int line, const int ec) { if (ec != GASPI_SUCCESS) { gaspi_printf ("Assertion failed in %s[%i]:%d\n", file, line, ec); exit (EXIT_FAILURE); } } #define ASSERT(ec) success_or_exit (__FILE__, __LINE__, ec); int main(int argc, char *argv[]) { gaspi_rank_t rank, num; gaspi_return_t ret; /* Initialize GPI-2 */ ASSERT( gaspi_proc_init(GASPI_BLOCK) ); /* Get ranks information */ ASSERT( gaspi_proc_rank(&rank) ); ASSERT( gaspi_proc_num(&num) ); gaspi_printf("Hello from rank %d of %d\n", rank, num); /* Terminate */ ASSERT( gaspi_proc_term(GASPI_BLOCK) ); return EXIT_SUCCESS; }
Communication
The following example shows a matrix transpose of a distributed square matrix using gaspi_read.
#include <stdlib.h> #include <GASPI.h> /* helper function */ void wait_if_queue_full ( const gaspi_queue_id_t queue_id , const gaspi_number_t request_size ) { gaspi_number_t queue_size_max; gaspi_number_t queue_size; ASSERT (gaspi_queue_size_max (&queue_size_max)); ASSERT (gaspi_queue_size (queue_id, &queue_size)); if (queue_size + request_size >= queue_size_max) { ASSERT (gaspi_wait (queue_id, GASPI_BLOCK)); } } int main (int argc, char *argv[]) { ASSERT (gaspi_proc_init (GASPI_BLOCK)); gaspi_rank_t iProc; gaspi_rank_t nProc; ASSERT (gaspi_proc_rank (&iProc)); ASSERT (gaspi_proc_num (&nProc)); const gaspi_segment_id_t segment_id_src = 0; const gaspi_segment_id_t segment_id_dst = 1; const gaspi_size_t segment_size = nProc * sizeof(int); /* create 2 segments for data */ ASSERT (gaspi_segment_create ( segment_id_src, segment_size , GASPI_GROUP_ALL, GASPI_BLOCK , GASPI_ALLOC_DEFAULT ) ); ASSERT (gaspi_segment_create ( segment_id_dst, segment_size , GASPI_GROUP_ALL, GASPI_BLOCK , GASPI_ALLOC_DEFAULT ) ); int *src = NULL; int *dst = NULL; /* get initial pointers to each segment */ ASSERT (gaspi_segment_ptr (segment_id_src, &src)); ASSERT (gaspi_segment_ptr (segment_id_dst, &dst)); const gaspi_queue_id_t queue_id = 0; /* initialize source array */ for (int r = 0; r < nProc; ++r) { src[r] = iProc * nProc + r; } /* sync */ ASSERT (gaspi_barrier (GASPI_GROUP_ALL, GASPI_BLOCK)); for (gaspi_rank_t rank = 0; rank < nProc; ++rank) { const gaspi_offset_t offset_src = iProc * sizeof (int); const gaspi_offset_t offset_dst = rank * sizeof (int); wait_if_queue_full (queue_id, 1); ASSERT (gaspi_read ( segment_id_dst, offset_dst , rank, segment_id_src, offset_src , sizeof (int), queue_id, GASPI_BLOCK ) ); } /* we could do something else in between /* overlapping it with the read operations */ /* .... work .... */ ASSERT (gaspi_wait (queue_id, GASPI_BLOCK)); ASSERT (gaspi_barrier (GASPI_GROUP_ALL, GASPI_BLOCK)); ASSERT (gaspi_proc_term (GASPI_BLOCK)); return EXIT_SUCCESS; }
Groups
The following is a small example on using groups. In the example, the first 4 ranks form a group among them for later usage.
Important: Note that only the ranks forming the group (ranks 0 to 3) create the group and add the group ranks (0 to 3) to the group ie. including itself.
gaspi_group_t g; gaspi_rank_t nranks, rank; gaspi_proc_num(&nranks); gaspi_proc_rank(&rank); /* First 4 ranks form a group (assuming we have at least 4 ranks) */ if(rank < 4) { if(gaspi_group_create(&g) == GASPI_SUCCESS) { int r; for(r = 0; r < 4; r++) { if(gaspi_group_add(g, r) != GASPI_SUCCESS) return -1; } if(gaspi_group_commit(g, GASPI_BLOCK) != GASPI_SUCCESS) return -1; } else return -1; }