proxygen
ThreadCachedArenaTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2012-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <algorithm>
20 #include <iterator>
21 #include <map>
22 #include <mutex>
23 #include <random>
24 #include <thread>
25 #include <unordered_map>
26 
27 #include <glog/logging.h>
28 
29 #include <folly/Benchmark.h>
30 #include <folly/Memory.h>
31 #include <folly/Range.h>
32 #include <folly/lang/Align.h>
34 
35 using namespace folly;
36 
37 namespace {
38 
39 class ArenaTester {
40  public:
41  explicit ArenaTester(ThreadCachedArena& arena) : arena_(&arena) {}
42 
43  void allocate(size_t count, size_t maxSize);
44  void verify();
45  void merge(ArenaTester&& other);
46 
47  private:
48  std::mutex mergeMutex_;
49  std::vector<std::pair<uint8_t, Range<uint8_t*>>> areas_;
50  ThreadCachedArena* arena_;
51 };
52 
53 void ArenaTester::allocate(size_t count, size_t maxSize) {
54  // Allocate chunks of memory of random sizes
55  std::mt19937 rnd;
56  std::uniform_int_distribution<uint32_t> sizeDist(1, maxSize - 1);
57  areas_.clear();
58  areas_.reserve(count);
59  for (size_t i = 0; i < count; i++) {
60  size_t size = sizeDist(rnd);
61  uint8_t* p = static_cast<uint8_t*>(arena_->allocate(size));
62  areas_.emplace_back(uint8_t(rnd() & 0xff), Range<uint8_t*>(p, size));
63  }
64 
65  // Fill each area with a different value, to prove that they don't overlap
66  // Fill in random order.
67  std::random_shuffle(areas_.begin(), areas_.end(), [&rnd](ptrdiff_t n) {
68  return std::uniform_int_distribution<uint32_t>(0, n - 1)(rnd);
69  });
70 
71  for (auto& p : areas_) {
72  std::fill(p.second.begin(), p.second.end(), p.first);
73  }
74 }
75 
76 void ArenaTester::verify() {
77  for (auto& p : areas_) {
78  for (auto v : p.second) {
79  EXPECT_EQ(p.first, v);
80  }
81  }
82 }
83 
84 void ArenaTester::merge(ArenaTester&& other) {
85  {
86  std::lock_guard<std::mutex> lock(mergeMutex_);
87  std::move(
88  other.areas_.begin(), other.areas_.end(), std::back_inserter(areas_));
89  }
90  other.areas_.clear();
91 }
92 
93 } // namespace
94 
95 TEST(ThreadCachedArena, BlockSize) {
96  static const size_t alignment = max_align_v;
97  static const size_t requestedBlockSize = 64;
98 
99  ThreadCachedArena arena(requestedBlockSize);
100  size_t blockSize = alignment;
101  uint8_t* prev = static_cast<uint8_t*>(arena.allocate(1));
102 
103  // Keep allocating until we're no longer one single alignment away from the
104  // previous allocation -- that's when we've gotten to the next block.
105  uint8_t* p;
106  while ((p = static_cast<uint8_t*>(arena.allocate(1))) == prev + alignment) {
107  prev = p;
108  blockSize += alignment;
109  }
110 
111  VLOG(1) << "Requested block size: " << requestedBlockSize
112  << ", actual: " << blockSize;
113  EXPECT_LE(requestedBlockSize, blockSize);
114 }
115 
116 TEST(ThreadCachedArena, SingleThreaded) {
117  static const size_t requestedBlockSize = 64;
118  ThreadCachedArena arena(requestedBlockSize);
119  EXPECT_EQ(arena.totalSize(), sizeof(ThreadCachedArena));
120 
121  ArenaTester tester(arena);
122  tester.allocate(100, 100 << 10);
123  tester.verify();
124 
125  EXPECT_GT(arena.totalSize(), sizeof(ThreadCachedArena));
126 }
127 
128 TEST(ThreadCachedArena, MultiThreaded) {
129  static const size_t requestedBlockSize = 64;
130  ThreadCachedArena arena(requestedBlockSize);
131  ArenaTester mainTester(arena);
132 
133  // Do this twice, to catch the possibility that memory from the first
134  // round gets freed
135  static const size_t numThreads = 20;
136  for (size_t i = 0; i < 2; i++) {
137  std::vector<std::thread> threads;
138  threads.reserve(numThreads);
139  for (size_t j = 0; j < numThreads; j++) {
140  threads.emplace_back([&arena, &mainTester]() {
141  ArenaTester tester(arena);
142  tester.allocate(500, 1 << 10);
143  tester.verify();
144  mainTester.merge(std::move(tester));
145  });
146  }
147  for (auto& t : threads) {
148  t.join();
149  }
150  }
151 
152  mainTester.verify();
153 }
154 
156  using Map = std::unordered_map<
157  int,
158  int,
159  std::hash<int>,
160  std::equal_to<int>,
162 
163  static const size_t requestedBlockSize = 64;
164  ThreadCachedArena arena(requestedBlockSize);
165 
166  Map map{0,
167  std::hash<int>(),
168  std::equal_to<int>(),
169  ThreadCachedArenaAllocator<std::pair<const int, int>>(arena)};
170 
171  for (int i = 0; i < 1000; i++) {
172  map[i] = i;
173  }
174 
175  for (int i = 0; i < 1000; i++) {
176  EXPECT_EQ(i, map[i]);
177  }
178 }
179 
180 namespace {
181 
182 static const int kNumValues = 10000;
183 
184 BENCHMARK(bmUMStandard, iters) {
185  using Map = std::unordered_map<int, int>;
186 
187  while (iters--) {
188  Map map{0};
189  for (int i = 0; i < kNumValues; i++) {
190  map[i] = i;
191  }
192  }
193 }
194 
195 BENCHMARK(bmUMArena, iters) {
196  using Map = std::unordered_map<
197  int,
198  int,
199  std::hash<int>,
200  std::equal_to<int>,
202 
203  while (iters--) {
204  ThreadCachedArena arena;
205 
206  Map map{0,
207  std::hash<int>(),
208  std::equal_to<int>(),
209  ThreadCachedArenaAllocator<std::pair<const int, int>>(arena)};
210 
211  for (int i = 0; i < kNumValues; i++) {
212  map[i] = i;
213  }
214  }
215 }
216 
218 
219 BENCHMARK(bmMStandard, iters) {
220  using Map = std::map<int, int>;
221 
222  while (iters--) {
223  Map map;
224  for (int i = 0; i < kNumValues; i++) {
225  map[i] = i;
226  }
227  }
228 }
229 
231 
232 BENCHMARK(bmMArena, iters) {
233  using Map = std::map<
234  int,
235  int,
236  std::less<int>,
238 
239  while (iters--) {
240  ThreadCachedArena arena;
241 
242  Map map{std::less<int>(),
243  ThreadCachedArenaAllocator<std::pair<const int, int>>(arena)};
244 
245  for (int i = 0; i < kNumValues; i++) {
246  map[i] = i;
247  }
248  }
249 }
250 
252 
253 } // namespace
254 
255 // Benchmark Iters Total t t/iter iter/sec
256 // ----------------------------------------------------------------------------
257 // Comparing benchmarks: bmUMStandard,bmUMArena
258 // + 143% bmUMStandard 1570 2.005 s 1.277 ms 782.9
259 // * bmUMArena 3817 2.003 s 524.7 us 1.861 k
260 // ----------------------------------------------------------------------------
261 // Comparing benchmarks: bmMStandard,bmMArena
262 // +79.0% bmMStandard 1197 2.009 s 1.678 ms 595.8
263 // * bmMArena 2135 2.002 s 937.6 us 1.042 k
264 // ----------------------------------------------------------------------------
265 
266 int main(int argc, char* argv[]) {
267  testing::InitGoogleTest(&argc, argv);
268  gflags::ParseCommandLineFlags(&argc, &argv, true);
269  auto ret = RUN_ALL_TESTS();
270  if (!ret && FLAGS_benchmark) {
272  }
273  return ret;
274 }
#define EXPECT_LE(val1, val2)
Definition: gtest.h:1928
void verify(int extras)
OutputIt merge(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first, Compare comp)
Definition: Merge.h:46
void * allocate(size_t size)
int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_
Definition: gtest.h:2232
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
void runBenchmarks()
Definition: Benchmark.cpp:456
std::unordered_map< int64_t, VecT > Map
std::vector< std::thread::id > threads
constexpr std::size_t max_align_v
Definition: Align.h:90
char ** argv
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
static Map map(mapCap)
auto lock(Synchronized< D, M > &synchronized, Args &&...args)
Definition: Traits.h:594
int main(int argc, char *argv[])
int * count
BENCHMARK(fbFollyGlobalBenchmarkBaseline)
Definition: Benchmark.cpp:84
std::mutex mutex
BENCHMARK_DRAW_LINE()
GTEST_API_ void InitGoogleTest(int *argc, char **argv)
Definition: gtest.cc:5370
void merge(unsigned int iters, size_t maxSize, size_t bufSize)
TEST(SequencedExecutor, CPUThreadPoolExecutor)
#define EXPECT_GT(val1, val2)
Definition: gtest.h:1934