mxnet
lazy_alloc_array.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  */
19 
25 #ifndef MXNET_COMMON_LAZY_ALLOC_ARRAY_H_
26 #define MXNET_COMMON_LAZY_ALLOC_ARRAY_H_
27 
28 #include <dmlc/logging.h>
29 #include <memory>
30 #include <mutex>
31 #include <array>
32 #include <vector>
33 #include <atomic>
34 
35 namespace mxnet {
36 namespace common {
37 
38 template <typename TElem>
40  public:
48  template <typename FCreate>
49  inline std::shared_ptr<TElem> Get(int index, FCreate creator);
54  template <typename FVisit>
55  inline void ForEach(FVisit fvisit);
57  inline void Clear();
58 
59  private:
60  template <typename SyncObject>
61  class unique_unlock {
62  public:
63  explicit unique_unlock(std::unique_lock<SyncObject>* lock) : lock_(lock) {
64  if (lock_) {
65  lock_->unlock();
66  }
67  }
68  ~unique_unlock() {
69  if (lock_) {
70  lock_->lock();
71  }
72  }
73 
74  private:
75  std::unique_lock<SyncObject>* lock_;
76  };
77 
79  static constexpr std::size_t kInitSize = 16;
81  std::mutex create_mutex_;
83  std::array<std::shared_ptr<TElem>, kInitSize> head_;
85  std::vector<std::shared_ptr<TElem> > more_;
87  std::atomic<bool> is_clearing_;
88 };
89 
90 template <typename TElem>
91 inline LazyAllocArray<TElem>::LazyAllocArray() : is_clearing_(false) {}
92 
93 // implementations
94 template <typename TElem>
95 template <typename FCreate>
96 inline std::shared_ptr<TElem> LazyAllocArray<TElem>::Get(int index, FCreate creator) {
97  CHECK_GE(index, 0);
98  size_t idx = static_cast<size_t>(index);
99  if (idx < kInitSize) {
100  std::shared_ptr<TElem> ptr = head_[idx];
101  if (ptr) {
102  return ptr;
103  } else {
104  std::lock_guard<std::mutex> lock(create_mutex_);
105  if (!is_clearing_.load()) {
106  std::shared_ptr<TElem> ptr = head_[idx];
107  if (ptr) {
108  return ptr;
109  }
110  ptr = head_[idx] = std::shared_ptr<TElem>(creator());
111  return ptr;
112  }
113  }
114  } else {
115  std::lock_guard<std::mutex> lock(create_mutex_);
116  if (!is_clearing_.load()) {
117  idx -= kInitSize;
118  if (more_.size() <= idx) {
119  more_.reserve(idx + 1);
120  while (more_.size() <= idx) {
121  more_.push_back(std::shared_ptr<TElem>(nullptr));
122  }
123  }
124  std::shared_ptr<TElem> ptr = more_[idx];
125  if (ptr) {
126  return ptr;
127  }
128  ptr = more_[idx] = std::shared_ptr<TElem>(creator());
129  return ptr;
130  }
131  }
132  return nullptr;
133 }
134 
135 template <typename TElem>
137  std::unique_lock<std::mutex> lock(create_mutex_);
138  is_clearing_.store(true);
139  // Currently, head_ and more_ never get smaller, so it's safe to
140  // iterate them outside of the lock. The loops should catch
141  // any growth which might happen when create_mutex_ is unlocked
142  for (size_t i = 0; i < head_.size(); ++i) {
143  std::shared_ptr<TElem> p = head_[i];
144  head_[i] = std::shared_ptr<TElem>(nullptr);
145  unique_unlock<std::mutex> unlocker(&lock);
146  p = std::shared_ptr<TElem>(nullptr);
147  }
148  for (size_t i = 0; i < more_.size(); ++i) {
149  std::shared_ptr<TElem> p = more_[i];
150  more_[i] = std::shared_ptr<TElem>(nullptr);
151  unique_unlock<std::mutex> unlocker(&lock);
152  p = std::shared_ptr<TElem>(nullptr);
153  }
154  more_.clear();
155  is_clearing_.store(false);
156 }
157 
158 template <typename TElem>
159 template <typename FVisit>
160 inline void LazyAllocArray<TElem>::ForEach(FVisit fvisit) {
161  std::lock_guard<std::mutex> lock(create_mutex_);
162  for (size_t i = 0; i < head_.size(); ++i) {
163  if (head_[i].get() != nullptr) {
164  fvisit(i, head_[i].get());
165  }
166  }
167  for (size_t i = 0; i < more_.size(); ++i) {
168  if (more_[i].get() != nullptr) {
169  fvisit(i + kInitSize, more_[i].get());
170  }
171  }
172 }
173 
174 } // namespace common
175 } // namespace mxnet
176 #endif // MXNET_COMMON_LAZY_ALLOC_ARRAY_H_
mxnet
namespace of mxnet
Definition: api_registry.h:33
mxnet::common::cuda::rtc::lock
std::mutex lock
mxnet::common::LazyAllocArray::ForEach
void ForEach(FVisit fvisit)
for each not null element of the array, call fvisit
Definition: lazy_alloc_array.h:160
mxnet::common::LazyAllocArray::Get
std::shared_ptr< TElem > Get(int index, FCreate creator)
Get element of corresponding index, if it is not created create by creator.
Definition: lazy_alloc_array.h:96
mxnet::common::LazyAllocArray
Definition: lazy_alloc_array.h:39
mxnet::common::LazyAllocArray::LazyAllocArray
LazyAllocArray()
Definition: lazy_alloc_array.h:91
mxnet::common::LazyAllocArray::Clear
void Clear()
clear all the allocated elements in array
Definition: lazy_alloc_array.h:136