cognomen.c 4.34 KB
Newer Older
fogal1's avatar
fogal1 committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 *  This file is part of Cognomen.
 *
 *  Cognomen is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Cognomen is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Cognomen.  If not, see <http://www.gnu.org/licenses/>.
 *****
 * Main library implementation.
 *****/
#include "cognomen.h"

/** Needed for gethostid on some systems. */
#ifndef __USE_BSD
#   define __USE_BSD
#endif
#include <assert.h>
26
#include <stdlib.h>
ghweber's avatar
ghweber committed
27
#include <limits.h>
28
29
30
31

#if defined(_WIN32)
#include <win32-hostid.h>
#else
ghweber's avatar
ghweber committed
32
#include <unistd.h>
33
#endif
fogal1's avatar
fogal1 committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

#include <mpi.h>

/** Map which associates MPI ranks with host ids.  Element `x' is the host id
 * for rank `x'. */
static long *map_id = NULL;

static int mpi_size();
static int mpi_rank();
static void *xmalloc(size_t bytes);

/** Initialization for the Cognomen library.
 * identify must be called by all nodes in lockstep.  It initializes
 * the library and performs node identification.  You only need to do this once
 * per process (run). */
void
cog_identify()
{
    int sz;
    long id;
fogal1's avatar
fogal1 committed
54
    int proc;
fogal1's avatar
fogal1 committed
55
56
57
58
59
60
61
62
63
64
    const int rank = mpi_rank();

    id = gethostid();
    sz = mpi_size();

    if(map_id) {
        free(map_id);
    }
    map_id = (long *) xmalloc(sizeof(long) * sz);

fogal1's avatar
fogal1 committed
65
    for(proc=0; proc < sz; ++proc) {
fogal1's avatar
fogal1 committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
        if(proc == rank) {
            MPI_Bcast(&id, 1, MPI_LONG, proc, MPI_COMM_WORLD);
            map_id[proc] = id;
        } else {
            long recv;
            MPI_Bcast(&recv, 1, MPI_LONG, proc, MPI_COMM_WORLD);
            map_id[proc] = recv;
        }
    }
}

/** Returns the cognomen identifier for this process.  All processes on the
 * same node will receive the same identifier. */
void
cog_me(cog_id * const id)
{
    if(NULL == map_id) {
        return;
    }
    id->id = map_id[mpi_rank()];
}

/** Fill a set with the ranks of processes local to the given process. */
void
cog_set_local(cog_set * const local, const int rank)
{
fogal1's avatar
fogal1 committed
92
    int i;
fogal1's avatar
fogal1 committed
93
    long hostid = map_id[rank];
94
    size_t count=0;
fogal1's avatar
fogal1 committed
95
96
97

    /* first we need to know how big their set should be. */
    size_t n_local=0;
fogal1's avatar
fogal1 committed
98
    for(i=0; i < mpi_size(); ++i) {
fogal1's avatar
fogal1 committed
99
100
101
102
103
104
105
106
        if(map_id[i] == hostid) {
            ++n_local;
        }
    }
    local->set.v = (cog_id *) xmalloc(sizeof(cog_id) * n_local);
    local->set.size = n_local;

    /* Now we can copy all of the ids into the set. */
fogal1's avatar
fogal1 committed
107
    for(i=0; i < mpi_size(); ++i) {
fogal1's avatar
fogal1 committed
108
109
110
111
112
113
114
115
116
117
118
119
120
        if(map_id[i] == hostid) {
            cog_id v;
            v.id = i;
            local->set.v[count++] = v;
        }
    }
    assert(count == n_local);
}

/** @return the minimum rank defined in the given set */
int
cog_set_min(const cog_set * const cset)
{
fogal1's avatar
fogal1 committed
121
    size_t i;
fogal1's avatar
fogal1 committed
122
123
124
    /* We might be able to rely on the set being ordered ...
     * For now we'll just search, though. */
    int min = INT_MAX;
fogal1's avatar
fogal1 committed
125
    for(i=0; i < cset->set.size; ++i) {
fogal1's avatar
fogal1 committed
126
127
128
129
130
131
132
133
134
135
136
        if(cset->set.v[i].id < min) {
            min = cset->set.v[i].id;
        }
    }
    return min;
}

/** @return the maximum rank defined in the given set */
int
cog_set_max(const cog_set * const cset)
{
fogal1's avatar
fogal1 committed
137
    size_t i;
fogal1's avatar
fogal1 committed
138
139
140
    /* Likewise to _min, it seems like we have an ordering guarantee we could
     * optimize this with.  Ignore for now .. */
    int max = -1;
fogal1's avatar
fogal1 committed
141
    for(i=0; i < cset->set.size; ++i) {
fogal1's avatar
fogal1 committed
142
143
144
145
146
147
148
149
        if(cset->set.v[i].id > max) {
            max = cset->set.v[i].id;
        }
    }
    return max;
}

/** @return true if the given rank falls in the given set. */
150
int
fogal1's avatar
fogal1 committed
151
152
cog_set_intersect(const cog_set * const cset, int id)
{
fogal1's avatar
fogal1 committed
153
    size_t i;
fogal1's avatar
fogal1 committed
154
    /* Essentially a search for the given rank in a set. */
fogal1's avatar
fogal1 committed
155
    for(i=0; i < cset->set.size; ++i) {
fogal1's avatar
fogal1 committed
156
        if(cset->set.v[i].id == id) {
157
            return 1;
fogal1's avatar
fogal1 committed
158
159
        }
    }
160
    return 0;
fogal1's avatar
fogal1 committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
}

static int
mpi_size()
{
    int sz;
    MPI_Comm_size(MPI_COMM_WORLD, &sz);
    return sz;
}

static int
mpi_rank()
{
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    return rank;
}

static void *
xmalloc(size_t bytes)
{
    void *x = malloc(bytes);
    if(NULL == x) {
        abort();
    }
    return x;
}