1
1

util: move graph solver from usnic to util

Cisco wrote a bipartite graph solver to properly solve
interface pair selection for usNIC.  Using the reachable
framework, the TCP BTL (and possibly the runtime network
code) can use the graph solver to make more optimal pair
selection.  Jeff was happy to have the code more broadly
used, but didn't have time to do the move, hence this
commit.

There are a couple of minor changes to the code compared
to the usNIC version.  Obviously, the functions have
been renamed to match naming convention for their new
home.  Since it's easier to write unit tests for
util/ code, the unit tests have been made first class
tests run at "make check" time.  This last bit required
moving some of the definitions into a new header,
bipartite_graph_internal.h, so that they could be
included in both the library code and the test code.

Signed-off-by: Brian Barrett <bbarrett@amazon.com>
Этот коммит содержится в:
Brian Barrett 2017-09-14 18:36:48 +00:00
родитель f69466d633
Коммит bffcc3bca0
10 изменённых файлов: 566 добавлений и 485 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -681,3 +681,4 @@ test/util/opal_sos
test/util/opal_path_nfs
test/util/opal_path_nfs.out
test/util/opal_bit_ops
test/util/bipartite_graph

Просмотреть файл

@ -31,8 +31,7 @@ dist_opaldata_DATA = \
help-mpi-btl-usnic.txt
test_sources = \
test/btl_usnic_component_test.h \
test/btl_usnic_graph_test.h
test/btl_usnic_component_test.h
sources = \
btl_usnic_compat.h \
@ -50,8 +49,6 @@ sources = \
btl_usnic_endpoint.h \
btl_usnic_frag.c \
btl_usnic_frag.h \
btl_usnic_graph.h \
btl_usnic_graph.c \
btl_usnic_hwloc.c \
btl_usnic_hwloc.h \
btl_usnic_map.c \

Просмотреть файл

@ -149,8 +149,6 @@ usnic_compat_proc_name_compare(opal_process_name_t a,
# define opal_btl_usnic_ack_segment_t ompi_btl_usnic_ack_segment_t
# define opal_btl_usnic_ack_segment_t_class ompi_btl_usnic_ack_segment_t_class
# define opal_btl_usnic_graph_t ompi_btl_usnic_graph_t
# define opal_btl_usnic_run_tests ompi_btl_usnic_run_tests
# define USNIC_SEND_LOCAL des_src

Просмотреть файл

@ -27,6 +27,7 @@
#include "opal/util/arch.h"
#include "opal/util/show_help.h"
#include "opal/constants.h"
#include "opal/util/bipartite_graph.h"
#include "btl_usnic_compat.h"
#include "btl_usnic.h"
@ -34,7 +35,6 @@
#include "btl_usnic_endpoint.h"
#include "btl_usnic_module.h"
#include "btl_usnic_util.h"
#include "btl_usnic_graph.h"
/* larger weight values are more desirable (i.e., worth, not cost) */
enum {
@ -427,13 +427,13 @@ static void edge_pairs_to_match_table(
static int create_proc_module_graph(
opal_btl_usnic_proc_t *proc,
bool proc_is_left,
opal_btl_usnic_graph_t **g_out)
opal_bp_graph_t **g_out)
{
int err;
int i, j;
int u, v;
int num_modules;
opal_btl_usnic_graph_t *g = NULL;
opal_bp_graph_t *g = NULL;
if (NULL == g_out) {
return OPAL_ERR_BAD_PARAM;
@ -444,7 +444,7 @@ static int create_proc_module_graph(
/* Construct a bipartite graph with remote interfaces on the one side and
* local interfaces (modules) on the other. */
err = opal_btl_usnic_gr_create(NULL, NULL, &g);
err = opal_bp_graph_create(NULL, NULL, &g);
if (OPAL_SUCCESS != err) {
OPAL_ERROR_LOG(err);
goto out;
@ -453,9 +453,9 @@ static int create_proc_module_graph(
/* create vertices for each interface (local and remote) */
for (i = 0; i < num_modules; ++i) {
int idx = -1;
err = opal_btl_usnic_gr_add_vertex(g,
mca_btl_usnic_component.usnic_active_modules[i],
&idx);
err = opal_bp_graph_add_vertex(g,
mca_btl_usnic_component.usnic_active_modules[i],
&idx);
if (OPAL_SUCCESS != err) {
OPAL_ERROR_LOG(err);
goto out_free_graph;
@ -464,7 +464,7 @@ static int create_proc_module_graph(
}
for (i = 0; i < (int)proc->proc_modex_count; ++i) {
int idx = -1;
err = opal_btl_usnic_gr_add_vertex(g, &proc->proc_modex[i], &idx);
err = opal_bp_graph_add_vertex(g, &proc->proc_modex[i], &idx);
if (OPAL_SUCCESS != err) {
OPAL_ERROR_LOG(err);
goto out_free_graph;
@ -509,9 +509,9 @@ static int create_proc_module_graph(
opal_output_verbose(20, USNIC_OUT,
"btl:usnic:%s: adding edge (%d,%d) with cost=%" PRIi64 " for edge module[%d] <--> endpoint[%d]",
__func__, u, v, cost, i, j);
err = opal_btl_usnic_gr_add_edge(g, u, v, cost,
/*capacity=*/1,
/*e_data=*/NULL);
err = opal_bp_graph_add_edge(g, u, v, cost,
/*capacity=*/1,
/*e_data=*/NULL);
if (OPAL_SUCCESS != err) {
OPAL_ERROR_LOG(err);
goto out_free_graph;
@ -523,7 +523,7 @@ static int create_proc_module_graph(
return OPAL_SUCCESS;
out_free_graph:
opal_btl_usnic_gr_free(g);
opal_bp_graph_free(g);
out:
return err;
}
@ -547,7 +547,7 @@ static int match_modex(opal_btl_usnic_module_t *module,
int err = OPAL_SUCCESS;
size_t i;
uint32_t num_modules;
opal_btl_usnic_graph_t *g = NULL;
opal_bp_graph_t *g = NULL;
bool proc_is_left;
if (NULL == index_out) {
@ -599,7 +599,7 @@ static int match_modex(opal_btl_usnic_module_t *module,
int nme = 0;
int *me = NULL;
err = opal_btl_usnic_solve_bipartite_assignment(g, &nme, &me);
err = opal_bp_graph_solve_bipartite_assignment(g, &nme, &me);
if (OPAL_SUCCESS != err) {
OPAL_ERROR_LOG(err);
goto out_free_graph;
@ -608,7 +608,7 @@ static int match_modex(opal_btl_usnic_module_t *module,
edge_pairs_to_match_table(proc, proc_is_left, nme, me);
free(me);
err = opal_btl_usnic_gr_free(g);
err = opal_bp_graph_free(g);
if (OPAL_SUCCESS != err) {
OPAL_ERROR_LOG(err);
return err;
@ -655,7 +655,7 @@ static int match_modex(opal_btl_usnic_module_t *module,
return (*index_out == -1 ? OPAL_ERR_NOT_FOUND : OPAL_SUCCESS);
out_free_graph:
opal_btl_usnic_gr_free(g);
opal_bp_graph_free(g);
out_free_table:
free(proc->proc_ep_match_table);
proc->proc_ep_match_table = NULL;

Просмотреть файл

@ -42,6 +42,8 @@ headers = \
arch.h \
argv.h \
basename.h \
bipartite_graph.h \
bipartite_graph_internal.h \
bit_ops.h \
cmd_line.h \
crc.h \
@ -81,6 +83,7 @@ libopalutil_la_SOURCES = \
arch.c \
argv.c \
basename.c \
bipartite_graph.c \
cmd_line.c \
crc.c \
daemon_init.c \

Просмотреть файл

@ -1,5 +1,7 @@
/*
* Copyright (c) 2014 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2017 Amazon.com, Inc. or its affiliates. All Rights
* reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
@ -10,94 +12,32 @@
#include "opal_config.h"
#include <stdlib.h>
#include <stddef.h>
#include "opal_stdint.h"
#include "opal/class/opal_pointer_array.h"
#include "opal/constants.h"
#include "opal/class/opal_list.h"
#include "opal/class/opal_pointer_array.h"
#include "opal/util/output.h"
#include "opal/util/error.h"
/* mainly for BTL_ERROR */
#if BTL_IN_OPAL
#include "opal/mca/btl/btl.h"
#include "opal/mca/btl/base/base.h"
#include "opal/mca/btl/base/btl_base_error.h"
#else
#include "ompi/mca/btl/btl.h"
#include "ompi/mca/btl/base/base.h"
#include "ompi/mca/btl/base/btl_base_error.h"
#include "opal/util/bipartite_graph.h"
#include "opal/util/bipartite_graph_internal.h"
#ifndef container_of
#define container_of(ptr, type, member) ( \
(type *)( ((char *)(ptr)) - offsetof(type,member) ))
#endif
#include "btl_usnic.h"
#include "btl_usnic_graph.h"
#include "btl_usnic_compat.h"
#define GRAPH_DEBUG 0
#if GRAPH_DEBUG
# define GRAPH_DEBUG_OUT(args) BTL_OUTPUT(args)
# define GRAPH_DEBUG_OUT(args) printf(args)
#else
# define GRAPH_DEBUG_OUT(args) do {} while(0)
#endif
#define MAX_COST INT64_MAX
struct opal_btl_usnic_edge_t {
opal_object_t super;
opal_list_item_t outbound_li;
opal_list_item_t inbound_li;
/** source of this edge */
int source;
/** v_index of target of this edge */
int target;
/** cost (weight) of this edge */
int64_t cost;
/**
* (flow-network) capacity of this edge. Zero-capacity edges essentially do
* not exist and will be ignored by most of the algorithms implemented here.
*/
int capacity;
/** any other information associated with this edge */
void *e_data;
};
struct opal_btl_usnic_vertex_t {
/** index in the graph's array of vertices */
int v_index;
/** any other information associated with the vertex */
void *v_data;
/** linked list of edges for which this vertex is a source */
opal_list_t out_edges;
/** linked list of edges for which this vertex is a target */
opal_list_t in_edges;
};
struct opal_btl_usnic_graph_t {
/** number of vertices currently in this graph */
int num_vertices;
/** vertices in this graph (with number of set elements == num_vertices) */
opal_pointer_array_t vertices;
/** index of the source vertex, or -1 if not present */
int source_idx;
/** index of the sink vertex, or -1 if not present */
int sink_idx;
/** user callback to clean up the v_data */
opal_btl_usnic_cleanup_fn_t v_data_cleanup_fn;
/** user callback to clean up the e_data */
opal_btl_usnic_cleanup_fn_t e_data_cleanup_fn;
};
#ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
@ -108,57 +48,6 @@ struct opal_btl_usnic_graph_t {
#define f(i,j) flow[n*i + j]
#define LIST_FOREACH_CONTAINED(item, list, type, member) \
for (item = container_of( (list)->opal_list_sentinel.opal_list_next, type, member ); \
&item->member != &(list)->opal_list_sentinel; \
item = container_of( \
((opal_list_item_t *) (&item->member))->opal_list_next, type, member ))
#define LIST_FOREACH_SAFE_CONTAINED(item, next, list, type, member) \
for (item = container_of( (list)->opal_list_sentinel.opal_list_next, type, member ), \
next = container_of( \
((opal_list_item_t *) (&item->member))->opal_list_next, type, member ); \
&item->member != &(list)->opal_list_sentinel; \
item = next, \
next = container_of( \
((opal_list_item_t *) (&item->member))->opal_list_next, type, member ))
#define NUM_VERTICES(g) (g->num_vertices)
#define CHECK_VERTEX_RANGE(g,v) \
do { \
if ((v) < 0 || \
(v) >= NUM_VERTICES(g)) { \
return OPAL_ERR_BAD_PARAM; \
} \
} while (0)
/* cast away any constness of &g->vertices b/c the opal_pointer_array API is
* not const-correct */
#define V_ID_TO_PTR(g, v_id) \
((opal_btl_usnic_vertex_t *) \
opal_pointer_array_get_item((opal_pointer_array_t *)&g->vertices, v_id))
#define FOREACH_OUT_EDGE(g,v_id,e_ptr) \
LIST_FOREACH_CONTAINED(e_ptr, \
&(V_ID_TO_PTR(g, v_id)->out_edges), \
opal_btl_usnic_edge_t, \
outbound_li)
#define FOREACH_IN_EDGE(g,v_id,e_ptr) \
LIST_FOREACH_CONTAINED(e_ptr, \
&(V_ID_TO_PTR(g, v_id)->in_edges), \
opal_btl_usnic_edge_t, \
inbound_li)
/* Iterate over (u,v) edge pairs along the given path, where path is defined
* by the predecessor array "pred". Stops when a -1 predecessor is
* encountered. Note: because it is a *predecessor* array, the traversal
* starts at the sink and progresses towards the source. */
#define FOREACH_UV_ON_PATH(pred, source, sink, u, v) \
for (u = pred[sink], v = sink; u != -1; v = u, u = pred[u])
/* ensure that (a+b<=max) */
static inline void check_add64_overflow(int64_t a, int64_t b)
{
@ -166,20 +55,20 @@ static inline void check_add64_overflow(int64_t a, int64_t b)
!((b < 0) && (a < (INT64_MIN - b))));
}
static void edge_constructor(opal_btl_usnic_edge_t *e)
static void edge_constructor(opal_bp_graph_edge_t *e)
{
OBJ_CONSTRUCT(&e->outbound_li, opal_list_item_t);
OBJ_CONSTRUCT(&e->inbound_li, opal_list_item_t);
}
static void edge_destructor(opal_btl_usnic_edge_t *e)
static void edge_destructor(opal_bp_graph_edge_t *e)
{
OBJ_DESTRUCT(&e->outbound_li);
OBJ_DESTRUCT(&e->inbound_li);
}
OBJ_CLASS_DECLARATION(opal_btl_usnic_edge_t);
OBJ_CLASS_INSTANCE(opal_btl_usnic_edge_t, opal_object_t,
OBJ_CLASS_DECLARATION(opal_bp_graph_edge_t);
OBJ_CLASS_INSTANCE(opal_bp_graph_edge_t, opal_object_t,
edge_constructor, edge_destructor);
static void dump_vec(const char *name, int *vec, int n)
@ -228,9 +117,9 @@ static void dump_flow(int *flow, int n)
}
static int get_capacity(opal_btl_usnic_graph_t *g, int source, int target)
static int get_capacity(opal_bp_graph_t *g, int source, int target)
{
opal_btl_usnic_edge_t *e;
opal_bp_graph_edge_t *e;
CHECK_VERTEX_RANGE(g, source);
CHECK_VERTEX_RANGE(g, target);
@ -246,9 +135,9 @@ static int get_capacity(opal_btl_usnic_graph_t *g, int source, int target)
}
static int
set_capacity(opal_btl_usnic_graph_t *g, int source, int target, int cap)
set_capacity(opal_bp_graph_t *g, int source, int target, int cap)
{
opal_btl_usnic_edge_t *e;
opal_bp_graph_edge_t *e;
CHECK_VERTEX_RANGE(g, source);
CHECK_VERTEX_RANGE(g, target);
@ -264,8 +153,8 @@ set_capacity(opal_btl_usnic_graph_t *g, int source, int target, int cap)
return OPAL_ERR_NOT_FOUND;
}
static void free_vertex(opal_btl_usnic_graph_t *g,
opal_btl_usnic_vertex_t *v)
static void free_vertex(opal_bp_graph_t *g,
opal_bp_graph_vertex_t *v)
{
if (NULL != v) {
if (NULL != g->v_data_cleanup_fn && NULL != v->v_data) {
@ -275,12 +164,12 @@ static void free_vertex(opal_btl_usnic_graph_t *g,
}
}
int opal_btl_usnic_gr_create(opal_btl_usnic_cleanup_fn_t v_data_cleanup_fn,
opal_btl_usnic_cleanup_fn_t e_data_cleanup_fn,
opal_btl_usnic_graph_t **g_out)
int opal_bp_graph_create(opal_bp_graph_cleanup_fn_t v_data_cleanup_fn,
opal_bp_graph_cleanup_fn_t e_data_cleanup_fn,
opal_bp_graph_t **g_out)
{
int err;
opal_btl_usnic_graph_t *g = NULL;
opal_bp_graph_t *g = NULL;
if (NULL == g_out) {
return OPAL_ERR_BAD_PARAM;
@ -310,22 +199,22 @@ int opal_btl_usnic_gr_create(opal_btl_usnic_cleanup_fn_t v_data_cleanup_fn,
*g_out = g;
return OPAL_SUCCESS;
out_free_g:
out_free_g:
free(g);
return err;
}
int opal_btl_usnic_gr_free(opal_btl_usnic_graph_t *g)
int opal_bp_graph_free(opal_bp_graph_t *g)
{
int i;
opal_btl_usnic_edge_t *e, *next;
opal_btl_usnic_vertex_t *v;
opal_bp_graph_edge_t *e, *next;
opal_bp_graph_vertex_t *v;
/* remove all edges from all out_edges lists */
for (i = 0; i < NUM_VERTICES(g); ++i) {
v = V_ID_TO_PTR(g, i);
LIST_FOREACH_SAFE_CONTAINED(e, next, &v->out_edges,
opal_btl_usnic_edge_t, outbound_li) {
opal_bp_graph_edge_t, outbound_li) {
opal_list_remove_item(&v->out_edges, &e->outbound_li);
OBJ_RELEASE(e);
}
@ -334,7 +223,7 @@ int opal_btl_usnic_gr_free(opal_btl_usnic_graph_t *g)
for (i = 0; i < NUM_VERTICES(g); ++i) {
v = V_ID_TO_PTR(g, i);
LIST_FOREACH_SAFE_CONTAINED(e, next, &v->in_edges,
opal_btl_usnic_edge_t, inbound_li) {
opal_bp_graph_edge_t, inbound_li) {
opal_list_remove_item(&v->in_edges, &e->inbound_li);
if (NULL != g->e_data_cleanup_fn && NULL != e->e_data) {
@ -354,15 +243,15 @@ int opal_btl_usnic_gr_free(opal_btl_usnic_graph_t *g)
return OPAL_SUCCESS;
}
int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
bool copy_user_data,
opal_btl_usnic_graph_t **g_clone_out)
int opal_bp_graph_clone(const opal_bp_graph_t *g,
bool copy_user_data,
opal_bp_graph_t **g_clone_out)
{
int err;
int i;
int index;
opal_btl_usnic_graph_t *gx;
opal_btl_usnic_edge_t *e;
opal_bp_graph_t *gx;
opal_bp_graph_edge_t *e;
if (NULL == g_clone_out) {
return OPAL_ERR_BAD_PARAM;
@ -370,13 +259,14 @@ int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
*g_clone_out = NULL;
if (copy_user_data) {
BTL_ERROR(("user data copy requested but not yet supported"));
opal_output(0, "[%s:%d:%s] user data copy requested but not yet supported",
__FILE__, __LINE__, __func__);
abort();
return OPAL_ERR_FATAL;
}
gx = NULL;
err = opal_btl_usnic_gr_create(NULL, NULL, &gx);
err = opal_bp_graph_create(NULL, NULL, &gx);
if (OPAL_SUCCESS != err) {
return err;
}
@ -384,7 +274,7 @@ int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
/* reconstruct all vertices */
for (i = 0; i < NUM_VERTICES(g); ++i) {
err = opal_btl_usnic_gr_add_vertex(gx, NULL, &index);
err = opal_bp_graph_add_vertex(gx, NULL, &index);
if (OPAL_SUCCESS != err) {
goto out_free_gx;
}
@ -396,8 +286,8 @@ int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
for (i = 0; i < NUM_VERTICES(g); ++i) {
FOREACH_OUT_EDGE(g, i, e) {
assert(i == e->source);
err = opal_btl_usnic_gr_add_edge(gx, e->source, e->target,
e->cost, e->capacity, NULL);
err = opal_bp_graph_add_edge(gx, e->source, e->target,
e->cost, e->capacity, NULL);
if (OPAL_SUCCESS != err) {
goto out_free_gx;
}
@ -407,40 +297,40 @@ int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
*g_clone_out = gx;
return OPAL_SUCCESS;
out_free_gx:
out_free_gx:
/* we don't reach in and manipulate gx's state directly, so it should be
* safe to use the standard free function */
opal_btl_usnic_gr_free(gx);
opal_bp_graph_free(gx);
return err;
}
int opal_btl_usnic_gr_indegree(const opal_btl_usnic_graph_t *g,
int vertex)
int opal_bp_graph_indegree(const opal_bp_graph_t *g,
int vertex)
{
opal_btl_usnic_vertex_t *v;
opal_bp_graph_vertex_t *v;
v = V_ID_TO_PTR(g, vertex);
return opal_list_get_size(&v->in_edges);
}
int opal_btl_usnic_gr_outdegree(const opal_btl_usnic_graph_t *g,
int vertex)
int opal_bp_graph_outdegree(const opal_bp_graph_t *g,
int vertex)
{
opal_btl_usnic_vertex_t *v;
opal_bp_graph_vertex_t *v;
v = V_ID_TO_PTR(g, vertex);
return opal_list_get_size(&v->out_edges);
}
int opal_btl_usnic_gr_add_edge(opal_btl_usnic_graph_t *g,
int from,
int to,
int64_t cost,
int capacity,
void *e_data)
int opal_bp_graph_add_edge(opal_bp_graph_t *g,
int from,
int to,
int64_t cost,
int capacity,
void *e_data)
{
opal_btl_usnic_edge_t *e;
opal_btl_usnic_vertex_t *v_from, *v_to;
opal_bp_graph_edge_t *e;
opal_bp_graph_vertex_t *v_from, *v_to;
if (from < 0 || from >= NUM_VERTICES(g)) {
return OPAL_ERR_BAD_PARAM;
@ -464,7 +354,7 @@ int opal_btl_usnic_gr_add_edge(opal_btl_usnic_graph_t *g,
}
/* this reference is owned by the out_edges list */
e = OBJ_NEW(opal_btl_usnic_edge_t);
e = OBJ_NEW(opal_bp_graph_edge_t);
if (NULL == e) {
OPAL_ERROR_LOG(OPAL_ERR_OUT_OF_RESOURCE);
return OPAL_ERR_OUT_OF_RESOURCE;
@ -486,11 +376,11 @@ int opal_btl_usnic_gr_add_edge(opal_btl_usnic_graph_t *g,
return OPAL_SUCCESS;
}
int opal_btl_usnic_gr_add_vertex(opal_btl_usnic_graph_t *g,
void *v_data,
int *index_out)
int opal_bp_graph_add_vertex(opal_bp_graph_t *g,
void *v_data,
int *index_out)
{
opal_btl_usnic_vertex_t *v;
opal_bp_graph_vertex_t *v;
v = calloc(1, sizeof(*v));
if (NULL == v) {
@ -521,7 +411,7 @@ int opal_btl_usnic_gr_add_vertex(opal_btl_usnic_graph_t *g,
return OPAL_SUCCESS;
}
int opal_btl_usnic_gr_order(const opal_btl_usnic_graph_t *g)
int opal_bp_graph_order(const opal_bp_graph_t *g)
{
return NUM_VERTICES(g);
}
@ -565,9 +455,9 @@ static void shrink_flow_matrix(int *flow, int old_n, int new_n)
*/
static int
bottleneck_path(
opal_btl_usnic_graph_t *gx,
int n,
int *pred)
opal_bp_graph_t *gx,
int n,
int *pred)
{
int u, v;
int min;
@ -594,10 +484,10 @@ bottleneck_path(
*
* The contents of "pred" are only valid if this routine returns true.
*/
static bool bellman_ford(opal_btl_usnic_graph_t *gx,
int source,
int target,
int *pred)
bool opal_bp_graph_bellman_ford(opal_bp_graph_t *gx,
int source,
int target,
int *pred)
{
int64_t *dist;
int i;
@ -621,7 +511,7 @@ static bool bellman_ford(opal_btl_usnic_graph_t *gx,
}
/* initialize */
n = opal_btl_usnic_gr_order(gx);
n = opal_bp_graph_order(gx);
dist = malloc(n * sizeof(*dist));
if (NULL == dist) {
OPAL_ERROR_LOG(OPAL_ERR_OUT_OF_RESOURCE);
@ -642,7 +532,7 @@ static bool bellman_ford(opal_btl_usnic_graph_t *gx,
#endif
for (u = 0; u < NUM_VERTICES(gx); ++u) {
opal_btl_usnic_edge_t *e_ptr;
opal_bp_graph_edge_t *e_ptr;
FOREACH_OUT_EDGE(gx, u, e_ptr) {
v = e_ptr->target;
@ -670,14 +560,15 @@ static bool bellman_ford(opal_btl_usnic_graph_t *gx,
/* check for negative-cost cycles */
for (u = 0; u < NUM_VERTICES(gx); ++u) {
opal_btl_usnic_edge_t * e_ptr;
opal_bp_graph_edge_t * e_ptr;
FOREACH_OUT_EDGE(gx, u, e_ptr) {
v = e_ptr->target;
if (e_ptr->capacity > 0 &&
dist[u] != MAX_COST && /* avoid signed overflow */
(dist[u] + e_ptr->cost) < dist[v]) {
BTL_ERROR(("negative-weight cycle detected"));
opal_output(0, "[%s:%d:%s] negative-weight cycle detected",
__FILE__, __LINE__, __func__);
abort();
goto out;
}
@ -688,7 +579,7 @@ static bool bellman_ford(opal_btl_usnic_graph_t *gx,
found_target = true;
}
out:
out:
#if GRAPH_DEBUG
dump_vec("pred", pred, NUM_VERTICES(gx));
#endif
@ -716,7 +607,7 @@ out:
* The graph will be left in an undefined state if an error occurs (though
* freeing it should still be safe).
*/
static int bipartite_to_flow(opal_btl_usnic_graph_t *g)
int opal_bp_graph_bipartite_to_flow(opal_bp_graph_t *g)
{
int err;
int order;
@ -724,13 +615,13 @@ static int bipartite_to_flow(opal_btl_usnic_graph_t *g)
int num_left, num_right;
/* grab size before adding extra vertices */
order = opal_btl_usnic_gr_order(g);
order = opal_bp_graph_order(g);
err = opal_btl_usnic_gr_add_vertex(g, NULL, &g->source_idx);
err = opal_bp_graph_add_vertex(g, NULL, &g->source_idx);
if (OPAL_SUCCESS != err) {
return err;
}
err = opal_btl_usnic_gr_add_vertex(g, NULL, &g->sink_idx);
err = opal_bp_graph_add_vertex(g, NULL, &g->sink_idx);
if (OPAL_SUCCESS != err) {
return err;
}
@ -745,20 +636,21 @@ static int bipartite_to_flow(opal_btl_usnic_graph_t *g)
num_left = 0;
num_right = 0;
for (u = 0; u < order; ++u) {
int inbound = opal_btl_usnic_gr_indegree(g, u);
int outbound = opal_btl_usnic_gr_outdegree(g, u);
int inbound = opal_bp_graph_indegree(g, u);
int outbound = opal_bp_graph_outdegree(g, u);
if (inbound > 0 && outbound > 0) {
BTL_ERROR(("graph is not (unidirectionally) bipartite"));
opal_output(0, "[%s:%d:%s] graph is not (unidirectionally) bipartite",
__FILE__, __LINE__, __func__);
abort();
}
else if (inbound > 0) {
/* "right" side of the graph, create edges to the sink */
++num_right;
err = opal_btl_usnic_gr_add_edge(g, u, g->sink_idx,
0, /* no cost */
/*capacity=*/1,
/*e_data=*/NULL);
err = opal_bp_graph_add_edge(g, u, g->sink_idx,
0, /* no cost */
/*capacity=*/1,
/*e_data=*/NULL);
if (OPAL_SUCCESS != err) {
GRAPH_DEBUG_OUT(("add_edge failed"));
return err;
@ -767,10 +659,10 @@ static int bipartite_to_flow(opal_btl_usnic_graph_t *g)
else if (outbound > 0) {
/* "left" side of the graph, create edges to the source */
++num_left;
err = opal_btl_usnic_gr_add_edge(g, g->source_idx, u,
0, /* no cost */
/*capacity=*/1,
/*e_data=*/NULL);
err = opal_bp_graph_add_edge(g, g->source_idx, u,
0, /* no cost */
/*capacity=*/1,
/*e_data=*/NULL);
if (OPAL_SUCCESS != err) {
GRAPH_DEBUG_OUT(("add_edge failed"));
return err;
@ -789,20 +681,20 @@ static int bipartite_to_flow(opal_btl_usnic_graph_t *g)
* $c_f(u,v)=c(u,v)-f(u,v)$). Residual edges can exist where no edges
* exist in the original graph.
*/
order = opal_btl_usnic_gr_order(g); /* need residuals for newly created
source/sink edges too */
order = opal_bp_graph_order(g); /* need residuals for newly created
source/sink edges too */
for (u = 0; u < order; ++u) {
opal_btl_usnic_edge_t * e_ptr;
opal_bp_graph_edge_t * e_ptr;
FOREACH_OUT_EDGE(g, u, e_ptr) {
v = e_ptr->target;
/* (u,v) exists, add (v,u) if not already present. Cost is
* negative for these edges because "giving back" flow pays us
* back any cost already incurred. */
err = opal_btl_usnic_gr_add_edge(g, v, u,
-e_ptr->cost,
/*capacity=*/0,
/*e_data=*/NULL);
err = opal_bp_graph_add_edge(g, v, u,
-e_ptr->cost,
/*capacity=*/0,
/*e_data=*/NULL);
if (OPAL_SUCCESS != err && OPAL_EXISTS != err) {
return err;
}
@ -853,7 +745,7 @@ static int bipartite_to_flow(opal_btl_usnic_graph_t *g)
* the faster running time will be worth the additional implementation
* complexity.
*/
static int min_cost_flow_ssp(opal_btl_usnic_graph_t *gx,
static int min_cost_flow_ssp(opal_bp_graph_t *gx,
int **flow_out)
{
int err = OPAL_SUCCESS;
@ -870,7 +762,7 @@ static int min_cost_flow_ssp(opal_btl_usnic_graph_t *gx,
}
*flow_out = NULL;
n = opal_btl_usnic_gr_order(gx);
n = opal_bp_graph_order(gx);
pred = malloc(n*sizeof(*pred));
if (NULL == pred) {
@ -888,7 +780,7 @@ static int min_cost_flow_ssp(opal_btl_usnic_graph_t *gx,
}
/* loop as long as paths exist from source to sink */
while (bellman_ford(gx, gx->source_idx, gx->sink_idx, pred)) {
while (opal_bp_graph_bellman_ford(gx, gx->source_idx, gx->sink_idx, pred)) {
int cap_f_path;
/* find any shortest path P from s to t (already present in pred) */
@ -915,7 +807,8 @@ static int min_cost_flow_ssp(opal_btl_usnic_graph_t *gx,
assert(c >= 0);
err = set_capacity(gx, u, v, c);
if (OPAL_SUCCESS != err) {
BTL_ERROR(("unable to set capacity, missing edge?"));
opal_output(0, "[%s:%d:%s] unable to set capacity, missing edge?",
__FILE__, __LINE__, __func__);
abort();
}
@ -923,33 +816,34 @@ static int min_cost_flow_ssp(opal_btl_usnic_graph_t *gx,
assert(c >= 0);
err = set_capacity(gx, v, u, c);
if (OPAL_SUCCESS != err) {
BTL_ERROR(("unable to set capacity, missing edge?"));
opal_output(0, "[%s:%d:%s] unable to set capacity, missing edge?",
__FILE__, __LINE__, __func__);
abort();
}
}
}
out:
out:
*flow_out = flow;
free(pred);
return err;
out_error:
out_error:
free(*flow_out);
GRAPH_DEBUG_OUT(("returning error %d", err));
goto out;
}
int opal_btl_usnic_solve_bipartite_assignment(const opal_btl_usnic_graph_t *g,
int *num_match_edges_out,
int **match_edges_out)
int opal_bp_graph_solve_bipartite_assignment(const opal_bp_graph_t *g,
int *num_match_edges_out,
int **match_edges_out)
{
int err;
int i;
int u, v;
int n;
int *flow = NULL;
opal_btl_usnic_graph_t *gx = NULL;
opal_bp_graph_t *gx = NULL;
if (NULL == match_edges_out || NULL == num_match_edges_out) {
return OPAL_ERR_BAD_PARAM;
@ -958,9 +852,9 @@ int opal_btl_usnic_solve_bipartite_assignment(const opal_btl_usnic_graph_t *g,
*match_edges_out = NULL;
/* don't perturb the caller's data structure */
err = opal_btl_usnic_gr_clone(g, false, &gx);
err = opal_bp_graph_clone(g, false, &gx);
if (OPAL_SUCCESS != err) {
GRAPH_DEBUG_OUT(("opal_btl_usnic_gr_clone failed"));
GRAPH_DEBUG_OUT(("opal_bp_graph_clone failed"));
goto out;
}
@ -976,7 +870,7 @@ int opal_btl_usnic_solve_bipartite_assignment(const opal_btl_usnic_graph_t *g,
* original graph. This allows many other graph operations to have no
* direct knowledge of the flow matrix.
*/
err = bipartite_to_flow(gx);
err = opal_bp_graph_bipartite_to_flow(gx);
if (OPAL_SUCCESS != err) {
GRAPH_DEBUG_OUT(("bipartite_to_flow failed"));
OPAL_ERROR_LOG(err);
@ -997,12 +891,12 @@ int opal_btl_usnic_solve_bipartite_assignment(const opal_btl_usnic_graph_t *g,
assert(NULL != flow);
/* don't care about new edges in gx, only old edges in g */
n = opal_btl_usnic_gr_order(g);
n = opal_bp_graph_order(g);
#if GRAPH_DEBUG
dump_flow(flow, NUM_VERTICES(gx));
#endif
shrink_flow_matrix(flow, opal_btl_usnic_gr_order(gx), n);
shrink_flow_matrix(flow, opal_bp_graph_order(gx), n);
#if GRAPH_DEBUG
dump_flow(flow, n);
#endif
@ -1039,10 +933,8 @@ int opal_btl_usnic_solve_bipartite_assignment(const opal_btl_usnic_graph_t *g,
}
}
out:
out:
free(flow);
opal_btl_usnic_gr_free(gx);
opal_bp_graph_free(gx);
return err;
}
#include "test/btl_usnic_graph_test.h"

Просмотреть файл

@ -1,5 +1,7 @@
/*
* Copyright (c) 2014 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2017 Amazon.com, Inc. or its affiliates. All Rights
* reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
@ -15,23 +17,21 @@
* that complicates other pieces of the implementation (specifically, adding
* and removing edges). */
#ifndef BTL_USNIC_GRAPH_H
#define BTL_USNIC_GRAPH_H
#ifndef OPAL_BP_GRAPH_H
#define OPAL_BP_GRAPH_H
#include "opal_config.h"
struct opal_bp_graph_vertex_t;
struct opal_bp_graph_edge_t;
struct opal_bp_graph_t;
struct opal_btl_usnic_vertex_t;
struct opal_btl_usnic_edge_t;
struct opal_btl_usnic_graph_t;
typedef struct opal_btl_usnic_vertex_t opal_btl_usnic_vertex_t;
typedef struct opal_btl_usnic_edge_t opal_btl_usnic_edge_t;
typedef struct opal_btl_usnic_graph_t opal_btl_usnic_graph_t;
typedef struct opal_bp_graph_vertex_t opal_bp_graph_vertex_t;
typedef struct opal_bp_graph_edge_t opal_bp_graph_edge_t;
typedef struct opal_bp_graph_t opal_bp_graph_t;
/**
* callback function pointer type for cleaning up user data associated with a
* vertex or edge */
typedef void (*opal_btl_usnic_cleanup_fn_t)(void *user_data);
typedef void (*opal_bp_graph_cleanup_fn_t)(void *user_data);
/**
* create a new empty graph
@ -44,9 +44,9 @@ typedef void (*opal_btl_usnic_cleanup_fn_t)(void *user_data);
*
* @returns OPAL_SUCCESS or an OMPI error code
*/
int opal_btl_usnic_gr_create(opal_btl_usnic_cleanup_fn_t v_data_cleanup_fn,
opal_btl_usnic_cleanup_fn_t e_data_cleanup_fn,
opal_btl_usnic_graph_t **g_out);
int opal_bp_graph_create(opal_bp_graph_cleanup_fn_t v_data_cleanup_fn,
opal_bp_graph_cleanup_fn_t e_data_cleanup_fn,
opal_bp_graph_t **g_out);
/**
* free the given graph
@ -56,7 +56,7 @@ int opal_btl_usnic_gr_create(opal_btl_usnic_cleanup_fn_t v_data_cleanup_fn,
*
* @returns OPAL_SUCCESS or an OMPI error code
*/
int opal_btl_usnic_gr_free(opal_btl_usnic_graph_t *g);
int opal_bp_graph_free(opal_bp_graph_t *g);
/**
* clone (deep copy) the given graph
@ -70,9 +70,9 @@ int opal_btl_usnic_gr_free(opal_btl_usnic_graph_t *g);
* @param[in] g_clone_out the resulting cloned graph
* @returns OPAL_SUCCESS or an OMPI error code
*/
int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
bool copy_user_data,
opal_btl_usnic_graph_t **g_clone_out);
int opal_bp_graph_clone(const opal_bp_graph_t *g,
bool copy_user_data,
opal_bp_graph_t **g_clone_out);
/**
* return the number of edges for which this vertex is a destination
@ -81,8 +81,8 @@ int opal_btl_usnic_gr_clone(const opal_btl_usnic_graph_t *g,
* @param[in] vertex the vertex id to query
* @returns the number of edges for which this vertex is a destination
*/
int opal_btl_usnic_gr_indegree(const opal_btl_usnic_graph_t *g,
int vertex);
int opal_bp_graph_indegree(const opal_bp_graph_t *g,
int vertex);
/**
* return the number of edges for which this vertex is a source
@ -91,8 +91,8 @@ int opal_btl_usnic_gr_indegree(const opal_btl_usnic_graph_t *g,
* @param[in] vertex the vertex id to query
* @returns the number of edges for which this vertex is a source
*/
int opal_btl_usnic_gr_outdegree(const opal_btl_usnic_graph_t *g,
int vertex);
int opal_bp_graph_outdegree(const opal_bp_graph_t *g,
int vertex);
/**
* add an edge to the given graph
@ -106,12 +106,12 @@ int opal_btl_usnic_gr_outdegree(const opal_btl_usnic_graph_t *g,
*
* @returns OPAL_SUCCESS or an OMPI error code
*/
int opal_btl_usnic_gr_add_edge(opal_btl_usnic_graph_t *g,
int from,
int to,
int64_t cost,
int capacity,
void *e_data);
int opal_bp_graph_add_edge(opal_bp_graph_t *g,
int from,
int to,
int64_t cost,
int capacity,
void *e_data);
/**
* add a vertex to the given graph
@ -122,16 +122,16 @@ int opal_btl_usnic_gr_add_edge(opal_btl_usnic_graph_t *g,
*
* @returns OPAL_SUCCESS or an OMPI error code
*/
int opal_btl_usnic_gr_add_vertex(opal_btl_usnic_graph_t *g,
void *v_data,
int *index_out);
int opal_bp_graph_add_vertex(opal_bp_graph_t *g,
void *v_data,
int *index_out);
/**
* compute the order of a graph (number of vertices)
*
* @param[in] g the graph to query
*/
int opal_btl_usnic_gr_order(const opal_btl_usnic_graph_t *g);
int opal_bp_graph_order(const opal_bp_graph_t *g);
/**
* This function solves the "assignment problem":
@ -157,7 +157,8 @@ int opal_btl_usnic_gr_order(const opal_btl_usnic_graph_t *g);
*
* @returns OPAL_SUCCESS or an OMPI error code
*/
int opal_btl_usnic_solve_bipartite_assignment(const opal_btl_usnic_graph_t *g,
int *num_match_edges_out,
int **match_edges_out);
#endif /* BTL_USNIC_GRAPH_H */
int opal_bp_graph_solve_bipartite_assignment(const opal_bp_graph_t *g,
int *num_match_edges_out,
int **match_edges_out);
#endif /* OPAL_BP_GRAPH_H */

140
opal/util/bipartite_graph_internal.h Обычный файл
Просмотреть файл

@ -0,0 +1,140 @@
/*
* Copyright (c) 2014 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2017 Amazon.com, Inc. or its affiliates. All Rights
* reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
/*
* This file defines a number of internal structures to the BP graph
* code which need to be exposed only for unit testing. This file
* should not be included in code that uses the BP graph interface.
*/
#ifndef BIPARTITE_GRAPH_INTERNAL
#define BIPARTITE_GRAPH_INTERNAL 1
struct opal_bp_graph_edge_t {
opal_object_t super;
opal_list_item_t outbound_li;
opal_list_item_t inbound_li;
/** source of this edge */
int source;
/** v_index of target of this edge */
int target;
/** cost (weight) of this edge */
int64_t cost;
/**
* (flow-network) capacity of this edge. Zero-capacity edges essentially do
* not exist and will be ignored by most of the algorithms implemented here.
*/
int capacity;
/** any other information associated with this edge */
void *e_data;
};
struct opal_bp_graph_vertex_t {
/** index in the graph's array of vertices */
int v_index;
/** any other information associated with the vertex */
void *v_data;
/** linked list of edges for which this vertex is a source */
opal_list_t out_edges;
/** linked list of edges for which this vertex is a target */
opal_list_t in_edges;
};
struct opal_bp_graph_t {
/** number of vertices currently in this graph */
int num_vertices;
/** vertices in this graph (with number of set elements == num_vertices) */
opal_pointer_array_t vertices;
/** index of the source vertex, or -1 if not present */
int source_idx;
/** index of the sink vertex, or -1 if not present */
int sink_idx;
/** user callback to clean up the v_data */
opal_bp_graph_cleanup_fn_t v_data_cleanup_fn;
/** user callback to clean up the e_data */
opal_bp_graph_cleanup_fn_t e_data_cleanup_fn;
};
#define LIST_FOREACH_CONTAINED(item, list, type, member) \
for (item = container_of( (list)->opal_list_sentinel.opal_list_next, type, member ); \
&item->member != &(list)->opal_list_sentinel; \
item = container_of( \
((opal_list_item_t *) (&item->member))->opal_list_next, type, member ))
#define LIST_FOREACH_SAFE_CONTAINED(item, next, list, type, member) \
for (item = container_of( (list)->opal_list_sentinel.opal_list_next, type, member ), \
next = container_of( \
((opal_list_item_t *) (&item->member))->opal_list_next, type, member ); \
&item->member != &(list)->opal_list_sentinel; \
item = next, \
next = container_of( \
((opal_list_item_t *) (&item->member))->opal_list_next, type, member ))
#define NUM_VERTICES(g) (g->num_vertices)
#define CHECK_VERTEX_RANGE(g,v) \
do { \
if ((v) < 0 || \
(v) >= NUM_VERTICES(g)) { \
return OPAL_ERR_BAD_PARAM; \
} \
} while (0)
/* cast away any constness of &g->vertices b/c the opal_pointer_array API is
* not const-correct */
#define V_ID_TO_PTR(g, v_id) \
((opal_bp_graph_vertex_t *) \
opal_pointer_array_get_item((opal_pointer_array_t *)&g->vertices, v_id))
#define FOREACH_OUT_EDGE(g,v_id,e_ptr) \
LIST_FOREACH_CONTAINED(e_ptr, \
&(V_ID_TO_PTR(g, v_id)->out_edges), \
opal_bp_graph_edge_t, \
outbound_li)
#define FOREACH_IN_EDGE(g,v_id,e_ptr) \
LIST_FOREACH_CONTAINED(e_ptr, \
&(V_ID_TO_PTR(g, v_id)->in_edges), \
opal_bp_graph_edge_t, \
inbound_li)
/* Iterate over (u,v) edge pairs along the given path, where path is defined
* by the predecessor array "pred". Stops when a -1 predecessor is
* encountered. Note: because it is a *predecessor* array, the traversal
* starts at the sink and progresses towards the source. */
#define FOREACH_UV_ON_PATH(pred, source, sink, u, v) \
for (u = pred[sink], v = sink; u != -1; v = u, u = pred[u])
bool opal_bp_graph_bellman_ford(opal_bp_graph_t *gx,
int source,
int target,
int *pred);
int opal_bp_graph_bipartite_to_flow(opal_bp_graph_t *g);
#endif

Просмотреть файл

@ -34,7 +34,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/test/support
check_PROGRAMS = \
opal_bit_ops opal_path_nfs
opal_bit_ops \
opal_path_nfs \
bipartite_graph
TESTS = \
$(check_PROGRAMS)
@ -74,7 +76,6 @@ opal_bit_ops_LDADD = \
$(top_builddir)/test/support/libsupport.a
opal_bit_ops_DEPENDENCIES = $(opal_path_nfs_LDADD)
opal_path_nfs_SOURCES = opal_path_nfs.c
opal_path_nfs_LDADD = \
$(top_builddir)/opal/lib@OPAL_LIB_PREFIX@open-pal.la \
@ -118,6 +119,12 @@ opal_path_nfs_DEPENDENCIES = $(opal_path_nfs_LDADD)
# $(top_builddir)/test/support/libsupport.a
#orte_universe_setup_file_io_DEPENDENCIES = $(orte_universe_setup_file_io_LDADD)
bipartite_graph_SOURCES = bipartite_graph.c
bipartite_graph_LDADD = \
$(top_builddir)/opal/lib@OPAL_LIB_PREFIX@open-pal.la \
$(top_builddir)/test/support/libsupport.a
bipartite_graph_DEPENDENCIES = $(bipartite_graph_LDADD)
clean-local:
rm -f test_session_dir_out test-file opal_path_nfs.out

Разница между файлами не показана из-за своего большого размера Загрузить разницу