OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ |
| 10 |
| 11 #include <deque> |
| 12 #include <list> |
| 13 #include <string> |
| 14 |
| 15 #include "webrtc/base/numerics/cyclic_buffer.h" |
| 16 #include "webrtc/base/timeutils.h" |
| 17 #include "webrtc/base/random.h" |
| 18 #include "webrtc/test/gtest.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 struct Usage { |
| 23 size_t default_constructor = 0; |
| 24 size_t cast_constructor = 0; |
| 25 size_t copy_constructor = 0; |
| 26 size_t move_constructor = 0; |
| 27 size_t move_cast_constructor = 0; |
| 28 size_t destructors = 0; |
| 29 size_t copy_assignment = 0; |
| 30 size_t cast_assignment = 0; |
| 31 size_t move_assignment = 0; |
| 32 size_t move_cast_assignment = 0; |
| 33 size_t casts = 0; |
| 34 }; |
| 35 |
| 36 // void ResetMagicTypeUsage(Usage& usage) { |
| 37 // usage.default_constructor = 0; |
| 38 // usage.cast_constructor = 0; |
| 39 // usage.copy_constructor = 0; |
| 40 // usage.move_constructor = 0; |
| 41 // usage.move_cast_constructor = 0; |
| 42 // usage.destructors = 0; |
| 43 // usage.copy_assignment = 0; |
| 44 // usage.cast_assignment = 0; |
| 45 // usage.move_assignment = 0; |
| 46 // usage.move_cast_assignment = 0; |
| 47 // usage.casts = 0; |
| 48 // } |
| 49 |
| 50 // void PrintMagicTypeUsage(const Usage& usage) { |
| 51 // printf("%zu default constructor calls\n", usage.default_constructor); |
| 52 // printf("%zu cast constructor calls\n", usage.cast_constructor); |
| 53 // printf("%zu copy constructor calls\n", usage.copy_constructor); |
| 54 // printf("%zu move cast constructor calls\n", usage.move_cast_constructor); |
| 55 // printf("%zu move constructor calls\n", usage.move_constructor); |
| 56 // printf("%zu destructor calls\n", usage.destructors); |
| 57 // printf("%zu copy assignments calls\n", usage.copy_assignment); |
| 58 // printf("%zu cast assignments calls\n", usage.cast_assignment); |
| 59 // printf("%zu move assignments calls\n", usage.move_assignment); |
| 60 // printf("%zu move cast assignments calls\n", usage.move_cast_assignment); |
| 61 // printf("%zu casts\n", usage.casts); |
| 62 // } |
| 63 |
| 64 // Wrapper type to detect memory management problems, like using memcpy to copy |
| 65 // data without running the constructor, using the data before it has been |
| 66 // assigned, or using the data after it has been deleted. |
| 67 template <typename T> |
| 68 class MagicType { |
| 69 public: |
| 70 MagicType() : magic_(this), constructed_(true), assigned_(false) { |
| 71 usage.default_constructor++; |
| 72 } |
| 73 |
| 74 MagicType(const T& value) // we actually want implicit casts here |
| 75 : value_(value), magic_(this), constructed_(true), assigned_(true) { |
| 76 usage.cast_constructor++; |
| 77 } |
| 78 |
| 79 MagicType(T&& value) // we actually want implicit casts here |
| 80 : value_(std::move(value)), |
| 81 magic_(this), |
| 82 constructed_(true), |
| 83 assigned_(true) { |
| 84 usage.move_cast_constructor++; |
| 85 } |
| 86 |
| 87 MagicType(const MagicType<T>& m) |
| 88 : value_(m.value_), |
| 89 magic_(this), |
| 90 constructed_(m.constructed_), |
| 91 assigned_(m.assigned_) { |
| 92 RTC_DCHECK(m.magic_ == &m); |
| 93 RTC_DCHECK(m.constructed_); |
| 94 usage.copy_constructor++; |
| 95 } |
| 96 |
| 97 MagicType(MagicType<T>&& m) |
| 98 : value_(std::move(m.value_)), |
| 99 magic_(this), |
| 100 constructed_(m.constructed_), |
| 101 assigned_(m.assigned_) { |
| 102 RTC_DCHECK(m.magic_ == &m); |
| 103 RTC_DCHECK(m.constructed_); |
| 104 usage.move_constructor++; |
| 105 } |
| 106 |
| 107 ~MagicType() { |
| 108 RTC_DCHECK(magic_ == this); |
| 109 RTC_DCHECK(constructed_); |
| 110 magic_ = nullptr; |
| 111 constructed_ = false; |
| 112 assigned_ = false; |
| 113 usage.destructors++; |
| 114 } |
| 115 |
| 116 MagicType<T>& operator=(const T& value) { |
| 117 RTC_DCHECK(magic_ == this); |
| 118 RTC_DCHECK(constructed_); |
| 119 value_ = value; |
| 120 assigned_ = true; |
| 121 usage.cast_assignment++; |
| 122 return *this; |
| 123 } |
| 124 |
| 125 MagicType<T>& operator=(T&& value) { |
| 126 RTC_DCHECK(magic_ == this); |
| 127 RTC_DCHECK(constructed_); |
| 128 value_ = value; |
| 129 assigned_ = true; |
| 130 usage.move_cast_assignment++; |
| 131 return *this; |
| 132 } |
| 133 |
| 134 MagicType<T>& operator=(const MagicType<T>& m) { |
| 135 RTC_DCHECK(m.magic_ == &m); |
| 136 RTC_DCHECK(m.constructed_); |
| 137 RTC_DCHECK(magic_ == this); |
| 138 RTC_DCHECK(constructed_); |
| 139 value_ = m.value_; |
| 140 assigned_ = m.assigned_; |
| 141 usage.copy_assignment++; |
| 142 return *this; |
| 143 } |
| 144 |
| 145 MagicType<T>& operator=(MagicType<T>&& m) { |
| 146 RTC_DCHECK(m.magic_ == &m); |
| 147 RTC_DCHECK(m.constructed_); |
| 148 RTC_DCHECK(magic_ == this); |
| 149 RTC_DCHECK(constructed_); |
| 150 value_ = m.value_; |
| 151 assigned_ = m.assigned_; |
| 152 usage.move_assignment++; |
| 153 return *this; |
| 154 } |
| 155 |
| 156 operator T() const { |
| 157 RTC_DCHECK(magic_ == this); |
| 158 RTC_DCHECK(constructed_); |
| 159 RTC_DCHECK(assigned_); |
| 160 usage.casts++; |
| 161 return value_; |
| 162 } |
| 163 |
| 164 static Usage usage; |
| 165 |
| 166 private: |
| 167 T value_; |
| 168 MagicType<T>* magic_; |
| 169 bool constructed_; |
| 170 bool assigned_; |
| 171 }; |
| 172 |
| 173 template <typename T> |
| 174 Usage MagicType<T>::usage; |
| 175 } // namespace |
| 176 |
| 177 namespace webrtc { |
| 178 |
| 179 TEST(CyclicBufferTest, EmptyBuffer) { |
| 180 CyclicBuffer<int, GrowthPolicy::ASSERT> buffer(5); |
| 181 EXPECT_EQ(buffer.size(), 0u); |
| 182 } |
| 183 |
| 184 TEST(CyclicBufferTest, PushBack) { |
| 185 CyclicBuffer<int, GrowthPolicy::ASSERT> buffer(5); |
| 186 buffer.push_back(45); |
| 187 EXPECT_EQ(buffer.size(), 1u); |
| 188 EXPECT_EQ(buffer.back(), 45); |
| 189 EXPECT_EQ(buffer.front(), 45); |
| 190 buffer.push_back(98); |
| 191 EXPECT_EQ(buffer.size(), 2u); |
| 192 EXPECT_EQ(buffer.back(), 98); |
| 193 EXPECT_EQ(buffer.front(), 45); |
| 194 buffer.push_back(35); |
| 195 EXPECT_EQ(buffer.size(), 3u); |
| 196 EXPECT_EQ(buffer.back(), 35); |
| 197 EXPECT_EQ(buffer.front(), 45); |
| 198 } |
| 199 |
| 200 TEST(CyclicBufferTest, PopFront) { |
| 201 CyclicBuffer<int, GrowthPolicy::ASSERT> buffer(5); |
| 202 buffer.push_back(45); |
| 203 buffer.push_back(98); |
| 204 buffer.push_back(35); |
| 205 EXPECT_EQ(buffer.size(), 3u); |
| 206 EXPECT_EQ(buffer.back(), 35); |
| 207 EXPECT_EQ(buffer.front(), 45); |
| 208 buffer.pop_front(); |
| 209 EXPECT_EQ(buffer.size(), 2u); |
| 210 EXPECT_EQ(buffer.back(), 35); |
| 211 EXPECT_EQ(buffer.front(), 98); |
| 212 buffer.pop_front(); |
| 213 EXPECT_EQ(buffer.size(), 1u); |
| 214 EXPECT_EQ(buffer.back(), 35); |
| 215 EXPECT_EQ(buffer.front(), 35); |
| 216 buffer.pop_front(); |
| 217 EXPECT_EQ(buffer.size(), 0u); |
| 218 } |
| 219 |
| 220 TEST(CyclicBufferTest, PushFront) { |
| 221 CyclicBuffer<int, GrowthPolicy::ASSERT> buffer(5); |
| 222 buffer.push_front(45); |
| 223 EXPECT_EQ(buffer.size(), 1u); |
| 224 EXPECT_EQ(buffer.back(), 45); |
| 225 EXPECT_EQ(buffer.front(), 45); |
| 226 buffer.push_front(98); |
| 227 EXPECT_EQ(buffer.size(), 2u); |
| 228 EXPECT_EQ(buffer.back(), 45); |
| 229 EXPECT_EQ(buffer.front(), 98); |
| 230 buffer.push_front(35); |
| 231 EXPECT_EQ(buffer.size(), 3u); |
| 232 EXPECT_EQ(buffer.back(), 45); |
| 233 EXPECT_EQ(buffer.front(), 35); |
| 234 } |
| 235 |
| 236 TEST(CyclicBufferTest, PopBack) { |
| 237 CyclicBuffer<int, GrowthPolicy::ASSERT> buffer(5); |
| 238 buffer.push_front(45); |
| 239 buffer.push_front(98); |
| 240 buffer.push_front(35); |
| 241 EXPECT_EQ(buffer.size(), 3u); |
| 242 EXPECT_EQ(buffer.back(), 45); |
| 243 EXPECT_EQ(buffer.front(), 35); |
| 244 buffer.pop_back(); |
| 245 EXPECT_EQ(buffer.size(), 2u); |
| 246 EXPECT_EQ(buffer.back(), 98); |
| 247 EXPECT_EQ(buffer.front(), 35); |
| 248 buffer.pop_back(); |
| 249 EXPECT_EQ(buffer.size(), 1u); |
| 250 EXPECT_EQ(buffer.back(), 35); |
| 251 EXPECT_EQ(buffer.front(), 35); |
| 252 buffer.pop_back(); |
| 253 EXPECT_EQ(buffer.size(), 0u); |
| 254 } |
| 255 |
| 256 TEST(CyclicBufferTest, BufferWithCapacityOne) { |
| 257 CyclicBuffer<int, GrowthPolicy::ASSERT> buffer(1); |
| 258 // Insert and remove one element from a buffer with capacity 1. |
| 259 buffer.push_back(45); |
| 260 EXPECT_EQ(buffer.size(), 1u); |
| 261 EXPECT_EQ(buffer.back(), 45); |
| 262 EXPECT_EQ(buffer.front(), 45); |
| 263 buffer.pop_front(); |
| 264 EXPECT_EQ(buffer.size(), 0u); |
| 265 // Verify that we are still in a valid state by attempting a another |
| 266 // operation. |
| 267 buffer.push_back(21); |
| 268 EXPECT_EQ(buffer.size(), 1u); |
| 269 EXPECT_EQ(buffer.back(), 21); |
| 270 EXPECT_EQ(buffer.front(), 21); |
| 271 buffer.pop_front(); |
| 272 EXPECT_EQ(buffer.size(), 0u); |
| 273 } |
| 274 |
| 275 TEST(CyclicBufferTest, BufferMagicType) { |
| 276 CyclicBuffer<MagicType<int>, GrowthPolicy::ASSERT> buffer(3); |
| 277 buffer.push_back(45); |
| 278 EXPECT_EQ(buffer.size(), 1u); |
| 279 EXPECT_EQ(buffer.back(), 45); |
| 280 EXPECT_EQ(buffer.front(), 45); |
| 281 buffer.push_back(98); |
| 282 EXPECT_EQ(buffer.size(), 2u); |
| 283 EXPECT_EQ(buffer.back(), 98); |
| 284 EXPECT_EQ(buffer.front(), 45); |
| 285 buffer.push_back(35); |
| 286 EXPECT_EQ(buffer.size(), 3u); |
| 287 EXPECT_EQ(buffer.back(), 35); |
| 288 EXPECT_EQ(buffer.front(), 45); |
| 289 buffer.pop_front(); |
| 290 EXPECT_EQ(buffer.size(), 2u); |
| 291 EXPECT_EQ(buffer.back(), 35); |
| 292 EXPECT_EQ(buffer.front(), 98); |
| 293 buffer.pop_front(); |
| 294 EXPECT_EQ(buffer.size(), 1u); |
| 295 EXPECT_EQ(buffer.back(), 35); |
| 296 EXPECT_EQ(buffer.front(), 35); |
| 297 buffer.pop_front(); |
| 298 EXPECT_EQ(buffer.size(), 0u); |
| 299 } |
| 300 |
| 301 template <typename Buffer> |
| 302 int64_t TestIntBufferPerformance(Buffer* buffer, |
| 303 size_t elements, |
| 304 size_t capacity) { |
| 305 int64_t start = rtc::TimeMicros(); |
| 306 for (size_t i = 0; i < elements; i++) { |
| 307 if (buffer->size() == capacity) |
| 308 buffer->pop_front(); |
| 309 buffer->push_back(i); |
| 310 } |
| 311 int64_t stop = rtc::TimeMicros(); |
| 312 return stop - start; |
| 313 } |
| 314 |
| 315 template <typename Buffer> |
| 316 int64_t TestStringBufferPerformance(Buffer* buffer, |
| 317 size_t elements, |
| 318 size_t capacity) { |
| 319 int64_t start = rtc::TimeMicros(); |
| 320 for (size_t i = 0; i < elements; i++) { |
| 321 if (buffer->size() == capacity) |
| 322 buffer->pop_front(); |
| 323 buffer->push_back(std::to_string(i)); |
| 324 } |
| 325 int64_t stop = rtc::TimeMicros(); |
| 326 return stop - start; |
| 327 } |
| 328 |
| 329 TEST(CyclicBufferTest, DISABLED_PerformanceInt) { |
| 330 CyclicBuffer<int, GrowthPolicy::ASSERT> cyclic(1023u); |
| 331 std::deque<int> double_ended; |
| 332 // printf("Time used by std::list = %ld\n", TestListPerformance(1000000u, |
| 333 // 1023u)); |
| 334 |
| 335 int64_t deque_time = 0; |
| 336 int64_t cyclicbuffer_time = 0; |
| 337 for (int i = 0; i < 100; i++) { |
| 338 deque_time += TestIntBufferPerformance(&double_ended, 1000000u, 1023u); |
| 339 cyclicbuffer_time += TestIntBufferPerformance(&cyclic, 1000000u, 1023u); |
| 340 } |
| 341 printf("Time used by std::deque = %ld\n", deque_time / 100); |
| 342 printf("Time used by CyclicBuffer = %ld\n", cyclicbuffer_time / 100); |
| 343 } |
| 344 |
| 345 TEST(CyclicBufferTest, DISABLED_PerformanceDouble) { |
| 346 CyclicBuffer<double, GrowthPolicy::ASSERT> cyclic(1023u); |
| 347 std::deque<double> double_ended; |
| 348 |
| 349 int64_t deque_time = 0; |
| 350 int64_t cyclicbuffer_time = 0; |
| 351 for (int i = 0; i < 100; i++) { |
| 352 deque_time += TestIntBufferPerformance(&double_ended, 1000000u, 1023u); |
| 353 cyclicbuffer_time += TestIntBufferPerformance(&cyclic, 1000000u, 1023u); |
| 354 } |
| 355 printf("Time used by std::deque = %ld\n", deque_time / 100); |
| 356 printf("Time used by CyclicBuffer = %ld\n", cyclicbuffer_time / 100); |
| 357 } |
| 358 |
| 359 TEST(CyclicBufferTest, DISABLED_PerformanceString) { |
| 360 CyclicBuffer<std::string, GrowthPolicy::ASSERT> cyclic(1023u); |
| 361 std::deque<std::string> double_ended; |
| 362 |
| 363 int64_t deque_time = 0; |
| 364 int64_t cyclicbuffer_time = 0; |
| 365 for (int i = 0; i < 100; i++) { |
| 366 deque_time += TestStringBufferPerformance(&double_ended, 1000000u, 1023u); |
| 367 cyclicbuffer_time += TestStringBufferPerformance(&cyclic, 1000000u, 1023u); |
| 368 } |
| 369 printf("Time used by std::deque = %ld\n", deque_time / 100); |
| 370 printf("Time used by CyclicBuffer = %ld\n", cyclicbuffer_time / 100); |
| 371 } |
| 372 |
| 373 TEST(CyclicBufferTest, DISABLED_PerformanceMagicTypeString) { |
| 374 CyclicBuffer<MagicType<std::string>, GrowthPolicy::ASSERT> cyclic(1023u); |
| 375 std::deque<MagicType<std::string>> double_ended; |
| 376 |
| 377 int64_t deque_time = 0; |
| 378 int64_t cyclicbuffer_time = 0; |
| 379 for (int i = 0; i < 100; i++) { |
| 380 // ResetMagicTypeUsage(MagicType<std::string>::usage); |
| 381 deque_time += TestStringBufferPerformance(&double_ended, 1000000u, 1023u); |
| 382 // printf("std::deque performance\n"); |
| 383 // PrintMagicTypeUsage(MagicType<std::string>::usage); |
| 384 |
| 385 // ResetMagicTypeUsage(MagicType<std::string>::usage); |
| 386 cyclicbuffer_time += TestStringBufferPerformance(&cyclic, 1000000u, 1023u); |
| 387 // printf("CyclicBuffer performance\n"); |
| 388 // PrintMagicTypeUsage(MagicType<std::string>::usage); |
| 389 // printf("\n"); |
| 390 } |
| 391 printf("Time used by std::deque = %ld\n", deque_time / 100); |
| 392 printf("Time used by CyclicBuffer = %ld\n", cyclicbuffer_time / 100); |
| 393 } |
| 394 |
| 395 void BehavesAsDeque(size_t operations, size_t capacity, uint64_t seed) { |
| 396 Random random(seed); |
| 397 std::deque<uint32_t> q; |
| 398 CyclicBuffer<uint32_t, GrowthPolicy::ASSERT> cq(capacity); |
| 399 |
| 400 for (size_t i = 0; i < operations; i++) { |
| 401 bool insert = random.Rand<bool>(); |
| 402 bool front = random.Rand<bool>(); |
| 403 if ((insert && q.size() < capacity) || q.empty()) { |
| 404 uint32_t value = random.Rand<uint32_t>(); |
| 405 if (front) { |
| 406 q.push_front(value); |
| 407 cq.push_front(value); |
| 408 } else { |
| 409 q.push_back(value); |
| 410 cq.push_back(value); |
| 411 } |
| 412 } else { |
| 413 if (front) { |
| 414 q.pop_front(); |
| 415 cq.pop_front(); |
| 416 } else { |
| 417 q.pop_back(); |
| 418 cq.pop_back(); |
| 419 } |
| 420 } |
| 421 |
| 422 EXPECT_EQ(q.size(), cq.size()); |
| 423 if (!q.empty()) { |
| 424 EXPECT_EQ(q.front(), cq.front()); |
| 425 EXPECT_EQ(q.back(), cq.back()); |
| 426 } |
| 427 } |
| 428 } |
| 429 |
| 430 TEST(CyclicBufferTest, BehavesAsDeque) { |
| 431 BehavesAsDeque(1000000, 1023, 123454321); |
| 432 } |
| 433 |
| 434 TEST(CyclicBufferTest, BehavesAsDequeSmallBuffer) { |
| 435 BehavesAsDeque(1000000, 4, 123454321); |
| 436 } |
| 437 |
| 438 } // namespace webrtc |
OLD | NEW |