Examples

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.

Hello World
Communication

Groups

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

Timeouts

Failure tolerant parallel programs require non-blocking communication calls. GPI-2 provides a timeout mechanism for all potentially blocking procedures. Timeouts for procedures are specified in milliseconds.

Segments

GASPI segments may be globally accessible from every thread of every rank. GASPI segments can also be used to leverage different memory models within a single application or to even run different applications.

Atomics

GPI-2 provides atomic operations such that variables can be manipulated atomically. There are two basic atomic operations: fetch_and_add and compare_and_swap. The values can be used as global shared variables and to synchronise processes or events.

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