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 */