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