mxnet
memory.h
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
23 // Acknowledgement: This file originates from incubator-tvm
24 #ifndef MXNET_RUNTIME_MEMORY_H_
25 #define MXNET_RUNTIME_MEMORY_H_
26 
27 #include <cstdlib>
28 #include <utility>
29 #include <type_traits>
30 #include "object.h"
31 
32 namespace mxnet {
33 namespace runtime {
40 template <typename T, typename... Args>
41 inline ObjectPtr<T> make_object(Args&&... args);
42 
43 // Detail implementations after this
44 //
45 // The current design allows swapping the
46 // allocator pattern when necessary.
47 //
48 // Possible future allocator optimizations:
49 // - Arena allocator that gives ownership of memory to arena (deleter_= nullptr)
50 // - Thread-local object pools: one pool per size and alignment requirement.
51 // - Can specialize by type of object to give the specific allocator to each object.
52 
59 template <typename Derived>
61  public:
68  template <typename T, typename... Args>
69  inline ObjectPtr<T> make_object(Args&&... args) {
70  using Handler = typename Derived::template Handler<T>;
71  static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");
72  T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);
73  ptr->type_index_ = T::RuntimeTypeIndex();
74  ptr->deleter_ = Handler::Deleter();
75  return ObjectPtr<T>(ptr);
76  }
77 
85  template <typename ArrayType, typename ElemType, typename... Args>
86  inline ObjectPtr<ArrayType> make_inplace_array(size_t num_elems, Args&&... args) {
87  using Handler = typename Derived::template ArrayHandler<ArrayType, ElemType>;
88  static_assert(std::is_base_of<Object, ArrayType>::value,
89  "make_inplace_array can only be used to create Object");
90  ArrayType* ptr =
91  Handler::New(static_cast<Derived*>(this), num_elems, std::forward<Args>(args)...);
92  ptr->type_index_ = ArrayType::RuntimeTypeIndex();
93  ptr->deleter_ = Handler::Deleter();
94  return ObjectPtr<ArrayType>(ptr);
95  }
96 };
97 
98 // Simple allocator that uses new/delete.
99 class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
100  public:
101  template <typename T>
102  class Handler {
103  public:
104  using StorageType = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
105 
106  template <typename... Args>
107  static T* New(SimpleObjAllocator*, Args&&... args) {
108  // NOTE: the first argument is not needed for SimpleObjAllocator
109  // It is reserved for special allocators that needs to recycle
110  // the object to itself (e.g. in the case of object pool).
111  //
112  // In the case of an object pool, an allocator needs to create
113  // a special chunk memory that hides reference to the allocator
114  // and call allocator's release function in the deleter.
115 
116  // NOTE2: Use inplace new to allocate
117  // This is used to get rid of warning when deleting a virtual
118  // class with non-virtual destructor.
119  // We are fine here as we captured the right deleter during construction.
120  // This is also the right way to get storage type for an object pool.
121  StorageType* data = new StorageType();
122  new (data) T(std::forward<Args>(args)...);
123  return reinterpret_cast<T*>(data);
124  }
125 
127  return Deleter_;
128  }
129 
130  private:
131  static void Deleter_(Object* objptr) {
132  // NOTE: this is important to cast back to T*
133  // because objptr and tptr may not be the same
134  // depending on how sub-class allocates the space.
135  T* tptr = static_cast<T*>(objptr);
136  // It is important to do tptr->T::~T(),
137  // so that we explicitly call the specific destructor
138  // instead of tptr->~T(), which could mean the intention
139  // call a virtual destructor(which may not be available and is not required).
140  tptr->T::~T();
141  delete reinterpret_cast<StorageType*>(tptr);
142  }
143  };
144 
145  // Array handler that uses new/delete.
146  template <typename ArrayType, typename ElemType>
147  class ArrayHandler {
148  public:
149  using StorageType = typename std::aligned_storage<sizeof(ArrayType), alignof(ArrayType)>::type;
150  // for now only support elements that aligns with array header.
151  static_assert(alignof(ArrayType) % alignof(ElemType) == 0 &&
152  sizeof(ArrayType) % alignof(ElemType) == 0,
153  "element alignment constraint");
154 
155  template <typename... Args>
156  static ArrayType* New(SimpleObjAllocator*, size_t num_elems, Args&&... args) {
157  // NOTE: the first argument is not needed for ArrayObjAllocator
158  // It is reserved for special allocators that needs to recycle
159  // the object to itself (e.g. in the case of object pool).
160  //
161  // In the case of an object pool, an allocator needs to create
162  // a special chunk memory that hides reference to the allocator
163  // and call allocator's release function in the deleter.
164  // NOTE2: Use inplace new to allocate
165  // This is used to get rid of warning when deleting a virtual
166  // class with non-virtual destructor.
167  // We are fine here as we captured the right deleter during construction.
168  // This is also the right way to get storage type for an object pool.
169  size_t unit = sizeof(StorageType);
170  size_t requested_size = num_elems * sizeof(ElemType) + sizeof(ArrayType);
171  size_t num_storage_slots = (requested_size + unit - 1) / unit;
172  StorageType* data = new StorageType[num_storage_slots];
173  new (data) ArrayType(std::forward<Args>(args)...);
174  return reinterpret_cast<ArrayType*>(data);
175  }
176 
178  return Deleter_;
179  }
180 
181  private:
182  static void Deleter_(Object* objptr) {
183  // NOTE: this is important to cast back to ArrayType*
184  // because objptr and tptr may not be the same
185  // depending on how sub-class allocates the space.
186  ArrayType* tptr = static_cast<ArrayType*>(objptr);
187  // It is important to do tptr->ArrayType::~ArrayType(),
188  // so that we explicitly call the specific destructor
189  // instead of tptr->~ArrayType(), which could mean the intention
190  // call a virtual destructor(which may not be available and is not required).
191  tptr->ArrayType::~ArrayType();
192  StorageType* p = reinterpret_cast<StorageType*>(tptr);
193  delete[] p;
194  }
195  };
196 };
197 
198 template <typename T, typename... Args>
199 inline ObjectPtr<T> make_object(Args&&... args) {
200  return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);
201 }
202 
203 template <typename ArrayType, typename ElemType, typename... Args>
204 inline ObjectPtr<ArrayType> make_inplace_array_object(size_t num_elems, Args&&... args) {
205  return SimpleObjAllocator().make_inplace_array<ArrayType, ElemType>(num_elems,
206  std::forward<Args>(args)...);
207 }
208 
209 } // namespace runtime
210 } // namespace mxnet
211 #endif // MXNET_RUNTIME_MEMORY_H_
mxnet
namespace of mxnet
Definition: api_registry.h:33
mxnet::runtime::Object
base class of all object containers.
Definition: object.h:151
mxnet::runtime::SimpleObjAllocator::Handler
Definition: memory.h:102
mxnet::runtime::Object::FDeleter
void(* FDeleter)(Object *self)
Object deleter.
Definition: object.h:157
mxnet::runtime::ObjectPtr
A custom smart pointer for Object.
Definition: object.h:346
mxnet::runtime::make_object
ObjectPtr< T > make_object(Args &&... args)
Allocate an object using default allocator.
Definition: memory.h:199
mxnet::runtime::SimpleObjAllocator::ArrayHandler
Definition: memory.h:147
mxnet::runtime::ObjAllocatorBase::make_object
ObjectPtr< T > make_object(Args &&... args)
Make a new object using the allocator.
Definition: memory.h:69
mxnet::runtime::SimpleObjAllocator::ArrayHandler::StorageType
typename std::aligned_storage< sizeof(ArrayType), alignof(ArrayType)>::type StorageType
Definition: memory.h:149
mxnet::runtime::SimpleObjAllocator::Handler::StorageType
typename std::aligned_storage< sizeof(T), alignof(T)>::type StorageType
Definition: memory.h:104
mxnet::runtime::SimpleObjAllocator
Definition: memory.h:99
mxnet::runtime::SimpleObjAllocator::Handler::New
static T * New(SimpleObjAllocator *, Args &&... args)
Definition: memory.h:107
mxnet::runtime::ObjAllocatorBase::make_inplace_array
ObjectPtr< ArrayType > make_inplace_array(size_t num_elems, Args &&... args)
Definition: memory.h:86
mxnet::runtime::ObjAllocatorBase
Base class of object allocators that implements make. Use curiously recurring template pattern.
Definition: memory.h:60
mxnet::runtime::SimpleObjAllocator::ArrayHandler::Deleter
static Object::FDeleter Deleter()
Definition: memory.h:177
mxnet::runtime::SimpleObjAllocator::ArrayHandler::New
static ArrayType * New(SimpleObjAllocator *, size_t num_elems, Args &&... args)
Definition: memory.h:156
mxnet::runtime::SimpleObjAllocator::Handler::Deleter
static Object::FDeleter Deleter()
Definition: memory.h:126
mxnet::runtime::make_inplace_array_object
ObjectPtr< ArrayType > make_inplace_array_object(size_t num_elems, Args &&... args)
Definition: memory.h:204
object.h
A managed object in MXNet runtime.