JayBeams  0.1
Another project to have fun coding.
plan.hpp
Go to the documentation of this file.
1 #ifndef jb_clfft_plan_hpp
2 #define jb_clfft_plan_hpp
3 
5 #include <jb/clfft/error.hpp>
6 #include <jb/clfft/init.hpp>
7 #include <jb/complex_traits.hpp>
8 
9 #include <boost/compute/command_queue.hpp>
10 #include <boost/compute/container/vector.hpp>
11 #include <boost/compute/context.hpp>
12 #include <boost/compute/event.hpp>
13 #include <boost/compute/utility/wait_list.hpp>
14 
15 namespace jb {
16 namespace clfft {
17 /**
18  * Wrap clfftPlanHandle objects.
19  *
20  * The clFFT library optimizes execution by pre-computing
21  * coefficients, execution plans, and OpenCL kernels for a given
22  * input. The plan also stores the precision (float vs. double), and
23  * the input and output types (e.g. float to std::complex<float>
24  * vs. std::complex<float> to std::complex<float>).
25  *
26  * In JayBeams we prefer to store such details in the type system, and
27  * create a different plan type for different input and output types.
28  *
29  * @tparam in_timeseries_type the type of the input timeseries,
30  * typically an instantiation of boost::compute::vector<T>.
31  * @tparam out_timseries_type the type of the output timeseries,
32  * typically an instantiation of boost::compute::vector<T>.
33  */
34 template <typename in_timeseries_type, typename out_timeseries_type>
35 class plan {
36 public:
37  //@{
38  /**
39  * type straits
40  */
41  using in_value_type = typename in_timeseries_type::value_type;
42  using out_value_type = typename out_timeseries_type::value_type;
45  // The precision type must be the same for out_value_type and
46  // in_value_type, this is statically checked by
47  // jb::fftw::plan::check_constraints.
48  using precision_type =
50  //@}
51 
52  /// Default constructor
53  plan()
54  : p_()
55  , d_(ENDDIRECTION) {
56  }
57 
58  /// Basic move constructor
59  plan(plan&& rhs)
60  : p_(rhs.p_)
61  , d_(rhs.d_) {
62  plan tmp;
63  rhs.p_ = tmp.p_;
64  rhs.d_ = tmp.d_;
65  }
66 
67  /// Move assigment operator
68  plan& operator=(plan&& rhs) {
69  plan tmp(std::move(rhs));
70  std::swap(p_, tmp.p_);
71  std::swap(d_, tmp.d_);
72  return *this;
73  }
74 
75  /// Destructor, cleanup the plan.
76  ~plan() noexcept(false) {
77  check_constraints checker;
78  if (d_ == ENDDIRECTION) {
79  return;
80  }
81  cl_int err = clfftDestroyPlan(&p_);
82  jb::clfft::check_error_code(err, "clfftDestroyPlan");
83  }
84 
85  /**
86  * Enqueue the transform to be executed.
87  *
88  * As it is often the case with OpenCL, this is an asynchronous
89  * operation. The transform is scheduled to be executed, but it may
90  * not have completed by the time this function returns. One must
91  * wait for the returned event to reach the @a done state before any
92  * operations can depend on the state of the output.
93  *
94  * @param out the output location for the data, must have the same
95  * size as the prototype used to construct the plan.
96  * @param in the input for the data, must have the same size as the
97  * prototype used to construct the plan.
98  * @param queue where to schedule the computation on.
99  * @param wait a set of events to wait for before starting the
100  * computation.
101  *
102  * @returns a event that when satisfied indicates the computation
103  * has completed.
104  * @throws std::exception if there is an error scheduling the
105  * computation.
106  */
107  boost::compute::event enqueue(
108  out_timeseries_type& out, in_timeseries_type const& in,
109  boost::compute::command_queue& queue,
110  boost::compute::wait_list const& wait = boost::compute::wait_list()) {
111  boost::compute::event event;
112  cl_int err = clfftEnqueueTransform(
113  p_, d_, 1, &queue.get(), wait.size(), wait.get_event_ptr(),
114  &event.get(), &in.get_buffer().get(), &out.get_buffer().get(), nullptr);
115  jb::clfft::check_error_code(err, "clfftEnqueueTransform");
116  return event;
117  }
118 
119 private:
120  // forward declare a helper type to check compile-time constraints.
121  struct check_constraints;
122 
123  // grant access to create_*_impl functions
124  template <typename itype, typename otype>
126  otype const&, itype const&, boost::compute::context&,
127  boost::compute::command_queue&, int);
128  template <typename itype, typename otype>
130  otype const&, itype const&, boost::compute::context&,
131  boost::compute::command_queue&, int);
132 
133  /**
134  * Refactor code to create plans.
135  *
136  * @param out the output destination prototype. The actual output
137  * in execute() must have the same size as this parameter.
138  * @param in the input data prototype. The actual input in
139  * execute() must have the same size as this parameter.
140  * @param context a collection of devices in a single OpenCL platform.
141  * @param queue a command queue associated with @a context.
142  * @param direction the direction of the transform
143  * @param batch_size the size of the batch, the size of @a out and
144  * @a in must be a multiple of this number.
145  *
146  * @returns a new plan that computes a DFT (or the inverse DFT) from
147  * a vector like @a in into a vector like @a out.
148  * @throws std::exception if the parameters are invalid or there was
149  * an OpenCL or clFFT error.
150  */
152  out_timeseries_type const& out, in_timeseries_type const& in,
153  boost::compute::context& context, boost::compute::command_queue& queue,
154  clfftDirection direction, std::size_t batch_size) {
155  if (out.size() != in.size()) {
156  throw std::invalid_argument("clfft::plan - size mismatch");
157  }
158  if (batch_size == 0) {
159  throw std::invalid_argument("clfft::plan - 0 is not a valid batch size");
160  }
161  if (in.size() % batch_size != 0) {
162  throw std::invalid_argument("clfft::plan - the input / output sizes must"
163  " be multiples of the batch size");
164  }
165  // The number of dimension (or rank) of the FFT, all our FFTs are
166  // in one dimension, hence the function names ...
167  clfftDim dim = CLFFT_1D;
168 
169  // ... the length on each dimension, if we had more dimensions
170  // this would be an array with 2 or 3 elements ...
171  std::size_t lengths[] = {in.size()};
172 
173  // ... create a default plan ...
174  clfftPlanHandle plan;
175  cl_int err = clfftCreateDefaultPlan(&plan, context.get(), dim, lengths);
176 
177  // ... clFFT uses ugly error checking, turn into exceptions if
178  // needed ...
179  jb::clfft::check_error_code(err, "clfftCreateDefaultPlan");
180 
181  // ... set the precision ...
182  err = clfftSetPlanPrecision(plan, in_value_traits::precision);
183  jb::clfft::check_error_code(err, "clfftSetPlanPrecision");
184 
185  // ... use the traits to determine the layout of the data ...
186  err =
187  clfftSetLayout(plan, in_value_traits::layout, out_value_traits::layout);
188  jb::clfft::check_error_code(err, "clfftSetLayout");
189 
190  // ... store the results of the FFT in a second buffer, it is
191  // possible to save memory by storing in the same buffer ...
192  err = clfftSetResultLocation(plan, CLFFT_OUTOFPLACE);
193  jb::clfft::check_error_code(err, "clfftSetResultLocation");
194 
195  // ... the size of the 'batch' ...
196  err = clfftSetPlanBatchSize(plan, batch_size);
197  jb::clfft::check_error_code(err, "clfftSetPlanBatchSize");
198 
199  // ... compute the plan, notice that this blocks because the plan
200  // won't be ready until the operations queued here complete ...
201  err = clfftBakePlan(plan, 1, &queue.get(), nullptr, nullptr);
202  jb::clfft::check_error_code(err, "clfftBakePlan");
203 
204  // ... here is the blocking operation ...
205  queue.finish();
206 
207  // ... return the wrapper ...
209  plan, direction);
210  }
211 
212  //@{
213  /**
214  * @name Deleted functions
215  */
216  plan(plan const&) = delete;
217  plan& operator=(plan const&) = delete;
218  //@}
219 
220 private:
221  /// Constructor from a clfftPlanHandle
222  explicit plan(clfftPlanHandle p, clfftDirection d)
223  : p_(p)
224  , d_(d) {
225  }
226 
227 private:
228  clfftPlanHandle p_;
229  clfftDirection d_;
230 };
231 
232 /**
233  * Create a forward DFT plan.
234  *
235  * @param out the output destination prototype. The actual output
236  * in execute() must have the same size as this parameter.
237  * @param in the input data prototype. The actual input in
238  * execute() must have the same size as this parameter.
239  * @param ct a collection of devices in a single OpenCL platform.
240  * @param q a command queue associated with @a ct to create
241  * and bake the plan.
242  * @param batch_size the number of timeseries in the input and output
243  * vectors.
244  *
245  * @returns a new plan that computes a DFT from a vector like @a in
246  * into a vector like @a out.
247  * @throws std::exception if the parameters are invalid or there was
248  * an OpenCL or clFFT error.
249  * @param batch_size the number of timeseries in the vector.
250  *
251  * @tparam invector the type of the input vector
252  * @tparam outvector the type of the output vector
253  */
254 template <typename invector, typename outvector>
256  outvector const& out, invector const& in, boost::compute::context& ct,
257  boost::compute::command_queue& q, int batch_size = 1) {
259  out, in, ct, q, CLFFT_FORWARD, batch_size);
260 }
261 
262 /**
263  * Create an inverse DFT plan.
264  *
265  * @param out the output destination prototype. The actual output
266  * in execute() must have the same size as this parameter.
267  * @param in the input data prototype. The actual input in
268  * execute() must have the same size as this parameter.
269  * @param ct a collection of devices in a single OpenCL platform.
270  * @param q a command queue associated with @a ct to create
271  * and bake the plan.
272  * @param batch_size the number of timeseries in the input and output
273  * vectors.
274  *
275  * @returns a new plan that computes a DFT from a vector like @a in
276  * into a vector like @a out.
277  * @throws std::exception if the parameters are invalid or there was
278  * an OpenCL or clFFT error.
279  *
280  * @tparam invector the type of the input vector
281  * @tparam outvector the type of the output vector
282  */
283 template <typename invector, typename outvector>
285  outvector const& out, invector const& in, boost::compute::context& ct,
286  boost::compute::command_queue& q, int batch_size = 1) {
288  out, in, ct, q, CLFFT_BACKWARD, batch_size);
289 }
290 
291 /**
292  * Check the compile-time constraints for a jb::fftw::plan<>
293  */
294 template <typename in_timeseries_type, typename out_timeseries_type>
295 struct plan<in_timeseries_type, out_timeseries_type>::check_constraints {
297  using in_value_type = typename in_timeseries_type::value_type;
298  using out_value_type = typename out_timeseries_type::value_type;
299  using in_precision_type =
301  using out_precision_type =
303  static_assert(
304  std::is_same<in_precision_type, out_precision_type>::value,
305  "Mismatched precision_type, both timeseries must have the same"
306  " precision");
307  static_assert(
308  std::is_same<in_precision_type, float>::value or
309  std::is_same<in_precision_type, double>::value,
310  "Unsupported precision type, clFFT only supports double or float");
311  }
312 };
313 
314 } // namespace clfft
315 } // namespace jb
316 
317 #endif // jb_plan_hpp
plan(clfftPlanHandle p, clfftDirection d)
Constructor from a clfftPlanHandle.
Definition: plan.hpp:222
clfftPlanHandle p_
Definition: plan.hpp:228
clfftDirection d_
Definition: plan.hpp:229
void check_error_code(cl_int err, char const *msg)
Check in an OpenCL error code is really an error and raise an exception if so.
Definition: error.hpp:45
typename jb::extract_value_type< in_value_type >::precision precision_type
Definition: plan.hpp:49
friend plan< itype, otype > create_forward_plan_1d(otype const &, itype const &, boost::compute::context &, boost::compute::command_queue &, int)
~plan() noexcept(false)
Destructor, cleanup the plan.
Definition: plan.hpp:76
boost::compute::event enqueue(out_timeseries_type &out, in_timeseries_type const &in, boost::compute::command_queue &queue, boost::compute::wait_list const &wait=boost::compute::wait_list())
Enqueue the transform to be executed.
Definition: plan.hpp:107
Check the compile-time constraints for a jb::fftw::plan<>
Definition: plan.hpp:295
static plan create_plan_1d_impl(out_timeseries_type const &out, in_timeseries_type const &in, boost::compute::context &context, boost::compute::command_queue &queue, clfftDirection direction, std::size_t batch_size)
Refactor code to create plans.
Definition: plan.hpp:151
friend plan< itype, otype > create_inverse_plan_1d(otype const &, itype const &, boost::compute::context &, boost::compute::command_queue &, int)
plan(plan &&rhs)
Basic move constructor.
Definition: plan.hpp:59
Wrap clfftPlanHandle objects.
Definition: plan.hpp:35
typename in_timeseries_type::value_type in_value_type
type straits
Definition: plan.hpp:41
plan()
Default constructor.
Definition: plan.hpp:53
typename out_timeseries_type::value_type out_value_type
Definition: plan.hpp:42
plan & operator=(plan &&rhs)
Move assigment operator.
Definition: plan.hpp:68
Generic version, not implemented.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7