Blame test/mpi/rma/linked_list.c

Packit Service c5cf8c
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
Packit Service c5cf8c
/*
Packit Service c5cf8c
 *
Packit Service c5cf8c
 *  (C) 2003 by Argonne National Laboratory.
Packit Service c5cf8c
 *      See COPYRIGHT in top-level directory.
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
/*            MPI-3 distributed linked list construction example
Packit Service c5cf8c
 *            --------------------------------------------------
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Construct a distributed shared linked list using proposed MPI-3 dynamic
Packit Service c5cf8c
 * windows.  Initially process 0 creates the head of the list, attaches it to
Packit Service c5cf8c
 * the window, and broadcasts the pointer to all processes.  All processes then
Packit Service c5cf8c
 * concurrently append N new elements to the list.  When a process attempts to
Packit Service c5cf8c
 * attach its element to the tail of list it may discover that its tail pointer
Packit Service c5cf8c
 * is stale and it must chase ahead to the new tail before the element can be
Packit Service c5cf8c
 * attached.
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
#include <stdio.h>
Packit Service c5cf8c
#include <stdlib.h>
Packit Service c5cf8c
#include <mpi.h>
Packit Service c5cf8c
#include <assert.h>
Packit Service c5cf8c
#include "mpitest.h"
Packit Service c5cf8c
Packit Service c5cf8c
#ifdef HAVE_UNISTD_H
Packit Service c5cf8c
#include <unistd.h>
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
#define NUM_ELEMS 32
Packit Service c5cf8c
#define NPROBE    100
Packit Service c5cf8c
#define ELEM_PER_ROW 16
Packit Service c5cf8c
Packit Service c5cf8c
/* Linked list pointer */
Packit Service c5cf8c
typedef struct {
Packit Service c5cf8c
    int rank;
Packit Service c5cf8c
    MPI_Aint disp;
Packit Service c5cf8c
} llist_ptr_t;
Packit Service c5cf8c
Packit Service c5cf8c
/* Linked list element */
Packit Service c5cf8c
typedef struct {
Packit Service c5cf8c
    int value;
Packit Service c5cf8c
    llist_ptr_t next;
Packit Service c5cf8c
} llist_elem_t;
Packit Service c5cf8c
Packit Service c5cf8c
static const llist_ptr_t nil = { -1, (MPI_Aint) MPI_BOTTOM };
Packit Service c5cf8c
Packit Service c5cf8c
static const int verbose = 0;
Packit Service c5cf8c
Packit Service c5cf8c
/* List of locally allocated list elements. */
Packit Service c5cf8c
static llist_elem_t **my_elems = NULL;
Packit Service c5cf8c
static int my_elems_size = 0;
Packit Service c5cf8c
static int my_elems_count = 0;
Packit Service c5cf8c
Packit Service c5cf8c
/* Allocate a new shared linked list element */
Packit Service c5cf8c
MPI_Aint alloc_elem(int value, MPI_Win win)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPI_Aint disp;
Packit Service c5cf8c
    llist_elem_t *elem_ptr;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Allocate the new element and register it with the window */
Packit Service c5cf8c
    MPI_Alloc_mem(sizeof(llist_elem_t), MPI_INFO_NULL, &elem_ptr);
Packit Service c5cf8c
    elem_ptr->value = value;
Packit Service c5cf8c
    elem_ptr->next = nil;
Packit Service c5cf8c
    MPI_Win_attach(win, elem_ptr, sizeof(llist_elem_t));
Packit Service c5cf8c
Packit Service c5cf8c
    /* Add the element to the list of local elements so we can free it later. */
Packit Service c5cf8c
    if (my_elems_size == my_elems_count) {
Packit Service c5cf8c
        my_elems_size += 100;
Packit Service c5cf8c
        my_elems = realloc(my_elems, my_elems_size * sizeof(void *));
Packit Service c5cf8c
    }
Packit Service c5cf8c
    my_elems[my_elems_count] = elem_ptr;
Packit Service c5cf8c
    my_elems_count++;
Packit Service c5cf8c
Packit Service c5cf8c
    MPI_Get_address(elem_ptr, &disp;;
Packit Service c5cf8c
    return disp;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
int main(int argc, char **argv)
Packit Service c5cf8c
{
Packit Service c5cf8c
    int procid, nproc, i;
Packit Service c5cf8c
    MPI_Win llist_win;
Packit Service c5cf8c
    llist_ptr_t head_ptr, tail_ptr;
Packit Service c5cf8c
    int errs = 0;
Packit Service c5cf8c
Packit Service c5cf8c
    MTest_Init(&argc, &argv);
Packit Service c5cf8c
Packit Service c5cf8c
    MPI_Comm_rank(MPI_COMM_WORLD, &procid);
Packit Service c5cf8c
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
Packit Service c5cf8c
Packit Service c5cf8c
    MPI_Win_create_dynamic(MPI_INFO_NULL, MPI_COMM_WORLD, &llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Process 0 creates the head node */
Packit Service c5cf8c
    if (procid == 0)
Packit Service c5cf8c
        head_ptr.disp = alloc_elem(-1, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Broadcast the head pointer to everyone */
Packit Service c5cf8c
    head_ptr.rank = 0;
Packit Service c5cf8c
    MPI_Bcast(&head_ptr.disp, 1, MPI_AINT, 0, MPI_COMM_WORLD);
Packit Service c5cf8c
    tail_ptr = head_ptr;
Packit Service c5cf8c
Packit Service c5cf8c
    /* All processes concurrently append NUM_ELEMS elements to the list */
Packit Service c5cf8c
    for (i = 0; i < NUM_ELEMS; i++) {
Packit Service c5cf8c
        llist_ptr_t new_elem_ptr;
Packit Service c5cf8c
        int success;
Packit Service c5cf8c
Packit Service c5cf8c
        /* Create a new list element and register it with the window */
Packit Service c5cf8c
        new_elem_ptr.rank = procid;
Packit Service c5cf8c
        new_elem_ptr.disp = alloc_elem(procid, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
        /* Append the new node to the list.  This might take multiple attempts if
Packit Service c5cf8c
         * others have already appended and our tail pointer is stale. */
Packit Service c5cf8c
        do {
Packit Service c5cf8c
            llist_ptr_t next_tail_ptr = nil;
Packit Service c5cf8c
Packit Service c5cf8c
            MPI_Win_lock(MPI_LOCK_EXCLUSIVE, tail_ptr.rank, 0, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
            MPI_Compare_and_swap((void *) &new_elem_ptr.rank, (void *) &nil.rank,
Packit Service c5cf8c
                                 (void *) &next_tail_ptr.rank, MPI_INT, tail_ptr.rank,
Packit Service c5cf8c
                                 (MPI_Aint) & (((llist_elem_t *) tail_ptr.disp)->next.rank),
Packit Service c5cf8c
                                 llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
            MPI_Win_unlock(tail_ptr.rank, llist_win);
Packit Service c5cf8c
            success = (next_tail_ptr.rank == nil.rank);
Packit Service c5cf8c
Packit Service c5cf8c
            if (success) {
Packit Service c5cf8c
                int i, flag;
Packit Service c5cf8c
Packit Service c5cf8c
                MPI_Win_lock(MPI_LOCK_EXCLUSIVE, tail_ptr.rank, 0, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
                MPI_Put(&new_elem_ptr.disp, 1, MPI_AINT, tail_ptr.rank,
Packit Service c5cf8c
                        (MPI_Aint) & (((llist_elem_t *) tail_ptr.disp)->next.disp), 1,
Packit Service c5cf8c
                        MPI_AINT, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
                MPI_Win_unlock(tail_ptr.rank, llist_win);
Packit Service c5cf8c
                tail_ptr = new_elem_ptr;
Packit Service c5cf8c
Packit Service c5cf8c
                /* For implementations that use pt-to-pt messaging, force progress for other threads'
Packit Service c5cf8c
                 * RMA operations. */
Packit Service c5cf8c
                for (i = 0; i < NPROBE; i++)
Packit Service c5cf8c
                    MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag,
Packit Service c5cf8c
                               MPI_STATUS_IGNORE);
Packit Service c5cf8c
Packit Service c5cf8c
            } else {
Packit Service c5cf8c
                /* Tail pointer is stale, fetch the displacement.  May take multiple tries
Packit Service c5cf8c
                 * if it is being updated. */
Packit Service c5cf8c
                do {
Packit Service c5cf8c
                    MPI_Win_lock(MPI_LOCK_EXCLUSIVE, tail_ptr.rank, 0, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
                    MPI_Get(&next_tail_ptr.disp, 1, MPI_AINT, tail_ptr.rank,
Packit Service c5cf8c
                            (MPI_Aint) & (((llist_elem_t *) tail_ptr.disp)->next.disp),
Packit Service c5cf8c
                            1, MPI_AINT, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
                    MPI_Win_unlock(tail_ptr.rank, llist_win);
Packit Service c5cf8c
                } while (next_tail_ptr.disp == nil.disp);
Packit Service c5cf8c
                tail_ptr = next_tail_ptr;
Packit Service c5cf8c
            }
Packit Service c5cf8c
        } while (!success);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    MPI_Barrier(MPI_COMM_WORLD);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Traverse the list and verify that all processes inserted exactly the correct
Packit Service c5cf8c
     * number of elements. */
Packit Service c5cf8c
    if (procid == 0) {
Packit Service c5cf8c
        int have_root = 0;
Packit Service c5cf8c
        int *counts, count = 0;
Packit Service c5cf8c
Packit Service c5cf8c
        counts = (int *) malloc(sizeof(int) * nproc);
Packit Service c5cf8c
        assert(counts != NULL);
Packit Service c5cf8c
Packit Service c5cf8c
        for (i = 0; i < nproc; i++)
Packit Service c5cf8c
            counts[i] = 0;
Packit Service c5cf8c
Packit Service c5cf8c
        tail_ptr = head_ptr;
Packit Service c5cf8c
Packit Service c5cf8c
        /* Walk the list and tally up the number of elements inserted by each rank */
Packit Service c5cf8c
        while (tail_ptr.disp != nil.disp) {
Packit Service c5cf8c
            llist_elem_t elem;
Packit Service c5cf8c
Packit Service c5cf8c
            MPI_Win_lock(MPI_LOCK_EXCLUSIVE, tail_ptr.rank, 0, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
            MPI_Get(&elem, sizeof(llist_elem_t), MPI_BYTE,
Packit Service c5cf8c
                    tail_ptr.rank, tail_ptr.disp, sizeof(llist_elem_t), MPI_BYTE, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
            MPI_Win_unlock(tail_ptr.rank, llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
            tail_ptr = elem.next;
Packit Service c5cf8c
Packit Service c5cf8c
            /* This is not the root */
Packit Service c5cf8c
            if (have_root) {
Packit Service c5cf8c
                assert(elem.value >= 0 && elem.value < nproc);
Packit Service c5cf8c
                counts[elem.value]++;
Packit Service c5cf8c
                count++;
Packit Service c5cf8c
Packit Service c5cf8c
                if (verbose) {
Packit Service c5cf8c
                    int last_elem = tail_ptr.disp == nil.disp;
Packit Service c5cf8c
                    printf("%2d%s", elem.value, last_elem ? "" : " -> ");
Packit Service c5cf8c
                    if (count % ELEM_PER_ROW == 0 && !last_elem)
Packit Service c5cf8c
                        printf("\n");
Packit Service c5cf8c
                }
Packit Service c5cf8c
            }
Packit Service c5cf8c
Packit Service c5cf8c
            /* This is the root */
Packit Service c5cf8c
            else {
Packit Service c5cf8c
                assert(elem.value == -1);
Packit Service c5cf8c
                have_root = 1;
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
Packit Service c5cf8c
        if (verbose)
Packit Service c5cf8c
            printf("\n\n");
Packit Service c5cf8c
Packit Service c5cf8c
        /* Verify the counts we collected */
Packit Service c5cf8c
        for (i = 0; i < nproc; i++) {
Packit Service c5cf8c
            int expected = NUM_ELEMS;
Packit Service c5cf8c
Packit Service c5cf8c
            if (counts[i] != expected) {
Packit Service c5cf8c
                printf("Error: Rank %d inserted %d elements, expected %d\n", i, counts[i],
Packit Service c5cf8c
                       expected);
Packit Service c5cf8c
                errs++;
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
Packit Service c5cf8c
        free(counts);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    MPI_Win_free(&llist_win);
Packit Service c5cf8c
Packit Service c5cf8c
    /* Free all the elements in the list */
Packit Service c5cf8c
    for (; my_elems_count > 0; my_elems_count--)
Packit Service c5cf8c
        MPI_Free_mem(my_elems[my_elems_count - 1]);
Packit Service c5cf8c
Packit Service c5cf8c
    MTest_Finalize(errs);
Packit Service c5cf8c
    return MTestReturnValue(errs);
Packit Service c5cf8c
}