/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */ /* * * (C) 2001 by Argonne National Laboratory. * See COPYRIGHT in top-level directory. */ #include "mpiimpl.h" static int unweighted_dummy = 0x46618; static int weights_empty_dummy = 0x022284; /* cannot ==NULL, would be ambiguous */ int *const MPI_UNWEIGHTED = &unweighted_dummy; int *const MPI_WEIGHTS_EMPTY = &weights_empty_dummy; /* Keyval for topology information */ static int MPIR_Topology_keyval = MPI_KEYVAL_INVALID; /* Local functions */ static int MPIR_Topology_copy_fn(MPI_Comm, int, void *, void *, void *, int *); static int MPIR_Topology_delete_fn(MPI_Comm, int, void *, void *); static int MPIR_Topology_finalize(void *); /* Return a poiner to the topology structure on a communicator. Returns null if no topology structure is defined */ MPIR_Topology *MPIR_Topology_get(MPIR_Comm * comm_ptr) { int mpi_errno = MPI_SUCCESS; MPIR_Topology *topo_ptr; int flag; if (MPIR_Topology_keyval == MPI_KEYVAL_INVALID) { return 0; } mpi_errno = MPII_Comm_get_attr(comm_ptr->handle, MPIR_Topology_keyval, &topo_ptr, &flag, MPIR_ATTR_PTR); if (mpi_errno) return NULL; if (flag) return topo_ptr; return NULL; } #undef FUNCNAME #define FUNCNAME MPIR_Topology_put #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) int MPIR_Topology_put(MPIR_Comm * comm_ptr, MPIR_Topology * topo_ptr) { int mpi_errno = MPI_SUCCESS; MPIR_Assert(comm_ptr != NULL); if (MPIR_Topology_keyval == MPI_KEYVAL_INVALID) { /* Create a new keyval */ /* FIXME - thread safe code needs a thread lock here, followed * by another test on the keyval to see if a different thread * got there first */ mpi_errno = MPIR_Comm_create_keyval_impl(MPIR_Topology_copy_fn, MPIR_Topology_delete_fn, &MPIR_Topology_keyval, 0); /* Register the finalize handler */ if (mpi_errno) MPIR_ERR_POP(mpi_errno); MPIR_Add_finalize(MPIR_Topology_finalize, (void *) 0, MPIR_FINALIZE_CALLBACK_PRIO - 1); } mpi_errno = MPIR_Comm_set_attr_impl(comm_ptr, MPIR_Topology_keyval, topo_ptr, MPIR_ATTR_PTR); if (mpi_errno) MPIR_ERR_POP(mpi_errno); fn_exit: return mpi_errno; fn_fail: goto fn_exit; } /* Ignore p */ static int MPIR_Topology_finalize(void *p ATTRIBUTE((unused))) { MPL_UNREFERENCED_ARG(p); if (MPIR_Topology_keyval != MPI_KEYVAL_INVALID) { /* Just in case */ MPIR_Comm_free_keyval_impl(MPIR_Topology_keyval); MPIR_Topology_keyval = MPI_KEYVAL_INVALID; } return 0; } static int *MPIR_Copy_array(int n, const int a[], int *err) { int *new_p; /* the copy of NULL is NULL */ if (a == NULL) { MPIR_Assert(n == 0); return NULL; } new_p = (int *) MPL_malloc(n * sizeof(int), MPL_MEM_OTHER); /* --BEGIN ERROR HANDLING-- */ if (!new_p) { *err = MPI_ERR_OTHER; return 0; } /* --END ERROR HANDLING-- */ MPIR_Memcpy(new_p, a, n * sizeof(int)); return new_p; } /* The keyval copy and delete functions must handle copying and deleting the associated topology structures We can reduce the number of allocations by making a single allocation of enough integers for all fields (including the ones in the structure) and freeing the single object later. */ #undef FUNCNAME #define FUNCNAME MPIR_Topology_copy_fn #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) static int MPIR_Topology_copy_fn(MPI_Comm comm ATTRIBUTE((unused)), int keyval ATTRIBUTE((unused)), void *extra_data ATTRIBUTE((unused)), void *attr_in, void *attr_out, int *flag) { MPIR_Topology *old_topology = (MPIR_Topology *) attr_in; MPIR_Topology *copy_topology = NULL; MPIR_CHKPMEM_DECL(5); int mpi_errno = 0; MPL_UNREFERENCED_ARG(comm); MPL_UNREFERENCED_ARG(keyval); MPL_UNREFERENCED_ARG(extra_data); *flag = 0; *(void **) attr_out = NULL; MPIR_CHKPMEM_MALLOC(copy_topology, MPIR_Topology *, sizeof(MPIR_Topology), mpi_errno, "copy_topology", MPL_MEM_OTHER); MPL_VG_MEM_INIT(copy_topology, sizeof(MPIR_Topology)); /* simplify copying and error handling */ #define MPIR_ARRAY_COPY_HELPER(kind_,array_field_,count_field_) \ do { \ copy_topology->topo.kind_.array_field_ = \ MPIR_Copy_array(old_topology->topo.kind_.count_field_, \ old_topology->topo.kind_.array_field_, \ &mpi_errno); \ if (mpi_errno) MPIR_ERR_POP(mpi_errno); \ MPIR_CHKPMEM_REGISTER(copy_topology->topo.kind_.array_field_); \ } while (0) copy_topology->kind = old_topology->kind; if (old_topology->kind == MPI_CART) { copy_topology->topo.cart.ndims = old_topology->topo.cart.ndims; copy_topology->topo.cart.nnodes = old_topology->topo.cart.nnodes; MPIR_ARRAY_COPY_HELPER(cart, dims, ndims); MPIR_ARRAY_COPY_HELPER(cart, periodic, ndims); MPIR_ARRAY_COPY_HELPER(cart, position, ndims); } else if (old_topology->kind == MPI_GRAPH) { copy_topology->topo.graph.nnodes = old_topology->topo.graph.nnodes; copy_topology->topo.graph.nedges = old_topology->topo.graph.nedges; MPIR_ARRAY_COPY_HELPER(graph, index, nnodes); MPIR_ARRAY_COPY_HELPER(graph, edges, nedges); } else if (old_topology->kind == MPI_DIST_GRAPH) { copy_topology->topo.dist_graph.indegree = old_topology->topo.dist_graph.indegree; copy_topology->topo.dist_graph.outdegree = old_topology->topo.dist_graph.outdegree; MPIR_ARRAY_COPY_HELPER(dist_graph, in, indegree); MPIR_ARRAY_COPY_HELPER(dist_graph, in_weights, indegree); MPIR_ARRAY_COPY_HELPER(dist_graph, out, outdegree); MPIR_ARRAY_COPY_HELPER(dist_graph, out_weights, outdegree); } /* --BEGIN ERROR HANDLING-- */ else { /* Unknown topology */ return MPI_ERR_TOPOLOGY; } /* --END ERROR HANDLING-- */ #undef MPIR_ARRAY_COPY_HELPER *(void **) attr_out = (void *) copy_topology; *flag = 1; MPIR_CHKPMEM_COMMIT(); fn_exit: /* Return mpi_errno in case one of the copy array functions failed */ return mpi_errno; fn_fail: /* --BEGIN ERROR HANDLING-- */ MPIR_CHKPMEM_REAP(); goto fn_exit; /* --END ERROR HANDLING-- */ } #undef FUNCNAME #define FUNCNAME MPIR_Topology_delete_fn #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) static int MPIR_Topology_delete_fn(MPI_Comm comm ATTRIBUTE((unused)), int keyval ATTRIBUTE((unused)), void *attr_val, void *extra_data ATTRIBUTE((unused))) { MPIR_Topology *topology = (MPIR_Topology *) attr_val; MPL_UNREFERENCED_ARG(comm); MPL_UNREFERENCED_ARG(keyval); MPL_UNREFERENCED_ARG(extra_data); /* FIXME - free the attribute data structure */ if (topology->kind == MPI_CART) { MPL_free(topology->topo.cart.dims); MPL_free(topology->topo.cart.periodic); MPL_free(topology->topo.cart.position); MPL_free(topology); } else if (topology->kind == MPI_GRAPH) { MPL_free(topology->topo.graph.index); MPL_free(topology->topo.graph.edges); MPL_free(topology); } else if (topology->kind == MPI_DIST_GRAPH) { MPL_free(topology->topo.dist_graph.in); MPL_free(topology->topo.dist_graph.out); if (topology->topo.dist_graph.in_weights) MPL_free(topology->topo.dist_graph.in_weights); if (topology->topo.dist_graph.out_weights) MPL_free(topology->topo.dist_graph.out_weights); MPL_free(topology); } /* --BEGIN ERROR HANDLING-- */ else { return MPI_ERR_TOPOLOGY; } /* --END ERROR HANDLING-- */ return MPI_SUCCESS; } /* the next two routines implement the following behavior (quoted from Section * 7.6 of the MPI-3.0 standard): * * For a distributed graph topology, created with MPI_DIST_GRAPH_CREATE, the * sequence of neighbors in the send and receive buffers at each process * is defined as the sequence returned by MPI_DIST_GRAPH_NEIGHBORS for * destinations and sources, respectively. For a general graph topology, * created with MPI_GRAPH_CREATE, the order of neighbors in the send and * receive buffers is defined as the sequence of neighbors as returned by * MPI_GRAPH_NEIGHBORS. Note that general graph topologies should generally * be replaced by the distributed graph topologies. * * For a Cartesian topology, created with MPI_CART_CREATE, the sequence of * neighbors in the send and receive buffers at each process is defined by * order of the dimensions, first the neighbor in the negative direction and * then in the positive direction with displacement 1. The numbers of * sources and destinations in the communication routines are 2*ndims with * ndims defined in MPI_CART_CREATE. If a neighbor does not exist, i.e., at * the border of a Cartesian topology in the case of a non-periodic virtual * grid dimension (i.e., periods[...]==false), then this neighbor is defined * to be MPI_PROC_NULL. */ #undef FUNCNAME #define FUNCNAME MPIR_Topo_canon_nhb_count #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) int MPIR_Topo_canon_nhb_count(MPIR_Comm * comm_ptr, int *indegree, int *outdegree, int *weighted) { int mpi_errno = MPI_SUCCESS; MPIR_Topology *topo_ptr; topo_ptr = MPIR_Topology_get(comm_ptr); MPIR_ERR_CHKANDJUMP(!topo_ptr, mpi_errno, MPI_ERR_TOPOLOGY, "**notopology"); /* TODO consider dispatching via a vtable instead of doing if/else */ if (topo_ptr->kind == MPI_DIST_GRAPH) { mpi_errno = MPIR_Dist_graph_neighbors_count_impl(comm_ptr, indegree, outdegree, weighted); if (mpi_errno) MPIR_ERR_POP(mpi_errno); } else if (topo_ptr->kind == MPI_GRAPH) { int nneighbors = 0; mpi_errno = MPIR_Graph_neighbors_count_impl(comm_ptr, comm_ptr->rank, &nneighbors); if (mpi_errno) MPIR_ERR_POP(mpi_errno); *indegree = *outdegree = nneighbors; *weighted = FALSE; } else if (topo_ptr->kind == MPI_CART) { *indegree = 2 * topo_ptr->topo.cart.ndims; *outdegree = 2 * topo_ptr->topo.cart.ndims; *weighted = FALSE; } else { MPIR_Assert(FALSE); } fn_exit: return mpi_errno; fn_fail: goto fn_exit; } #undef FUNCNAME #define FUNCNAME MPIR_Topo_canon_nhb #undef FCNAME #define FCNAME MPL_QUOTE(FUNCNAME) int MPIR_Topo_canon_nhb(MPIR_Comm * comm_ptr, int indegree, int sources[], int inweights[], int outdegree, int dests[], int outweights[]) { int mpi_errno = MPI_SUCCESS; MPIR_Topology *topo_ptr; MPIR_FUNC_TERSE_STATE_DECL(MPID_STATE_MPIR_TOPO_CANON_NHB); MPIR_FUNC_TERSE_ENTER(MPID_STATE_MPIR_TOPO_CANON_NHB); topo_ptr = MPIR_Topology_get(comm_ptr); MPIR_ERR_CHKANDJUMP(!topo_ptr, mpi_errno, MPI_ERR_TOPOLOGY, "**notopology"); /* TODO consider dispatching via a vtable instead of doing if/else */ if (topo_ptr->kind == MPI_DIST_GRAPH) { mpi_errno = MPIR_Dist_graph_neighbors_impl(comm_ptr, indegree, sources, inweights, outdegree, dests, outweights); if (mpi_errno) MPIR_ERR_POP(mpi_errno); } else if (topo_ptr->kind == MPI_GRAPH) { MPIR_Assert(indegree == outdegree); mpi_errno = MPIR_Graph_neighbors_impl(comm_ptr, comm_ptr->rank, indegree, sources); if (mpi_errno) MPIR_ERR_POP(mpi_errno); MPIR_Memcpy(dests, sources, outdegree * sizeof(*dests)); /* ignore inweights/outweights */ } else if (topo_ptr->kind == MPI_CART) { int d; MPIR_Assert(indegree == outdegree); MPIR_Assert(indegree == 2 * topo_ptr->topo.cart.ndims); for (d = 0; d < topo_ptr->topo.cart.ndims; ++d) { mpi_errno = MPIR_Cart_shift_impl(comm_ptr, d, 1, &sources[2 * d], &sources[2 * d + 1]); if (mpi_errno) MPIR_ERR_POP(mpi_errno); dests[2 * d] = sources[2 * d]; dests[2 * d + 1] = sources[2 * d + 1]; } /* ignore inweights/outweights */ } else { MPIR_Assert(FALSE); } #ifdef MPL_USE_DBG_LOGGING { int i; MPL_DBG_MSG_FMT(MPIR_DBG_PT2PT, VERBOSE, (MPL_DBG_FDEST, "canonical neighbors for comm=0x%x comm_ptr=%p", comm_ptr->handle, comm_ptr)); for (i = 0; i < outdegree; ++i) { MPL_DBG_MSG_FMT(MPIR_DBG_PT2PT, VERBOSE, (MPL_DBG_FDEST, "%d/%d: to %d", i, outdegree, dests[i])); } for (i = 0; i < indegree; ++i) { MPL_DBG_MSG_FMT(MPIR_DBG_PT2PT, VERBOSE, (MPL_DBG_FDEST, "%d/%d: from %d", i, indegree, sources[i])); } } #endif fn_exit: MPIR_FUNC_TERSE_EXIT(MPID_STATE_MPIR_TOPO_CANON_NHB); return mpi_errno; fn_fail: goto fn_exit; }