2004-08-21 04:49:07 +04:00
|
|
|
/*
|
2004-11-22 04:38:40 +03:00
|
|
|
* Copyright (c) 2004-2005 The Trustees of Indiana University.
|
|
|
|
* All rights reserved.
|
|
|
|
* Copyright (c) 2004-2005 The Trustees of the University of Tennessee.
|
|
|
|
* All rights reserved.
|
2004-11-28 23:09:25 +03:00
|
|
|
* Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
|
|
|
|
* University of Stuttgart. All rights reserved.
|
2005-03-24 15:43:37 +03:00
|
|
|
* Copyright (c) 2004-2005 The Regents of the University of California.
|
|
|
|
* All rights reserved.
|
2004-11-22 04:38:40 +03:00
|
|
|
* $COPYRIGHT$
|
|
|
|
*
|
|
|
|
* Additional copyrights may follow
|
|
|
|
*
|
2004-08-21 04:49:07 +04:00
|
|
|
* $HEADER$
|
|
|
|
*/
|
2005-09-02 16:57:47 +04:00
|
|
|
/** @file */
|
2004-08-21 04:49:07 +04:00
|
|
|
|
|
|
|
#include "ompi_config.h"
|
|
|
|
|
2005-08-13 01:42:07 +04:00
|
|
|
#include "ompi/include/constants.h"
|
2005-09-02 16:57:47 +04:00
|
|
|
#include "ompi/communicator/communicator.h"
|
|
|
|
#include "ompi/datatype/convertor.h"
|
|
|
|
#include "ompi/mca/coll/coll.h"
|
|
|
|
#include "opal/include/sys/atomic.h"
|
2004-08-21 04:49:07 +04:00
|
|
|
#include "coll_sm.h"
|
|
|
|
|
2005-09-02 16:57:47 +04:00
|
|
|
/**
|
|
|
|
* Shared memory broadcast.
|
|
|
|
*
|
2005-09-07 19:46:33 +04:00
|
|
|
* For the root, the general algorithm is to wait for a set of
|
|
|
|
* segments to become available. Once it is, the root claims the set
|
|
|
|
* by writing the current operation number and the number of processes
|
|
|
|
* using the set to the flag. The root then loops over the set of
|
|
|
|
* segments; for each segment, it copies a fragment of the user's
|
|
|
|
* buffer into the shared data segment and then writes the data size
|
|
|
|
* into its childrens' control buffers. The process is repeated until
|
2005-09-02 16:57:47 +04:00
|
|
|
* all fragments have been written.
|
2004-08-21 04:49:07 +04:00
|
|
|
*
|
2005-09-07 19:46:33 +04:00
|
|
|
* For non-roots, for each set of buffers, they wait until the current
|
|
|
|
* operation number appears in the in-use flag (i.e., written by the
|
|
|
|
* root). Then for each segment, they wait for a nonzero to appear
|
|
|
|
* into their control buffers. If they have children, they copy the
|
|
|
|
* data from their parent's shared data segment into their shared data
|
|
|
|
* segment, and write the data size into each of their childrens'
|
|
|
|
* control buffers. They then copy the data from their shared [local]
|
|
|
|
* data segment into the user's output buffer. The process is
|
|
|
|
* repeated until all fragments have been received. If they do not
|
|
|
|
* have children, they copy the data directly from the parent's shared
|
|
|
|
* data segment into the user's output buffer.
|
2004-08-21 04:49:07 +04:00
|
|
|
*/
|
2005-01-30 04:42:57 +03:00
|
|
|
int mca_coll_sm_bcast_intra(void *buff, int count,
|
|
|
|
struct ompi_datatype_t *datatype, int root,
|
|
|
|
struct ompi_communicator_t *comm)
|
2004-08-21 04:49:07 +04:00
|
|
|
{
|
2005-09-02 16:57:47 +04:00
|
|
|
struct iovec iov;
|
|
|
|
mca_coll_base_comm_t *data = comm->c_coll_selected_data;
|
2005-09-07 17:33:43 +04:00
|
|
|
int i, ret, rank, size, num_children, src_rank;
|
2005-09-07 01:41:55 +04:00
|
|
|
int flag_num, segment_num, max_segment_num;
|
2005-09-07 17:33:43 +04:00
|
|
|
int parent_rank;
|
2005-09-02 16:57:47 +04:00
|
|
|
size_t total_size, max_data, bytes;
|
2005-09-07 01:41:55 +04:00
|
|
|
mca_coll_sm_in_use_flag_t *flag;
|
|
|
|
ompi_convertor_t convertor;
|
2005-09-02 16:57:47 +04:00
|
|
|
mca_coll_sm_tree_node_t *me, *parent, **children;
|
2005-09-07 17:33:43 +04:00
|
|
|
mca_coll_base_mpool_index_t *index;
|
2005-09-02 16:57:47 +04:00
|
|
|
|
|
|
|
/* Setup some identities */
|
|
|
|
|
|
|
|
rank = ompi_comm_rank(comm);
|
|
|
|
size = ompi_comm_size(comm);
|
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
OBJ_CONSTRUCT(&convertor, ompi_convertor_t);
|
|
|
|
iov.iov_len = mca_coll_sm_component.sm_fragment_size;
|
|
|
|
bytes = 0;
|
|
|
|
|
|
|
|
me = &data->mcb_tree[(rank + size - root) % size];
|
|
|
|
D(("rank %d: virtual rank %d\n", rank, me - data->mcb_tree));
|
2005-09-02 16:57:47 +04:00
|
|
|
parent = me->mcstn_parent;
|
|
|
|
children = me->mcstn_children;
|
|
|
|
num_children = me->mcstn_num_children;
|
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/* Only have one top-level decision as to whether I'm the root or
|
|
|
|
not. Do this at the slight expense of repeating a little logic
|
|
|
|
-- but it's better than a conditional branch in every loop
|
|
|
|
iteration. */
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* Root
|
|
|
|
*********************************************************************/
|
2005-09-02 16:57:47 +04:00
|
|
|
|
|
|
|
if (root == rank) {
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* The root needs a send convertor to pack from the user's
|
|
|
|
buffer to shared memory */
|
|
|
|
|
2005-09-02 16:57:47 +04:00
|
|
|
if (OMPI_SUCCESS !=
|
|
|
|
(ret =
|
|
|
|
ompi_convertor_copy_and_prepare_for_send(ompi_mpi_local_convertor,
|
|
|
|
datatype,
|
|
|
|
count,
|
|
|
|
buff,
|
2005-09-07 01:41:55 +04:00
|
|
|
&convertor))) {
|
2005-09-02 16:57:47 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2005-09-07 01:41:55 +04:00
|
|
|
ompi_convertor_get_packed_size(&convertor, &total_size);
|
2005-09-02 16:57:47 +04:00
|
|
|
D(("root got send convertor w/ total_size == %lu\n",
|
|
|
|
(unsigned long) total_size));
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Main loop over sending fragments */
|
|
|
|
|
|
|
|
do {
|
|
|
|
flag_num = (data->mcb_operation_count++ %
|
|
|
|
mca_coll_sm_component.sm_comm_num_in_use_flags);
|
|
|
|
|
2005-09-07 17:33:43 +04:00
|
|
|
FLAG_SETUP(flag_num, flag, data);
|
|
|
|
FLAG_WAIT_FOR_IDLE(flag);
|
|
|
|
FLAG_RETAIN(flag, size - 1, data->mcb_operation_count - 1);
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Loop over all the segments in this set */
|
|
|
|
|
2005-10-01 03:12:23 +04:00
|
|
|
segment_num =
|
|
|
|
flag_num * mca_coll_sm_component.sm_segs_per_inuse_flag;
|
|
|
|
max_segment_num =
|
|
|
|
(flag_num + 1) * mca_coll_sm_component.sm_segs_per_inuse_flag;
|
2005-09-07 01:41:55 +04:00
|
|
|
do {
|
2005-09-07 17:33:43 +04:00
|
|
|
index = &(data->mcb_mpool_index[segment_num]);
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Copy the fragment from the user buffer to my fragment
|
|
|
|
in the current segment */
|
2005-09-15 06:18:16 +04:00
|
|
|
COPY_FRAGMENT_IN(convertor, index, rank, iov, max_data);
|
2005-09-07 01:41:55 +04:00
|
|
|
bytes += max_data;
|
|
|
|
|
|
|
|
/* Wait for the write to absolutely complete */
|
|
|
|
opal_atomic_wmb();
|
|
|
|
|
2005-09-07 17:33:43 +04:00
|
|
|
/* Tell my children that this fragment is ready */
|
|
|
|
PARENT_NOTIFY_CHILDREN(children, num_children, index,
|
|
|
|
max_data);
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
++segment_num;
|
|
|
|
} while (bytes < total_size && segment_num < max_segment_num);
|
|
|
|
} while (bytes < total_size);
|
2005-09-02 16:57:47 +04:00
|
|
|
}
|
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/*********************************************************************
|
|
|
|
* Non-root
|
|
|
|
*********************************************************************/
|
2005-09-02 16:57:47 +04:00
|
|
|
|
|
|
|
else {
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Non-root processes need a receive convertor to unpack from
|
|
|
|
shared mmory to the user's buffer */
|
|
|
|
|
2005-09-02 16:57:47 +04:00
|
|
|
if (OMPI_SUCCESS !=
|
|
|
|
(ret =
|
|
|
|
ompi_convertor_copy_and_prepare_for_recv(ompi_mpi_local_convertor,
|
|
|
|
datatype,
|
|
|
|
count,
|
|
|
|
buff,
|
2005-09-07 01:41:55 +04:00
|
|
|
&convertor))) {
|
2005-09-02 16:57:47 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2005-09-07 01:41:55 +04:00
|
|
|
ompi_convertor_get_packed_size(&convertor, &total_size);
|
2005-09-02 16:57:47 +04:00
|
|
|
D(("rank %d got recv convertor w/ total_size == %lu\n",
|
|
|
|
rank, (unsigned long) total_size));
|
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/* Loop over receiving (and possibly re-sending) the
|
|
|
|
fragments */
|
|
|
|
|
|
|
|
do {
|
|
|
|
flag_num = (data->mcb_operation_count %
|
|
|
|
mca_coll_sm_component.sm_comm_num_in_use_flags);
|
2005-09-02 16:57:47 +04:00
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/* Wait for the root to mark this set of segments as
|
|
|
|
ours */
|
2005-09-07 17:33:43 +04:00
|
|
|
FLAG_SETUP(flag_num, flag, data);
|
|
|
|
FLAG_WAIT_FOR_OP(flag, data->mcb_operation_count);
|
2005-09-07 01:41:55 +04:00
|
|
|
++data->mcb_operation_count;
|
2005-09-03 15:49:46 +04:00
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/* Loop over all the segments in this set */
|
2005-09-02 16:57:47 +04:00
|
|
|
|
2005-10-01 03:12:23 +04:00
|
|
|
segment_num =
|
|
|
|
flag_num * mca_coll_sm_component.sm_segs_per_inuse_flag;
|
|
|
|
max_segment_num =
|
|
|
|
(flag_num + 1) * mca_coll_sm_component.sm_segs_per_inuse_flag;
|
2005-09-07 01:41:55 +04:00
|
|
|
do {
|
2005-09-02 16:57:47 +04:00
|
|
|
|
2005-09-07 17:33:43 +04:00
|
|
|
/* Pre-calculate some values */
|
2005-09-07 01:41:55 +04:00
|
|
|
parent_rank = (parent->mcstn_id + root) % size;
|
2005-09-07 17:33:43 +04:00
|
|
|
index = &(data->mcb_mpool_index[segment_num]);
|
|
|
|
|
|
|
|
/* Wait for my parent to tell me that the segment is ready */
|
|
|
|
CHILD_WAIT_FOR_NOTIFY(rank, index, max_data);
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* If I have children, send the data to them */
|
|
|
|
if (num_children > 0) {
|
2005-09-07 17:33:43 +04:00
|
|
|
/* Copy the fragment from the parent's portion in
|
|
|
|
the segment to my portion in the segment. */
|
|
|
|
COPY_FRAGMENT_BETWEEN(parent_rank, rank, index, max_data);
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Wait for the write to absolutely complete */
|
|
|
|
opal_atomic_wmb();
|
|
|
|
|
|
|
|
/* Tell my children that this fragment is ready */
|
2005-09-07 17:33:43 +04:00
|
|
|
PARENT_NOTIFY_CHILDREN(children, num_children, index,
|
|
|
|
max_data);
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Set the "copy from buffer" to be my local
|
|
|
|
segment buffer so that we don't potentially
|
|
|
|
incur a non-local memory copy from the parent's
|
|
|
|
fan out data segment [again] when copying to
|
|
|
|
the user's buffer */
|
2005-09-07 17:33:43 +04:00
|
|
|
src_rank = rank;
|
2005-09-07 01:41:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If I don't have any children, set the "copy from
|
|
|
|
buffer" to be my parent's fan out segment to copy
|
|
|
|
directly from my parent */
|
|
|
|
|
|
|
|
else {
|
2005-09-07 17:33:43 +04:00
|
|
|
src_rank = parent_rank;
|
2005-09-07 01:41:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy to my output buffer */
|
2005-09-07 17:33:43 +04:00
|
|
|
COPY_FRAGMENT_OUT(convertor, src_rank, index, iov, max_data);
|
2005-09-02 16:57:47 +04:00
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
bytes += max_data;
|
|
|
|
++segment_num;
|
|
|
|
} while (bytes < total_size && segment_num < max_segment_num);
|
2005-09-02 16:57:47 +04:00
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/* Wait for all copy-out writes to complete before I say
|
|
|
|
I'm done with the segments */
|
|
|
|
opal_atomic_wmb();
|
2005-09-02 16:57:47 +04:00
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
/* We're finished with this set of segments */
|
2005-09-07 17:33:43 +04:00
|
|
|
FLAG_RELEASE(flag);
|
2005-09-07 01:41:55 +04:00
|
|
|
} while (bytes < total_size);
|
2005-09-02 16:57:47 +04:00
|
|
|
}
|
2005-09-07 01:41:55 +04:00
|
|
|
|
|
|
|
/* Kill the convertor */
|
|
|
|
|
|
|
|
OBJ_DESTRUCT(&convertor);
|
2005-09-02 16:57:47 +04:00
|
|
|
|
|
|
|
/* All done */
|
|
|
|
|
2005-09-07 01:41:55 +04:00
|
|
|
D(("rank %d done with bcast\n", rank));
|
2005-09-02 16:57:47 +04:00
|
|
|
return OMPI_SUCCESS;
|
2004-08-21 04:49:07 +04:00
|
|
|
}
|