HAMR
The Heterogeneous Accelerator Memory Resource
hamr_buffer_handle.h
1 #ifndef hamr_buffer_handle_h
2 #define hamr_buffer_handle_h
3 
4 #include "hamr_config.h"
5 #include "hamr_env.h"
7 #include "hamr_gil_state.h"
8 
9 #include <Python.h>
10 #include <memory>
11 #include <iostream>
12 
13 namespace hamr
14 {
15 /// traits for Numpy's array interface protocol
16 template <typename cpp_t> struct array_interface_tt
17 {};
18 
19 #define array_interface_tt_declare(ENDIAN, KIND, SIZE, CPP_T) \
20 /** CPP_T traits for Numpy's array interface protocol */ \
21 template <> struct array_interface_tt<CPP_T> \
22 { \
23  /** number of bytes per element (SIZE) */ \
24  static constexpr size_t elemsize() \
25  { \
26  return SIZE; \
27  } \
28  /** char code for the type (KIND) */ \
29  static constexpr char typekind() \
30  { \
31  return (#KIND) [0]; \
32  } \
33  /** true if the machine order is little endian */ \
34  static constexpr bool little_endian() \
35  { \
36  return (#ENDIAN) [0] == '<'; \
37  } \
38  /** Numpy type descriptor string (ENDIAN KIND SIZE) */ \
39  static constexpr const char *descr() \
40  { \
41  return #ENDIAN #KIND #SIZE; \
42  } \
43 };
44 array_interface_tt_declare(<, i, 1, char)
45 array_interface_tt_declare(<, i, 2, short)
46 array_interface_tt_declare(<, i, 4, int)
47 array_interface_tt_declare(<, i, 8, long)
48 array_interface_tt_declare(<, i, 8, long long)
49 array_interface_tt_declare(<, u, 1, unsigned char)
50 array_interface_tt_declare(<, u, 2, unsigned short)
51 array_interface_tt_declare(<, u, 4, unsigned int)
52 array_interface_tt_declare(<, u, 8, unsigned long)
53 array_interface_tt_declare(<, u, 8, unsigned long long)
54 array_interface_tt_declare(<, f, 4, float)
55 array_interface_tt_declare(<, f, 8, double)
56 
57 
58 /// type traits for constructing SWIG wrapped objects
59 template <typename cpp_t> struct buffer_handle_tt
60 {};
61 
62 #define buffer_handle_tt_declare(CPP_T, SWIG_T) \
63 template <> struct buffer_handle_tt<CPP_T> \
64 { \
65  static swig_type_info *swig_type() \
66  { \
67  return SWIG_T; \
68  }; \
69 };
70 
71 buffer_handle_tt_declare(float, SWIGTYPE_p_hamr__buffer_handleT_float_t)
72 buffer_handle_tt_declare(double, SWIGTYPE_p_hamr__buffer_handleT_double_t)
73 buffer_handle_tt_declare(char, SWIGTYPE_p_hamr__buffer_handleT_char_t)
74 buffer_handle_tt_declare(short, SWIGTYPE_p_hamr__buffer_handleT_short_t)
75 buffer_handle_tt_declare(int, SWIGTYPE_p_hamr__buffer_handleT_int_t)
76 buffer_handle_tt_declare(long, SWIGTYPE_p_hamr__buffer_handleT_long_t)
77 buffer_handle_tt_declare(long long, SWIGTYPE_p_hamr__buffer_handleT_long_long_t)
78 buffer_handle_tt_declare(unsigned char, SWIGTYPE_p_hamr__buffer_handleT_unsigned_char_t)
79 buffer_handle_tt_declare(unsigned short, SWIGTYPE_p_hamr__buffer_handleT_unsigned_short_t)
80 buffer_handle_tt_declare(unsigned int, SWIGTYPE_p_hamr__buffer_handleT_unsigned_int_t)
81 buffer_handle_tt_declare(unsigned long, SWIGTYPE_p_hamr__buffer_handleT_unsigned_long_t)
82 buffer_handle_tt_declare(unsigned long long, SWIGTYPE_p_hamr__buffer_handleT_unsigned_long_long_t)
83 
84 
85 /** A resource management class that is used to keep data shared with Python
86  * codes from a ::hamr_buffer alive while it is being accessed by the Python
87  * codes. The class also implements the Numpy array interface protocol and
88  * the Numba CUDA array interface protocol enabling seamless access by those
89  * libraries.
90  */
91 template <typename T>
92 class HAMR_EXPORT buffer_handle
93 {
94 public:
95  /// construct an empty, and unusable object
96  buffer_handle() : m_data(nullptr), m_size(0),
97  m_read_only(0), m_host_accessible(0), m_cuda_accessible(0),
98  m_stream(0)
99  {}
100 
101  /// construct from existing data
102  buffer_handle(const std::shared_ptr<T> &src, size_t size,
103  int read_only, int host_accessible, int cuda_accessible,
104  size_t stream);
105 
106  /// construct from existing read only data
107  buffer_handle(const std::shared_ptr<const T> &src, size_t size,
108  int host_accessible, int cuda_accessible, size_t stream);
109 
110  /// destruct
111  ~buffer_handle();
112 
113  /// copy construct
114  buffer_handle(const buffer_handle<T> &other);
115 
116  /// move construct
118 
119  /// copy assign
120  buffer_handle<T> &operator=(const buffer_handle<T> &) = default;
121 
122  /// move assign
123  buffer_handle<T> &operator=(buffer_handle<T> &&) = default;
124 
125 
126  /** returns a dictionary as described in the Numba CUDA array interface
127  * protocol if the data is accessible from CUDA. Otherwise, an
128  * AttributeError is rasied. */
129  PyObject *get_cuda_array_interface();
130 
131  /** returns a dictionary as described in the Numpy __array_interface__
132  * protocol if the data is accessible from CUDA. Otherwise, an
133  * AttributeError is rasied. */
134  PyObject *get_numpy_array_interface();
135 
136 //private:
137  /** returns a dictionary as decribed in the Numpy array interface and Numba
138  * CUDA array interface protocols. The caller must ensure the data is
139  * accessible in the desired technology before use. See
140  * ::get_numpy_array_interface and ::get_cuda_array_interface which
141  * implement check ensuring that this is true. */
142  PyObject *get_array_interface();
143 
144  void to_stream(std::ostream &os) const;
145 
146  std::shared_ptr<T> m_data;
147  size_t m_size;
148  int m_read_only;
149  int m_host_accessible;
150  int m_cuda_accessible;
151  size_t m_stream;
152 };
153 
154 // **************************************************************************
155 template <typename T>
156 std::ostream &operator<<(std::ostream &os, const buffer_handle<T> &buf)
157 {
158  buf.to_stream(os);
159  return os;
160 }
161 
162 // --------------------------------------------------------------------------
163 template <typename T>
164 void buffer_handle<T>::to_stream(std::ostream &os) const
165 {
166  os << "buffer_handle<" << typeid(T).name() << sizeof(T)
167  << "> m_data = " << size_t(this->m_data.get())
168  << " m_size = " << this->m_size << " m_read_only = "
169  << this->m_read_only << " m_host_accessible = "
170  << this->m_host_accessible << " m_cuda_accessible = "
171  << this->m_cuda_accessible;
172 }
173 
174 // --------------------------------------------------------------------------
175 template <typename T>
176 buffer_handle<T>::buffer_handle(const std::shared_ptr<T> &src,
177  size_t size, int read_only, int host_accessible, int cuda_accessible,
178  size_t stream) : m_data(src), m_size(size), m_read_only(read_only),
179  m_host_accessible(host_accessible), m_cuda_accessible(cuda_accessible),
180  m_stream(stream)
181 {
182  if (hamr::get_verbose())
183  {
184  std::cerr << "buffer_handle::construct " << *this << std::endl;
185  }
186 }
187 
188 // --------------------------------------------------------------------------
189 template <typename T>
190 buffer_handle<T>::buffer_handle(const std::shared_ptr<const T> &src,
191  size_t size, int host_accessible, int cuda_accessible,
192  size_t stream) : buffer_handle(std::const_pointer_cast<T>(src), size,
194 {
195 }
196 
197 // --------------------------------------------------------------------------
198 template <typename T>
200 {
201  if (hamr::get_verbose())
202  {
203  std::cerr << "buffer_handle::destruct " << *this << std::endl;
204  }
205 }
206 
207 // --------------------------------------------------------------------------
208 template <typename T>
210  m_data(other.m_data), m_size(other.m_size),
211  m_read_only(other.m_read_only), m_host_accessible(other.m_host_accessible),
212  m_cuda_accessible(other.m_cuda_accessible), m_stream(other.m_stream)
213 {
214  if (hamr::get_verbose())
215  {
216  std::cerr << "buffer_handle::copy construct " << *this << std::endl;
217  }
218 }
219 
220 // --------------------------------------------------------------------------
221 template <typename T>
223  m_data(std::move(other.m_data)), m_size(other.m_size),
224  m_read_only(other.m_read_only), m_host_accessible(other.m_host_accessible),
225  m_cuda_accessible(other.m_cuda_accessible), m_stream(other.m_stream)
226 {
227  if (hamr::get_verbose())
228  {
229  std::cerr << "buffer_handle::move construct " << *this << std::endl;
230  }
231 }
232 
233 // --------------------------------------------------------------------------
234 template <typename T>
236 {
237  hamr::gil_state gil;
238 
239  if (!m_cuda_accessible)
240  {
241  PyErr_SetString(PyExc_AttributeError,
242  "The data is not accessible from CUDA");
243  Py_RETURN_NONE;
244  }
245 
246  if (hamr::get_verbose())
247  {
248  std::cerr << "buffer_handle<" << typeid(T).name() << sizeof(T)
249  << ">::get_cuda_array_interface()" << std::endl;
250  }
251 
252  return this->get_array_interface();
253 }
254 
255 // --------------------------------------------------------------------------
256 template <typename T>
258 {
259  hamr::gil_state gil;
260 
261  if (!m_host_accessible)
262  {
263  PyErr_SetString(PyExc_AttributeError,
264  "The data is not accessible from the host");
265  Py_RETURN_NONE;
266  }
267 
268  if (hamr::get_verbose())
269  {
270  std::cerr << "buffer_handle<" << typeid(T).name() << sizeof(T)
271  << ">::get_numpy_array_interface()" << std::endl;
272  }
273 
274  return this->get_array_interface();
275 }
276 
277 // --------------------------------------------------------------------------
278 template <typename T>
280 {
281  // shape
282  PyObject *shape = PyTuple_New(1);
283  PyTuple_SetItem(shape, 0, PyLong_FromLong(m_size));
284 
285  // typestr
286  PyObject *typestr = PyUnicode_FromString(array_interface_tt<T>::descr());
287  Py_INCREF(typestr); // for use in descr
288 
289  // descr
290  PyObject *descr_tup = PyTuple_New(2);
291  PyTuple_SetItem(descr_tup, 0, PyUnicode_FromString(""));
292  PyTuple_SetItem(descr_tup, 1, typestr);
293  PyObject *descr = PyList_New(1);
294  PyList_SetItem(descr, 0, descr_tup);
295 
296  // data
297  PyObject *data = PyTuple_New(2);
298  PyTuple_SetItem(data, 0, PyLong_FromSize_t(size_t(m_data.get())));
299  PyTuple_SetItem(data, 1, PyBool_FromLong(m_read_only));
300 
301  // strides
302  PyObject *strides = Py_None;
303 
304  // mask
305  PyObject *mask = Py_None;
306 
307  // stream
308  PyObject *strm = PyLong_FromSize_t(m_stream);
309 
310  // version
311  PyObject *version = PyLong_FromLong(3);
312 
313  // build the __array_interface__ dictionary
314  PyObject *aint = PyDict_New();
315  PyDict_SetItemString(aint, "shape", shape);
316  PyDict_SetItemString(aint, "typestr", typestr);
317  PyDict_SetItemString(aint, "descr", descr);
318  PyDict_SetItemString(aint, "data", data);
319  PyDict_SetItemString(aint, "strides", strides);
320  PyDict_SetItemString(aint, "mask", mask);
321  PyDict_SetItemString(aint, "stream", strm);
322  PyDict_SetItemString(aint, "version", version);
323 
324  Py_DECREF(shape);
325  Py_DECREF(typestr);
326  Py_DECREF(descr);
327  Py_DECREF(data);
328  Py_DECREF(strm);
329  Py_DECREF(version);
330 
331  return aint;
332 }
333 
334 }
335 #endif
hamr::buffer_handle::get_numpy_array_interface
PyObject * get_numpy_array_interface()
Definition: hamr_buffer_handle.h:257
hamr::array_interface_tt
traits for Numpy's array interface protocol
Definition: hamr_buffer_handle.h:16
hamr::gil_state
A RAII helper for managing the Python GIL.
Definition: hamr_gil_state.h:13
hamr::buffer_handle::get_cuda_array_interface
PyObject * get_cuda_array_interface()
Definition: hamr_buffer_handle.h:235
hamr::stream
A wrapper around technology specific streams.
Definition: hamr_stream.h:35
hamr::buffer_handle::buffer_handle
buffer_handle()
construct an empty, and unusable object
Definition: hamr_buffer_handle.h:96
hamr::buffer_handle_tt
type traits for constructing SWIG wrapped objects
Definition: hamr_buffer_handle.h:59
hamr_buffer_allocator.h
hamr::get_verbose
constexpr HAMR_EXPORT int get_verbose()
returns the value of the HAMR_VERBOSE environment variable
Definition: hamr_env.h:14
hamr::buffer_handle::get_array_interface
PyObject * get_array_interface()
Definition: hamr_buffer_handle.h:279
hamr::host_accessible
HAMR_EXPORT int host_accessible(buffer_allocator alloc)
Definition: hamr_buffer_allocator.h:35
hamr::buffer_handle
Definition: hamr_buffer_handle.h:92
hamr
heterogeneous accelerator memory resource
Definition: hamr_buffer.h:13
hamr::buffer_handle::~buffer_handle
~buffer_handle()
destruct
Definition: hamr_buffer_handle.h:199
hamr::cuda_accessible
HAMR_EXPORT int cuda_accessible(buffer_allocator alloc)
Definition: hamr_buffer_allocator.h:47
hamr::data
auto data(PP &&... args)
Definition: hamr_buffer_util.h:148