1 /*
2 * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
3 * University Research and Technology
4 * Corporation. All rights reserved.
5 * Copyright (c) 2004-2007 The University of Tennessee and The University
6 * of Tennessee Research Foundation. All rights
7 * reserved.
8 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9 * University of Stuttgart. All rights reserved.
10 * Copyright (c) 2004-2005 The Regents of the University of California.
11 * All rights reserved.
12 * Copyright (c) 2008-2009 Cisco Systems, Inc. All rights reserved.
13 * $COPYRIGHT$
14 *
15 * Additional copyrights may follow
16 *
17 * $HEADER$
18 */
19
20 /** @file
21 *
22 * This is the max module source code. It contains the "setup"
23 * functions that will create a module for the MPI_MAX MPI_Op.
24 */
25
26 #include "ompi_config.h"
27
28 #include "opal/class/opal_object.h"
29 #include "opal/util/output.h"
30
31 #include "ompi/constants.h"
32 #include "ompi/op/op.h"
33 #include "ompi/mca/op/op.h"
34 #include "ompi/mca/op/base/base.h"
35 #include "ompi/mca/op/example/op_example.h"
36
37 /**
38 * Derive a struct from the base op module struct, allowing us to
39 * cache some module-specific information for MAX. Note that
40 * information that should be shared across all modules should be put
41 * on the example component.
42 */
43 typedef struct {
44 ompi_op_base_module_1_0_0_t super;
45
46 /* Just like the ompi_op_example_component_t, this struct is meant to
47 cache information on a per-module basis. What follows are
48 examples; replace them with whatever is relevant for your
49 component/module. Keep in mind that there will be one distinct
50 module for each MPI_Op; you may want to have different data
51 cached on the module, depending on the MPI_Op that it is
52 supporting.
53
54 In this example, we'll keep the fallback function pointers for
55 several integer types. */
56 ompi_op_base_handler_fn_t fallback_float;
57 ompi_op_base_module_t *fallback_float_module;
58 ompi_op_base_handler_fn_t fallback_real;
59 ompi_op_base_module_t *fallback_real_module;
60
61 ompi_op_base_handler_fn_t fallback_double;
62 ompi_op_base_module_t *fallback_double_module;
63 ompi_op_base_handler_fn_t fallback_double_precision;
64 ompi_op_base_module_t *fallback_double_precision_module;
65 } module_max_t;
66
67 /**
68 * "Constructor" for the max module class
69 */
70 static void module_max_constructor(module_max_t *m)
71 {
72 /* Use this function to initialize any data in the class that is
73 specific to this class (i.e. do *not* initialize the parent
74 data members!). */
75 m->fallback_float = NULL;
76 m->fallback_float_module = NULL;
77 m->fallback_real = NULL;
78 m->fallback_real_module = NULL;
79
80 m->fallback_double = NULL;
81 m->fallback_double_module = NULL;
82 m->fallback_double_precision = NULL;
83 m->fallback_double_precision_module = NULL;
84 }
85
86 /**
87 * "Destructor" for the max module class
88 */
89 static void module_max_destructor(module_max_t *m)
90 {
91 /* Use this function to clean up any data members that may be
92 necessary. This may include freeing resources and/or setting
93 members to sentinel values to know that the object has been
94 destructed. */
95 m->fallback_float = (ompi_op_base_handler_fn_t) 0xdeadbeef;
96 m->fallback_float_module = (ompi_op_base_module_t*) 0xdeadbeef;
97 m->fallback_real = (ompi_op_base_handler_fn_t) 0xdeadbeef;
98 m->fallback_real_module = (ompi_op_base_module_t*) 0xdeadbeef;
99
100 m->fallback_double = (ompi_op_base_handler_fn_t) 0xdeadbeef;
101 m->fallback_double_module = (ompi_op_base_module_t*) 0xdeadbeef;
102 m->fallback_double_precision = (ompi_op_base_handler_fn_t) 0xdeadbeef;
103 m->fallback_double_precision_module = (ompi_op_base_module_t*) 0xdeadbeef;
104 }
105
106 /**
107 * Setup the class for the max module, listing:
108 * - the name of the class
109 * - the "parent" of the class
110 * - function pointer for the constructor (or NULL)
111 * - function pointer for the destructor (or NULL)
112 */
113 static OBJ_CLASS_INSTANCE(module_max_t,
114 ompi_op_base_module_t,
115 module_max_constructor,
116 module_max_destructor);
117
118 /**
119 * Max function for C float
120 */
121 static void max_float(void *in, void *out, int *count,
122 ompi_datatype_t **type, ompi_op_base_module_t *module)
123 {
124 module_max_t *m = (module_max_t*) module;
125
126 /* Be chatty to the output, just so that we can see that this
127 function was called */
128 opal_output(0, "In example max float function");
129
130 /* This is where you can decide at run-time whether to use the
131 hardware or the fallback function. For example, you could have
132 logic something like this:
133
134 extent = *count * size(int);
135 if (memory_accessible_on_hw(in, extent) &&
136 memory_accessible_on_hw(out, extent)) {
137 ...do the function on hardware...
138 } else if (extent >= large_enough) {
139 ...copy host memory -> hardware memory...
140 ...do the function on hardware...
141 ...copy hardware memory -> host memory...
142 } else {
143 m->fallback_float(in, out, count, type, m->fallback_int_module);
144 }
145 */
146
147 /* But for this example, we'll just call the fallback function to
148 actually do the work */
149 m->fallback_float(in, out, count, type, m->fallback_float_module);
150 }
151
152 /**
153 * Max function for C double
154 */
155 static void max_double(void *in, void *out, int *count,
156 ompi_datatype_t **type, ompi_op_base_module_t *module)
157 {
158 module_max_t *m = (module_max_t*) module;
159 opal_output(0, "In example max double function");
160
161 /* Just another example function -- similar to max_int() */
162
163 m->fallback_double(in, out, count, type, m->fallback_double_module);
164 }
165
166 /**
167 * Max function for Fortran REAL
168 */
169 static void max_real(void *in, void *out, int *count,
170 ompi_datatype_t **type, ompi_op_base_module_t *module)
171 {
172 module_max_t *m = (module_max_t*) module;
173 opal_output(0, "In example max real function");
174
175 /* Just another example function -- similar to max_int() */
176
177 m->fallback_real(in, out, count, type, m->fallback_real_module);
178 }
179
180 /**
181 * Max function for Fortran DOUBLE PRECISION
182 */
183 static void max_double_precision(void *in, void *out, int *count,
184 ompi_datatype_t **type,
185 ompi_op_base_module_t *module)
186 {
187 module_max_t *m = (module_max_t*) module;
188 opal_output(0, "In example max double precision function");
189
190 /* Just another example function -- similar to max_int() */
191
192 m->fallback_double_precision(in, out, count, type,
193 m->fallback_double_precision_module);
194 }
195
196 /**
197 * Setup function for MPI_MAX. If we get here, we can assume that a)
198 * the hardware is present, b) the MPI thread scenario is what we
199 * want, and c) the MAX operation is supported. So this function's
200 * job is to create a module and fill in function pointers for the
201 * functions that this hardware supports.
202 */
203 ompi_op_base_module_t *ompi_op_example_setup_max(ompi_op_t *op)
204 {
205 module_max_t *module = OBJ_NEW(module_max_t);
206
207 /* We defintely support the single precision floating point types */
208
209 /* Remember that we created an *example* module (vs. a *base*
210 module), so we can cache extra information on there that is
211 specific for the MAX operation. Let's cache the original
212 fallback function pointers, that were passed to us in this call
213 (i.e., they're already assigned on the op). */
214
215 /* C float */
216 module->super.opm_fns[OMPI_OP_BASE_TYPE_FLOAT] = max_float;
217 module->fallback_float = op->o_func.intrinsic.fns[OMPI_OP_BASE_TYPE_FLOAT];
218 module->fallback_float_module =
219 op->o_func.intrinsic.modules[OMPI_OP_BASE_TYPE_FLOAT];
220 /* If you cache a fallback function, you *must* RETAIN (i.e.,
221 increase the refcount) its module so that the module knows that
222 it is being used and won't be freed/destructed. */
223 OBJ_RETAIN(module->fallback_float_module);
224
225 /* Fortran REAL */
226 module->super.opm_fns[OMPI_OP_BASE_TYPE_REAL] = max_real;
227 module->fallback_real =
228 op->o_func.intrinsic.fns[OMPI_OP_BASE_TYPE_REAL];
229 module->fallback_real_module =
230 op->o_func.intrinsic.modules[OMPI_OP_BASE_TYPE_REAL];
231 OBJ_RETAIN(module->fallback_real_module);
232
233 /* Does our hardware support double precision? */
234
235 if (mca_op_example_component.double_supported) {
236 /* C double */
237 module->super.opm_fns[OMPI_OP_BASE_TYPE_DOUBLE] = max_double;
238 module->fallback_double =
239 op->o_func.intrinsic.fns[OMPI_OP_BASE_TYPE_DOUBLE];
240 module->fallback_double_module =
241 op->o_func.intrinsic.modules[OMPI_OP_BASE_TYPE_DOUBLE];
242 OBJ_RETAIN(module->fallback_double_module);
243
244 /* Fortran DOUBLE PRECISION */
245 module->super.opm_fns[OMPI_OP_BASE_TYPE_DOUBLE_PRECISION] =
246 max_double_precision;
247 module->fallback_double_precision =
248 op->o_func.intrinsic.fns[OMPI_OP_BASE_TYPE_DOUBLE_PRECISION];
249 module->fallback_double_precision_module =
250 op->o_func.intrinsic.modules[OMPI_OP_BASE_TYPE_DOUBLE_PRECISION];
251 OBJ_RETAIN(module->fallback_double_precision_module);
252 }
253
254 /* ...not listing the rest of the floating point-typed functions
255 in this example... */
256
257 return (ompi_op_base_module_t*) module;
258 }