1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
2 /*
3 * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2010 The University of Tennessee and The University
7 * of Tennessee Research Foundation. All rights
8 * reserved.
9 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
10 * University of Stuttgart. All rights reserved.
11 * Copyright (c) 2004-2005 The Regents of the University of California.
12 * All rights reserved.
13 * Copyright (c) 2006-2007 Los Alamos National Security, LLC. All rights
14 * reserved.
15 * Copyright (c) 2007-2008 UT-Battelle, LLC
16 * Copyright (c) 2007-2009 Cisco Systems, Inc. All rights reserved.
17 * Copyright (c) 2013-2015 Los Alamos National Security, LLC. All rights
18 * reserved.
19 * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved.
20 * $COPYRIGHT$
21 *
22 * Additional copyrights may follow
23 *
24 * $HEADER$
25 */
26 /**
27 * @file
28 *
29 * MPI_Op back-end operation framework. This framework allows
30 * component-izing the back-end operations of MPI_Op in order to use
31 * specialized hardware (e.g., mathematical accelerators). In short:
32 * each MPI_Op contains a table of function pointers; one for
33 * implementing the operation on each predefined datatype.
34 *
35 * The MPI interface provides error checking and error handler
36 * invocation, but the op components provide all other functionality.
37 *
38 * Component selection is done on a per-MPI_Op basis when each MPI_Op
39 * is created. All MPI_Ops go through the selection process, even
40 * user-defined MPI_Ops -- although it is expected that most (all?)
41 * op components will only be able to handle the predefined MPI_Ops.
42 *
43 * The general sequence of usage for the op framework is:
44 *
45 * 1. ompi_op_base_open() is invoked during MPI_INIT to find/open all
46 * op components.
47 *
48 * 2. ompi_op_base_find_available() is invoked during MPI_INIT to call
49 * each successfully opened op component's opc_init_query() function.
50 * All op components that return OMPI_SUCCESS are kept; all others are
51 * closed and removed from the process.
52 *
53 * 3. ompi_op_base_op_select() is invoked during MPI_INIT for each
54 * predefined MPI_Op (e.g., MPI_SUM). This function will call each
55 * available op component's opc_op_query() function to see if this
56 * component wants to provide a module for one or more of the function
57 * pointers on this MPI_Op. Priorities are used to rank returned
58 * modules; the module with the highest priority has its function
59 * pointers set in the MPI_Op function table.
60 *
61 * Note that a module may only have *some* non-NULL function pointers
62 * (i.e., for the functions that it can support). For example, some
63 * modules may only support operations on single-precision floating
64 * point datatypes. These modules would provide function pointers for
65 * these datatypes and NULL for all the rest. The op framework will
66 * mix-n-match function pointers between modules to obtain a full set
67 * of non-NULL function pointers for a given MPI_Op (note that the op
68 * base provides a complete set of functions for the MPI_Op, usually a
69 * simple C loop around the operation, such as "+=" -- so even if
70 * there is no specialized op component available, there will *always*
71 * be a full set of MPI_Op function pointers). The op framework will
72 * OBJ_RETAIN an op module once for each function pointer where it is
73 * used on a given MPI_Op.
74 *
75 * Note that this scheme can result in up to N different modules being
76 * used for a single MPI_Op, one per needed datatype function.
77 *
78 * 5. Finally, during MPI_FINALIZE, ompi_op_base_close() is invoked to
79 * close all available op components.
80 */
81
82 #ifndef MCA_OP_H
83 #define MCA_OP_H
84
85 #include "ompi_config.h"
86
87 #include "opal/class/opal_object.h"
88 #include "ompi/mca/mca.h"
89
90 /*
91 * This file includes some basic struct declarations (but not
92 * definitions) just so that we can avoid including files like op/op.h
93 * and datatype/datatype.h, which would create #include file loops.
94 */
95 #include "ompi/types.h"
96
97 BEGIN_C_DECLS
98
99 /**
100 * Corresponding to the types that we can reduce over. See
101 * MPI-1:4.9.2, p114-115 and
102 * MPI-2:4.15, p76-77
103 */
104 enum {
105 /** C integer: int8_t */
106 OMPI_OP_BASE_TYPE_INT8_T,
107 /** C integer: uint8_t */
108 OMPI_OP_BASE_TYPE_UINT8_T,
109 /** C integer: int16_t */
110 OMPI_OP_BASE_TYPE_INT16_T,
111 /** C integer: uint16_t */
112 OMPI_OP_BASE_TYPE_UINT16_T,
113 /** C integer: int32_t */
114 OMPI_OP_BASE_TYPE_INT32_T,
115 /** C integer: uint32_t */
116 OMPI_OP_BASE_TYPE_UINT32_T,
117 /** C integer: int64_t */
118 OMPI_OP_BASE_TYPE_INT64_T,
119 /** C integer: uint64_t */
120 OMPI_OP_BASE_TYPE_UINT64_T,
121
122 /** Fortran integer */
123 OMPI_OP_BASE_TYPE_INTEGER,
124 /** Fortran integer*1 */
125 OMPI_OP_BASE_TYPE_INTEGER1,
126 /** Fortran integer*2 */
127 OMPI_OP_BASE_TYPE_INTEGER2,
128 /** Fortran integer*4 */
129 OMPI_OP_BASE_TYPE_INTEGER4,
130 /** Fortran integer*8 */
131 OMPI_OP_BASE_TYPE_INTEGER8,
132 /** Fortran integer*16 */
133 OMPI_OP_BASE_TYPE_INTEGER16,
134
135 /** Floating point: short float */
136 OMPI_OP_BASE_TYPE_SHORT_FLOAT,
137 /** Floating point: float */
138 OMPI_OP_BASE_TYPE_FLOAT,
139 /** Floating point: double */
140 OMPI_OP_BASE_TYPE_DOUBLE,
141 /** Floating point: real */
142 OMPI_OP_BASE_TYPE_REAL,
143 /** Floating point: real*2 */
144 OMPI_OP_BASE_TYPE_REAL2,
145 /** Floating point: real*4 */
146 OMPI_OP_BASE_TYPE_REAL4,
147 /** Floating point: real*8 */
148 OMPI_OP_BASE_TYPE_REAL8,
149 /** Floating point: real*16 */
150 OMPI_OP_BASE_TYPE_REAL16,
151 /** Floating point: double precision */
152 OMPI_OP_BASE_TYPE_DOUBLE_PRECISION,
153 /** Floating point: long double */
154 OMPI_OP_BASE_TYPE_LONG_DOUBLE,
155
156 /** Logical */
157 OMPI_OP_BASE_TYPE_LOGICAL,
158 /** Bool */
159 OMPI_OP_BASE_TYPE_BOOL,
160
161 /** Complex */
162 /* short float complex */
163 OMPI_OP_BASE_TYPE_C_SHORT_FLOAT_COMPLEX,
164 /* float complex */
165 OMPI_OP_BASE_TYPE_C_FLOAT_COMPLEX,
166 /* double complex */
167 OMPI_OP_BASE_TYPE_C_DOUBLE_COMPLEX,
168 /* long double complex */
169 OMPI_OP_BASE_TYPE_C_LONG_DOUBLE_COMPLEX,
170
171 /** Byte */
172 OMPI_OP_BASE_TYPE_BYTE,
173
174 /** 2 location Fortran: 2 real */
175 OMPI_OP_BASE_TYPE_2REAL,
176 /** 2 location Fortran: 2 double precision */
177 OMPI_OP_BASE_TYPE_2DOUBLE_PRECISION,
178 /** 2 location Fortran: 2 integer */
179 OMPI_OP_BASE_TYPE_2INTEGER,
180
181 /** 2 location C: float int */
182 OMPI_OP_BASE_TYPE_FLOAT_INT,
183 /** 2 location C: double int */
184 OMPI_OP_BASE_TYPE_DOUBLE_INT,
185 /** 2 location C: long int */
186 OMPI_OP_BASE_TYPE_LONG_INT,
187 /** 2 location C: int int */
188 OMPI_OP_BASE_TYPE_2INT,
189 /** 2 location C: short int */
190 OMPI_OP_BASE_TYPE_SHORT_INT,
191 /** 2 location C: long double int */
192 OMPI_OP_BASE_TYPE_LONG_DOUBLE_INT,
193
194 /** 2 location C: wchar_t */
195 OMPI_OP_BASE_TYPE_WCHAR,
196
197 /** Maximum type */
198 OMPI_OP_BASE_TYPE_MAX
199 };
200
201
202 /**
203 * Fortran handles; must be [manually set to be] equivalent to the
204 * values in mpif.h.
205 */
206 enum {
207 /** Corresponds to Fortran MPI_OP_NULL */
208 OMPI_OP_BASE_FORTRAN_NULL = 0,
209 /** Corresponds to Fortran MPI_MAX */
210 OMPI_OP_BASE_FORTRAN_MAX,
211 /** Corresponds to Fortran MPI_MIN */
212 OMPI_OP_BASE_FORTRAN_MIN,
213 /** Corresponds to Fortran MPI_SUM */
214 OMPI_OP_BASE_FORTRAN_SUM,
215 /** Corresponds to Fortran MPI_PROD */
216 OMPI_OP_BASE_FORTRAN_PROD,
217 /** Corresponds to Fortran MPI_LAND */
218 OMPI_OP_BASE_FORTRAN_LAND,
219 /** Corresponds to Fortran MPI_BAND */
220 OMPI_OP_BASE_FORTRAN_BAND,
221 /** Corresponds to Fortran MPI_LOR */
222 OMPI_OP_BASE_FORTRAN_LOR,
223 /** Corresponds to Fortran MPI_BOR */
224 OMPI_OP_BASE_FORTRAN_BOR,
225 /** Corresponds to Fortran MPI_LXOR */
226 OMPI_OP_BASE_FORTRAN_LXOR,
227 /** Corresponds to Fortran MPI_BXOR */
228 OMPI_OP_BASE_FORTRAN_BXOR,
229 /** Corresponds to Fortran MPI_MAXLOC */
230 OMPI_OP_BASE_FORTRAN_MAXLOC,
231 /** Corresponds to Fortran MPI_MINLOC */
232 OMPI_OP_BASE_FORTRAN_MINLOC,
233 /** Corresponds to Fortran MPI_REPLACE */
234 OMPI_OP_BASE_FORTRAN_REPLACE,
235 /** Corresponds to Fortran MPI_NO_OP */
236 OMPI_OP_BASE_FORTRAN_NO_OP,
237
238 /** Maximum value */
239 OMPI_OP_BASE_FORTRAN_OP_MAX
240 };
241
242 /**
243 * Pre-declare this so that we can pass it as an argument to the
244 * typedef'ed functions.
245 */
246 struct ompi_op_base_module_1_0_0_t;
247
248 typedef struct ompi_op_base_module_1_0_0_t ompi_op_base_module_t;
249
250 /**
251 * Typedef for 2-buffer op functions.
252 *
253 * We don't use MPI_User_function because this would create a
254 * confusing dependency loop between this file and mpi.h. So this is
255 * repeated code, but it's better this way (and this typedef will
256 * never change, so there's not much of a maintenance worry).
257 */
258 typedef void (*ompi_op_base_handler_fn_1_0_0_t)(void *, void *, int *,
259 struct ompi_datatype_t **,
260 struct ompi_op_base_module_1_0_0_t *);
261
262 typedef ompi_op_base_handler_fn_1_0_0_t ompi_op_base_handler_fn_t;
263
264 /*
265 * Typedef for 3-buffer (two input and one output) op functions.
266 */
267 typedef void (*ompi_op_base_3buff_handler_fn_1_0_0_t)(void *,
268 void *,
269 void *, int *,
270 struct ompi_datatype_t **,
271 struct ompi_op_base_module_1_0_0_t *);
272
273 typedef ompi_op_base_3buff_handler_fn_1_0_0_t ompi_op_base_3buff_handler_fn_t;
274
275 /**
276 * Op component initialization
277 *
278 * Initialize the given op component. This function should initialize
279 * any component-level. data. It will be called exactly once during
280 * MPI_INIT.
281 *
282 * @note The component framework is not lazily opened, so attempts
283 * should be made to minimze the amount of memory allocated during
284 * this function.
285 *
286 * @param[in] enable_progress_threads True if the component needs to
287 * support progress threads
288 * @param[in] enable_mpi_threads True if the component needs to
289 * support MPI_THREAD_MULTIPLE
290 *
291 * @retval OMPI_SUCCESS Component successfully initialized
292 * @retval OMPI_ERROR An unspecified error occurred
293 */
294 typedef int (*ompi_op_base_component_init_query_fn_t)
295 (bool enable_progress_threads, bool enable_mpi_threads);
296
297
298 /**
299 * Query whether a component is available for a specific MPI_Op.
300 *
301 * If the component is available, an object should be allocated and
302 * returned (with refcount at 1). The module will not be used for
303 * reduction operations until module_enable() is called on the module,
304 * but may be destroyed (via OBJ_RELEASE) either before or after
305 * module_enable() is called. If the module needs to release
306 * resources obtained during query(), it should do so in the module
307 * destructor.
308 *
309 * A component may provide NULL to this function to indicate it does
310 * not wish to run or return an error during module_enable().
311 *
312 * @param[in] op The MPI_Op being created
313 * @param[out] priority Priority setting for component on
314 * this op
315 *
316 * @returns An initialized module structure if the component can
317 * provide a module with the requested functionality or NULL if the
318 * component should not be used on the given communicator.
319 */
320 typedef struct ompi_op_base_module_1_0_0_t *
321 (*ompi_op_base_component_op_query_1_0_0_fn_t)
322 (struct ompi_op_t *op, int *priority);
323
324 /**
325 * Op component interface.
326 *
327 * Component interface for the op framework. A public instance of
328 * this structure, called mca_op_[component_name]_component, must
329 * exist in any op component.
330 */
331 typedef struct ompi_op_base_component_1_0_0_t {
332 /** Base component description */
333 mca_base_component_t opc_version;
334 /** Base component data block */
335 mca_base_component_data_t opc_data;
336
337 /** Component initialization function */
338 ompi_op_base_component_init_query_fn_t opc_init_query;
339 /** Query whether component is useable for given op */
340 ompi_op_base_component_op_query_1_0_0_fn_t opc_op_query;
341 } ompi_op_base_component_1_0_0_t;
342
343
344 /** Per guidence in mca.h, use the unversioned struct name if you just
345 want to always keep up with the most recent version of the
346 interace. */
347 typedef struct ompi_op_base_component_1_0_0_t ompi_op_base_component_t;
348
349 /**
350 * Module initialization function. Should return OPAL_SUCCESS if
351 * everything goes ok. This function can be NULL in the module struct
352 * if the module doesn't need to do anything between the component
353 * query function and being invoked for MPI_Op operations.
354 */
355 typedef int (*ompi_op_base_module_enable_1_0_0_fn_t)
356 (struct ompi_op_base_module_1_0_0_t *module,
357 struct ompi_op_t *op);
358
359 /**
360 * Module struct
361 */
362 typedef struct ompi_op_base_module_1_0_0_t {
363 /** Op modules all inherit from opal_object */
364 opal_object_t super;
365
366 /** Enable function called when an op module is (possibly) going
367 to be used for the given MPI_Op */
368 ompi_op_base_module_enable_1_0_0_fn_t opm_enable;
369
370 /** Just for reference -- a pointer to the MPI_Op that this module
371 is being used for */
372 struct ompi_op_t *opm_op;
373
374 /** Function pointers for all the different datatypes to be used
375 with the MPI_Op that this module is used with */
376 ompi_op_base_handler_fn_1_0_0_t opm_fns[OMPI_OP_BASE_TYPE_MAX];
377 ompi_op_base_3buff_handler_fn_1_0_0_t opm_3buff_fns[OMPI_OP_BASE_TYPE_MAX];
378 } ompi_op_base_module_1_0_0_t;
379
380 /**
381 * Declare the module as a class, unversioned
382 */
383 OMPI_DECLSPEC OBJ_CLASS_DECLARATION(ompi_op_base_module_t);
384
385 /**
386 * Declare the module as a class, unversioned
387 */
388 OMPI_DECLSPEC OBJ_CLASS_DECLARATION(ompi_op_base_module_1_0_0_t);
389
390 /**
391 * Struct that is used in op.h to hold all the function pointers and
392 * pointers to the corresopnding modules (so that we can properly
393 * RETAIN/RELEASE them)
394 */
395 typedef struct ompi_op_base_op_fns_1_0_0_t {
396 ompi_op_base_handler_fn_1_0_0_t fns[OMPI_OP_BASE_TYPE_MAX];
397 ompi_op_base_module_t *modules[OMPI_OP_BASE_TYPE_MAX];
398 } ompi_op_base_op_fns_1_0_0_t;
399
400 typedef ompi_op_base_op_fns_1_0_0_t ompi_op_base_op_fns_t;
401
402 /**
403 * Struct that is used in op.h to hold all the function pointers and
404 * pointers to the corresopnding modules (so that we can properly
405 * RETAIN/RELEASE them)
406 */
407 typedef struct ompi_op_base_op_3buff_fns_1_0_0_t {
408 ompi_op_base_3buff_handler_fn_1_0_0_t fns[OMPI_OP_BASE_TYPE_MAX];
409 ompi_op_base_module_t *modules[OMPI_OP_BASE_TYPE_MAX];
410 } ompi_op_base_op_3buff_fns_1_0_0_t;
411
412 typedef ompi_op_base_op_3buff_fns_1_0_0_t ompi_op_base_op_3buff_fns_t;
413
414 /*
415 * Macro for use in modules that are of type op v2.0.0
416 */
417 #define OMPI_OP_BASE_VERSION_1_0_0 \
418 OMPI_MCA_BASE_VERSION_2_1_0("op", 1, 0, 0)
419
420 END_C_DECLS
421
422 #endif /* OMPI_MCA_OP_H */