-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathgasnet_tm.c
More file actions
348 lines (298 loc) · 13.4 KB
/
gasnet_tm.c
File metadata and controls
348 lines (298 loc) · 13.4 KB
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/* $Source: github.com:BerkeleyLab/gasnet.git/gasnet_team.c $
* Description: GASNet implementation of teams
* Copyright 2018, The Regents of the University of California
* Terms of use are as specified in license.txt
*/
#include <gasnet_coll_internal.h>
// TODO-EX:
//
// Currently we implement a "team" (the abstract distributed collection, of
// which a "tm" is a representative) in terms of the legacy GASNet-1 collective
// team. We should eventually using something smarter, including scalable
// storage.
// Given (tm,rank) return the jobrank
gex_Rank_t gasneti_tm_fwd_rank(gasneti_TM_t tm, gex_Rank_t rank) {
gasnete_coll_team_t team = tm->_coll_team;
gasneti_assert(team != GASNET_TEAM_ALL); // TM0 should not reach here
gasneti_assert(!tm->_rank_map); // Teams with dense rank map should not reach here
gasneti_unreachable_error(("Unimplemented gasneti_tm_fwd_rank() call"));
return GEX_RANK_INVALID;
}
// Given (tm,rank) return the ep_location
gex_EP_Location_t gasneti_tm_fwd_location(gasneti_TM_t tm, gex_Rank_t rank, gex_Flags_t flags) {
gasnete_coll_team_t team = tm->_coll_team;
gasneti_assert(team != GASNET_TEAM_ALL); // TM0 should not reach here
gasneti_assert(!tm->_rank_map); // Teams with dense rank map should not reach here
gasneti_unreachable_error(("Unimplemented gasneti_tm_fwd_location() call"));
gex_EP_Location_t result = {GEX_RANK_INVALID, 0};
return result;
}
// Given (tm,jobrank) return the rank or GEX_RANK_INVALID
// TODO-EX: THIS IS A HORRIBLE O(size(tm)) SCAN!
gex_Rank_t gasneti_tm_rev_rank(gasneti_TM_t tm, gex_Rank_t jobrank) {
gasnete_coll_team_t team = tm->_coll_team;
gasneti_assert(team != GASNET_TEAM_ALL); // TM0 should not reach here
gex_Rank_t size = tm->_size;
gasneti_assert_uint(size ,==, team->total_ranks);
for (gex_Rank_t rank = 0; rank < size; ++rank) {
if (team->rel2act_map[rank] == jobrank) return rank;
}
return GEX_RANK_INVALID;
}
static size_t
get_scratch_size(gex_Rank_t new_tm_size, gex_Flags_t flags)
{
// Specially defined cases
if (0 == new_tm_size) return 0;
if (1 == new_tm_size) return GASNETE_COLL_SCRATCH_SIZE_MIN;
static size_t recommended;
static int is_init = 0;
if_pf (!is_init) {
static gasneti_mutex_t lock = GASNETI_MUTEX_INITIALIZER;
gasneti_mutex_lock(&lock);
if (!is_init) {
recommended = gasneti_getenv_int_withdefault("GASNET_COLL_SCRATCH_SIZE",
GASNETE_COLL_SCRATCH_SIZE_DEFAULT,1);
// Silently raise to implementation-defined minimum
recommended = MAX(recommended, GASNETE_COLL_SCRATCH_SIZE_MIN);
gasneti_sync_writes();
is_init = 1;
}
gasneti_mutex_unlock(&lock);
} else {
gasneti_sync_reads();
}
return recommended;
}
size_t gasneti_TM_Split(gex_TM_t *new_tm_p, gex_TM_t e_parent, int color, int key,
void *addr, size_t len, gex_Flags_t flags
GASNETI_THREAD_FARG)
{
gasneti_TM_t i_parent = gasneti_import_tm_nonpair(e_parent);
gasneti_EP_t ep = i_parent->_ep;
GASNETI_TRACE_PRINTF(O,("gex_TM_Split: parent="GASNETI_TMSELFFMT" color=%d key=%d flags=%d",
GASNETI_TMSELFSTR(e_parent), color, key, flags));
GASNETI_CHECK_INJECT();
static int did_warn = 0;
if ((flags & GEX_FLAG_TM_SCRATCH_SIZE_MIN) && !did_warn) {
if (! i_parent->_rank) {
gasneti_console_message("WARNING",
"gex_TM_Split() called using GEX_FLAG_TM_SCRATCH_SIZE_MIN, "
"deprecated since specification 0.11.");
}
did_warn = 1; // Some process did, even if it was not us.
}
#if GASNET_DEBUG
if ((flags & GEX_FLAG_TM_SCRATCH_SIZE_MIN) &&
(flags & GEX_FLAG_TM_SCRATCH_SIZE_RECOMMENDED)) {
gasneti_fatalerror("Call to gex_TM_Split() with mutually-exclusive "
"GEX_FLAG_TM_SCRATCH_SIZE_MIN and "
"GEX_FLAG_TM_SCRATCH_SIZE_RECOMMENDED both set in flags argument");
}
#endif
if (flags & (GEX_FLAG_TM_SCRATCH_SIZE_MIN | GEX_FLAG_TM_SCRATCH_SIZE_RECOMMENDED)) {
// Don't know true size w/o comms, but singleton parent can only produce singleton children
size_t result = new_tm_p ? get_scratch_size(i_parent->_size, flags) : 0;
GASNETI_TRACE_PRINTF(O,("gex_TM_Split: scratch size query result=%"PRIuSZ, result));
return result;
}
// Split's scratch address is GEX_FLAG_TM_LOCAL_SCRATCH by default,
// but GEX_FLAG_TM_NO_SCRATCH is also accepted.
// TODO: support GEX_FLAG_TM_SYMMETRIC_SCRATCH too
if (flags & GEX_FLAG_TM_GLOBAL_SCRATCH) {
gasneti_fatalerror("Invalid call to gex_TM_Split with GEX_FLAG_TM_GLOBAL_SCRATCH");
} else if (flags & GEX_FLAG_TM_SYMMETRIC_SCRATCH) {
gasneti_fatalerror("Invalid call to gex_TM_Split with GEX_FLAG_TM_SYMMETRIC_SCRATCH");
} else if (! (flags & GEX_FLAG_TM_NO_SCRATCH)) {
flags |= GEX_FLAG_TM_LOCAL_SCRATCH;
}
if (!new_tm_p) {
color = -1; // tell gasnete_coll_team_split() not to create a team for this caller
} else {
gasneti_assert_int(color ,>=, 0);
if (! (flags & GEX_FLAG_TM_NO_SCRATCH)) {
#if !GASNET_SEGMENT_EVERYTHING
gasneti_assert(ep->_segment);
gasneti_assert_ptr(addr ,>=, ep->_segment->_addr);
gasneti_assert_ptr((uint8_t*)addr+len ,<=, ep->_segment->_ub);
#endif
if (!len) {
gasneti_fatalerror("Invalid call to gex_TM_Split with scratch_size = 0");
}
}
}
gasnete_coll_team_t team =
gasnete_coll_team_split(i_parent->_coll_team, color, key,
len, addr, flags GASNETI_THREAD_PASS);
if (team == NULL) {
gasneti_assert(!new_tm_p);
GASNETI_TRACE_PRINTF(O,("gex_TM_Split: parent="GASNETI_TMSELFFMT" [No team created]",
GASNETI_TMSELFSTR(e_parent)));
return 0;
}
gasneti_TM_t i_tm = gasneti_alloc_tm(ep, team->myrank, team->total_ranks, flags);
i_tm->_coll_team = team;
gex_TM_t e_tm = gasneti_export_tm(i_tm);
team->e_tm = e_tm;
*new_tm_p = e_tm;
i_tm->_rank_map = team->rel2act_map;
i_tm->_index_map = NULL; // TODO-EX: provide this for teams w/ non-primordial EPs
GASNETI_TRACE_PRINTF(O,("gex_TM_Split: parent="GASNETI_TMSELFFMT" result="GASNETI_TMSELFFMT,
GASNETI_TMSELFSTR(e_parent), GASNETI_TMSELFSTR(e_tm)));
GASNETI_STAT_EVENT(O, TEAM_NEW_SPLIT);
return 1; // return is documented as undefined
}
// TODO-EX: implement full generality given in specification
// MISSING support for:
// + non-primordial EPs
// - (num_new_tms > 1)
// - non-zero gex_ep_index
// - caller's EP not in members[]
// + GEX_FLAG_SCRATCH_SEG_OFFSET
size_t gasneti_TM_Create(
gex_TM_t *new_tms,
size_t num_new_tms,
gex_TM_t e_parent,
gex_EP_Location_t *members,
size_t nmembers,
gex_Addr_t *scratch_addrs,
size_t scratch_size,
gex_Flags_t flags
GASNETI_THREAD_FARG)
{
size_t result = 0;
gasneti_TM_t i_parent = gasneti_import_tm_nonpair(e_parent);
// NOTE: we can simplify things by observing that ranks in TM0 are always jobranks
flags |= gasneti_is_tm0(i_parent) ? GEX_FLAG_RANK_IS_JOBRANK : 0;
gasneti_EP_t ep = i_parent->_ep;
int is_jobrank = (flags & GEX_FLAG_RANK_IS_JOBRANK);
GASNETI_TRACE_PRINTF(O,("gex_TM_Create: parent="GASNETI_TMSELFFMT" num_new_tms=%"PRIuSZ" nmembers=%"PRIuSZ" scratch_size=%"PRIuSZ" flags=%d",
GASNETI_TMSELFSTR(e_parent), num_new_tms, nmembers, scratch_size, flags));
GASNETI_CHECK_INJECT();
static int did_warn = 0;
if ((flags & GEX_FLAG_TM_SCRATCH_SIZE_MIN) && !did_warn) {
if (! i_parent->_rank) {
gasneti_console_message("WARNING",
"gex_TM_Create() called using GEX_FLAG_TM_SCRATCH_SIZE_MIN, "
"deprecated since specification 0.11.");
}
did_warn = 1; // Some process did, even if it was not us.
}
#if GASNET_DEBUG
if ((flags & GEX_FLAG_TM_SCRATCH_SIZE_MIN) &&
(flags & GEX_FLAG_TM_SCRATCH_SIZE_RECOMMENDED)) {
gasneti_fatalerror("Call to gex_TM_Create() with mutually-exclusive "
"GEX_FLAG_TM_SCRATCH_SIZE_MIN and "
"GEX_FLAG_TM_SCRATCH_SIZE_RECOMMENDED both set in flags argument");
}
#endif
// For now 0 or 1 are the only valid numbers of outputs.
gasneti_assert(!nmembers || num_new_tms == 1);
if (flags & (GEX_FLAG_TM_SCRATCH_SIZE_MIN | GEX_FLAG_TM_SCRATCH_SIZE_RECOMMENDED)) {
size_t result = nmembers ? get_scratch_size(nmembers, flags) : 0;
GASNETI_TRACE_PRINTF(O,("gex_TM_Create: scratch size query result=%"PRIuSZ, result));
return result;
}
if (num_new_tms && nmembers) {
if (!scratch_size && !(flags & GEX_FLAG_TM_NO_SCRATCH)) {
gasneti_fatalerror("Invalid call to gex_TM_Create with scratch_size = 0");
}
GASNETI_TRACE_PRINTF(D,("gex_TM_Create: members[ %s ]", gasneti_format_eploc(members, nmembers)));
}
// TODO-EX: remove when subteam collectives no longer require a parent-scope entry barrier
gasnete_coll_consensus_barrier(i_parent->_coll_team GASNETI_THREAD_PASS);
if (! nmembers) {
GASNETI_TRACE_PRINTF(O,("gex_TM_Create: parent="GASNETI_TMSELFFMT" [No team created]",
GASNETI_TMSELFSTR(e_parent)));
goto done;
}
// TODO: missing sanity checks on args, especially single-valued assertions
// Find self in members[]
gex_Rank_t my_member_rank = is_jobrank
? gasneti_i_tm_rank_to_jobrank(i_parent, i_parent->_rank)
: i_parent->_rank;
gex_Rank_t my_new_rank = GEX_RANK_INVALID;
for (gex_Rank_t r = 0; r < nmembers; ++r) {
if (members[r].gex_rank == my_member_rank && members[r].gex_ep_index == ep->_index) {
my_new_rank = r;
break;
}
}
if (my_new_rank == GEX_RANK_INVALID) { // TODO-EX: not the right test or message in multi-EP case
gasneti_fatalerror("Call to gex_TM_Create with nmembers=%"PRIuSZ" but 0 local members found in in members[]",
nmembers);
}
// Generate rank_map[], which the new team will own
gex_Rank_t *rank_map = gasneti_malloc(nmembers * sizeof(gex_Rank_t));
for (gex_Rank_t r = 0; r < nmembers; ++r) {
gasneti_assert_always_uint(members[r].gex_ep_index ,==, 0);
gex_Rank_t tmp = members[r].gex_rank;
rank_map[r] = is_jobrank ? tmp : gasneti_i_tm_rank_to_jobrank(i_parent, tmp);
}
gasnete_coll_team_t team = gasnete_coll_team_create(
i_parent->_coll_team, nmembers,
my_new_rank, rank_map,
scratch_size, scratch_addrs, flags
GASNETI_THREAD_PASS);
gasneti_TM_t i_tm = gasneti_alloc_tm(ep, my_new_rank, nmembers, flags);
i_tm->_coll_team = team;
gex_TM_t e_tm = gasneti_export_tm(i_tm);
team->e_tm = e_tm;
new_tms[0] = e_tm;
i_tm->_rank_map = team->rel2act_map;
i_tm->_index_map = NULL; // TODO-EX: provide this for teams w/ non-primordial EPs
// TODO-EX: outut only correct for num_new_tms==1
GASNETI_TRACE_PRINTF(O,("gex_TM_Create: parent="GASNETI_TMSELFFMT" rank=%d size=%d result="GASNETI_TMSELFFMT,
GASNETI_TMSELFSTR(e_parent), my_new_rank, (int)nmembers, GASNETI_TMSELFSTR(e_tm)));
GASNETI_STAT_EVENT(O, TEAM_NEW_CREATE);
result = 1; // return is documented as undefined
done:
return result;
}
int gasneti_TM_Destroy(
gex_TM_t e_tm,
gex_Memvec_t *scratch_p,
gex_Flags_t flags
GASNETI_THREAD_FARG)
{
gasneti_TM_t i_tm = gasneti_import_tm_nonpair(e_tm);
gasnete_coll_team_t team = i_tm->_coll_team;
GASNETI_TRACE_PRINTF(O,("gex_TM_Destroy: team="GASNETI_TMSELFFMT" flags=%d",
GASNETI_TMSELFSTR(e_tm), flags));
if (1) { // TODO: w/ multi-EP exactly one tm per proc should log this event
GASNETI_STAT_EVENT(O, TEAM_DESTROY);
}
GASNETI_CHECK_INJECT();
if (gasneti_is_tm0(i_tm)) {
gasneti_fatalerror("Invalid gasneti_TM_Destroy() of the primordial team");
}
if (! (flags & GEX_FLAG_GLOBALLY_QUIESCED)) {
gasnete_coll_consensus_barrier(team GASNETI_THREAD_PASS);
}
gasneti_free_tm(i_tm);
return gasnete_coll_team_free(team, scratch_p);
}
/* ------------------------------------------------------------------------------------ */
// Simple blocking Exchange utility function
extern void gasneti_blockingExchange(gex_TM_t tm, void *src, size_t len, void *dst)
{
// TODO-EX: use gex_Coll_Exchange() once available
const int coll_flags = GASNET_COLL_LOCAL | GASNET_COLL_IN_MYSYNC | GASNET_COLL_OUT_MYSYNC;
gasnet_coll_gather_all(gasneti_import_tm_nonpair(tm)->_coll_team, dst, src, len, coll_flags);
}
/* ------------------------------------------------------------------------------------ */
/* TM trace formatting - legal even without STATS/TRACE */
// Format a gex_TM_t as a GUID
extern const char *gasneti_formattm(gex_TM_t e_tm) {
if (e_tm == NULL) return "JOB"; // JobRank, as with token and legacy collectives
if (gasneti_e_tm_is_pair(e_tm)) {
gasneti_TM_Pair_t pair = gasneti_import_tm_pair(e_tm);
gex_EP_Index_t loc_idx = gasneti_tm_pair_loc_idx(pair);
gex_EP_Index_t rem_idx = gasneti_tm_pair_rem_idx(pair);
return gasneti_dynsprintf("TM_PAIR(%x,%x)", loc_idx, rem_idx);
} else {
gasnete_coll_team_t team = gasneti_import_tm(e_tm)->_coll_team;
if (team == NULL) return "TM0"; // Team0 before end of Client_Init
return gasneti_dynsprintf("TM%x", (unsigned int)team->team_id);
}
}