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,
72  "make can only be used to create Object");
73  T* ptr = Handler::New(static_cast<Derived*>(this),
74  std::forward<Args>(args)...);
75  ptr->type_index_ = T::RuntimeTypeIndex();
76  ptr->deleter_ = Handler::Deleter();
77  return ObjectPtr<T>(ptr);
78  }
79 
87  template<typename ArrayType, typename ElemType, typename... Args>
88  inline ObjectPtr<ArrayType> make_inplace_array(size_t num_elems, Args&&... args) {
89  using Handler = typename Derived::template ArrayHandler<ArrayType, ElemType>;
90  static_assert(std::is_base_of<Object, ArrayType>::value,
91  "make_inplace_array can only be used to create Object");
92  ArrayType* ptr = Handler::New(static_cast<Derived*>(this),
93  num_elems,
94  std::forward<Args>(args)...);
95  ptr->type_index_ = ArrayType::RuntimeTypeIndex();
96  ptr->deleter_ = Handler::Deleter();
97  return ObjectPtr<ArrayType>(ptr);
98  }
99 };
100 
101 // Simple allocator that uses new/delete.
103  public ObjAllocatorBase<SimpleObjAllocator> {
104  public:
105  template<typename T>
106  class Handler {
107  public:
108  using StorageType = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
109 
110  template<typename... Args>
111  static T* New(SimpleObjAllocator*, Args&&... args) {
112  // NOTE: the first argument is not needed for SimpleObjAllocator
113  // It is reserved for special allocators that needs to recycle
114  // the object to itself (e.g. in the case of object pool).
115  //
116  // In the case of an object pool, an allocator needs to create
117  // a special chunk memory that hides reference to the allocator
118  // and call allocator's release function in the deleter.
119 
120  // NOTE2: Use inplace new to allocate
121  // This is used to get rid of warning when deleting a virtual
122  // class with non-virtual destructor.
123  // We are fine here as we captured the right deleter during construction.
124  // This is also the right way to get storage type for an object pool.
125  StorageType* data = new StorageType();
126  new (data) T(std::forward<Args>(args)...);
127  return reinterpret_cast<T*>(data);
128  }
129 
131  return Deleter_;
132  }
133 
134  private:
135  static void Deleter_(Object* objptr) {
136  // NOTE: this is important to cast back to T*
137  // because objptr and tptr may not be the same
138  // depending on how sub-class allocates the space.
139  T* tptr = static_cast<T*>(objptr);
140  // It is important to do tptr->T::~T(),
141  // so that we explicitly call the specific destructor
142  // instead of tptr->~T(), which could mean the intention
143  // call a virtual destructor(which may not be available and is not required).
144  tptr->T::~T();
145  delete reinterpret_cast<StorageType*>(tptr);
146  }
147  };
148 
149  // Array handler that uses new/delete.
150  template<typename ArrayType, typename ElemType>
151  class ArrayHandler {
152  public:
153  using StorageType = typename std::aligned_storage<sizeof(ArrayType), alignof(ArrayType)>::type;
154  // for now only support elements that aligns with array header.
155  static_assert(alignof(ArrayType) % alignof(ElemType) == 0 &&
156  sizeof(ArrayType) % alignof(ElemType) == 0,
157  "element alignment constraint");
158 
159  template<typename... Args>
160  static ArrayType* New(SimpleObjAllocator*, size_t num_elems, Args&&... args) {
161  // NOTE: the first argument is not needed for ArrayObjAllocator
162  // It is reserved for special allocators that needs to recycle
163  // the object to itself (e.g. in the case of object pool).
164  //
165  // In the case of an object pool, an allocator needs to create
166  // a special chunk memory that hides reference to the allocator
167  // and call allocator's release function in the deleter.
168  // NOTE2: Use inplace new to allocate
169  // This is used to get rid of warning when deleting a virtual
170  // class with non-virtual destructor.
171  // We are fine here as we captured the right deleter during construction.
172  // This is also the right way to get storage type for an object pool.
173  size_t unit = sizeof(StorageType);
174  size_t requested_size = num_elems * sizeof(ElemType) + sizeof(ArrayType);
175  size_t num_storage_slots = (requested_size + unit - 1) / unit;
176  StorageType* data = new StorageType[num_storage_slots];
177  new (data) ArrayType(std::forward<Args>(args)...);
178  return reinterpret_cast<ArrayType*>(data);
179  }
180 
182  return Deleter_;
183  }
184 
185  private:
186  static void Deleter_(Object* objptr) {
187  // NOTE: this is important to cast back to ArrayType*
188  // because objptr and tptr may not be the same
189  // depending on how sub-class allocates the space.
190  ArrayType* tptr = static_cast<ArrayType*>(objptr);
191  // It is important to do tptr->ArrayType::~ArrayType(),
192  // so that we explicitly call the specific destructor
193  // instead of tptr->~ArrayType(), which could mean the intention
194  // call a virtual destructor(which may not be available and is not required).
195  tptr->ArrayType::~ArrayType();
196  StorageType* p = reinterpret_cast<StorageType*>(tptr);
197  delete []p;
198  }
199  };
200 };
201 
202 template<typename T, typename... Args>
203 inline ObjectPtr<T> make_object(Args&&... args) {
204  return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);
205 }
206 
207 template<typename ArrayType, typename ElemType, typename... Args>
208 inline ObjectPtr<ArrayType> make_inplace_array_object(size_t num_elems, Args&&... args) {
209  return SimpleObjAllocator().make_inplace_array<ArrayType, ElemType>(
210  num_elems, std::forward<Args>(args)...);
211 }
212 
213 } // namespace runtime
214 } // namespace mxnet
215 #endif // MXNET_RUNTIME_MEMORY_H_
typename std::aligned_storage< sizeof(ArrayType), alignof(ArrayType)>::type StorageType
Definition: memory.h:153
typename std::aligned_storage< sizeof(T), alignof(T)>::type StorageType
Definition: memory.h:108
namespace of mxnet
Definition: api_registry.h:33
A custom smart pointer for Object.
Definition: object.h:345
ObjectPtr< T > make_object(Args &&...args)
Make a new object using the allocator.
Definition: memory.h:69
ObjectPtr< T > make_object(Args &&...args)
Allocate an object using default allocator.
Definition: memory.h:203
static T * New(SimpleObjAllocator *, Args &&...args)
Definition: memory.h:111
static ArrayType * New(SimpleObjAllocator *, size_t num_elems, Args &&...args)
Definition: memory.h:160
base class of all object containers.
Definition: object.h:149
Base class of object allocators that implements make. Use curiously recurring template pattern...
Definition: memory.h:60
A managed object in MXNet runtime.
static Object::FDeleter Deleter()
Definition: memory.h:181
ObjectPtr< ArrayType > make_inplace_array(size_t num_elems, Args &&...args)
Definition: memory.h:88
Definition: memory.h:102
void(* FDeleter)(Object *self)
Object deleter.
Definition: object.h:155
static Object::FDeleter Deleter()
Definition: memory.h:130
ObjectPtr< ArrayType > make_inplace_array_object(size_t num_elems, Args &&...args)
Definition: memory.h:208