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;
}