autovector_test.cc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include "util/autovector.h"
  6. #include <atomic>
  7. #include <iostream>
  8. #include <string>
  9. #include <utility>
  10. #include "rocksdb/env.h"
  11. #include "test_util/testharness.h"
  12. #include "test_util/testutil.h"
  13. #include "util/string_util.h"
  14. using std::cout;
  15. using std::endl;
  16. namespace ROCKSDB_NAMESPACE {
  17. class AutoVectorTest : public testing::Test {};
  18. const unsigned long kSize = 8;
  19. namespace {
  20. template <class T>
  21. void AssertAutoVectorOnlyInStack(autovector<T, kSize>* vec, bool result) {
  22. ASSERT_EQ(vec->only_in_stack(), result);
  23. }
  24. } // namespace
  25. TEST_F(AutoVectorTest, PushBackAndPopBack) {
  26. autovector<size_t, kSize> vec;
  27. ASSERT_TRUE(vec.empty());
  28. ASSERT_EQ(0ul, vec.size());
  29. for (size_t i = 0; i < 1000 * kSize; ++i) {
  30. vec.push_back(i);
  31. ASSERT_TRUE(!vec.empty());
  32. if (i < kSize) {
  33. AssertAutoVectorOnlyInStack(&vec, true);
  34. } else {
  35. AssertAutoVectorOnlyInStack(&vec, false);
  36. }
  37. ASSERT_EQ(i + 1, vec.size());
  38. ASSERT_EQ(i, vec[i]);
  39. ASSERT_EQ(i, vec.at(i));
  40. }
  41. size_t size = vec.size();
  42. while (size != 0) {
  43. vec.pop_back();
  44. // will always be in heap
  45. AssertAutoVectorOnlyInStack(&vec, false);
  46. ASSERT_EQ(--size, vec.size());
  47. }
  48. ASSERT_TRUE(vec.empty());
  49. }
  50. TEST_F(AutoVectorTest, EmplaceBack) {
  51. using ValType = std::pair<size_t, std::string>;
  52. autovector<ValType, kSize> vec;
  53. for (size_t i = 0; i < 1000 * kSize; ++i) {
  54. vec.emplace_back(i, std::to_string(i + 123));
  55. ASSERT_TRUE(!vec.empty());
  56. if (i < kSize) {
  57. AssertAutoVectorOnlyInStack(&vec, true);
  58. } else {
  59. AssertAutoVectorOnlyInStack(&vec, false);
  60. }
  61. ASSERT_EQ(i + 1, vec.size());
  62. ASSERT_EQ(i, vec[i].first);
  63. ASSERT_EQ(std::to_string(i + 123), vec[i].second);
  64. }
  65. vec.clear();
  66. ASSERT_TRUE(vec.empty());
  67. AssertAutoVectorOnlyInStack(&vec, false);
  68. }
  69. TEST_F(AutoVectorTest, Resize) {
  70. autovector<size_t, kSize> vec;
  71. vec.resize(kSize);
  72. AssertAutoVectorOnlyInStack(&vec, true);
  73. for (size_t i = 0; i < kSize; ++i) {
  74. vec[i] = i;
  75. }
  76. vec.resize(kSize * 2);
  77. AssertAutoVectorOnlyInStack(&vec, false);
  78. for (size_t i = 0; i < kSize; ++i) {
  79. ASSERT_EQ(vec[i], i);
  80. }
  81. for (size_t i = 0; i < kSize; ++i) {
  82. vec[i + kSize] = i;
  83. }
  84. vec.resize(1);
  85. ASSERT_EQ(1U, vec.size());
  86. }
  87. namespace {
  88. void AssertEqual(const autovector<size_t, kSize>& a,
  89. const autovector<size_t, kSize>& b) {
  90. ASSERT_EQ(a.size(), b.size());
  91. ASSERT_EQ(a.empty(), b.empty());
  92. ASSERT_EQ(a.only_in_stack(), b.only_in_stack());
  93. for (size_t i = 0; i < a.size(); ++i) {
  94. ASSERT_EQ(a[i], b[i]);
  95. }
  96. }
  97. } // namespace
  98. TEST_F(AutoVectorTest, CopyAndAssignment) {
  99. // Test both heap-allocated and stack-allocated cases.
  100. for (auto size : {kSize / 2, kSize * 1000}) {
  101. autovector<size_t, kSize> vec;
  102. for (size_t i = 0; i < size; ++i) {
  103. vec.push_back(i);
  104. }
  105. {
  106. autovector<size_t, kSize> other;
  107. other = vec;
  108. AssertEqual(other, vec);
  109. }
  110. {
  111. autovector<size_t, kSize> other(vec);
  112. AssertEqual(other, vec);
  113. }
  114. }
  115. }
  116. TEST_F(AutoVectorTest, Iterators) {
  117. autovector<std::string, kSize> vec;
  118. for (size_t i = 0; i < kSize * 1000; ++i) {
  119. vec.push_back(std::to_string(i));
  120. }
  121. // basic operator test
  122. ASSERT_EQ(vec.front(), *vec.begin());
  123. ASSERT_EQ(vec.back(), *(vec.end() - 1));
  124. ASSERT_TRUE(vec.begin() < vec.end());
  125. // non-const iterator
  126. size_t index = 0;
  127. for (const auto& item : vec) {
  128. ASSERT_EQ(vec[index++], item);
  129. }
  130. index = vec.size() - 1;
  131. for (auto pos = vec.rbegin(); pos != vec.rend(); ++pos) {
  132. ASSERT_EQ(vec[index--], *pos);
  133. }
  134. // const iterator
  135. const auto& cvec = vec;
  136. index = 0;
  137. for (const auto& item : cvec) {
  138. ASSERT_EQ(cvec[index++], item);
  139. }
  140. index = vec.size() - 1;
  141. for (auto pos = cvec.rbegin(); pos != cvec.rend(); ++pos) {
  142. ASSERT_EQ(cvec[index--], *pos);
  143. }
  144. // forward and backward
  145. auto pos = vec.begin();
  146. while (pos != vec.end()) {
  147. auto old_val = *pos;
  148. auto old = pos++;
  149. // HACK: make sure -> works
  150. ASSERT_TRUE(!old->empty());
  151. ASSERT_EQ(old_val, *old);
  152. ASSERT_TRUE(pos == vec.end() || old_val != *pos);
  153. }
  154. pos = vec.begin();
  155. for (size_t i = 0; i < vec.size(); i += 2) {
  156. // Cannot use ASSERT_EQ since that macro depends on iostream serialization
  157. ASSERT_TRUE(pos + 2 - 2 == pos);
  158. pos += 2;
  159. ASSERT_TRUE(pos >= vec.begin());
  160. ASSERT_TRUE(pos <= vec.end());
  161. size_t diff = static_cast<size_t>(pos - vec.begin());
  162. ASSERT_EQ(i + 2, diff);
  163. }
  164. }
  165. namespace {
  166. std::vector<std::string> GetTestKeys(size_t size) {
  167. std::vector<std::string> keys;
  168. keys.resize(size);
  169. int index = 0;
  170. for (auto& key : keys) {
  171. key = "item-" + std::to_string(index++);
  172. }
  173. return keys;
  174. }
  175. } // namespace
  176. template <class TVector>
  177. void BenchmarkVectorCreationAndInsertion(
  178. std::string name, size_t ops, size_t item_size,
  179. const std::vector<typename TVector::value_type>& items) {
  180. auto env = Env::Default();
  181. int index = 0;
  182. auto start_time = env->NowNanos();
  183. auto ops_remaining = ops;
  184. while (ops_remaining--) {
  185. TVector v;
  186. for (size_t i = 0; i < item_size; ++i) {
  187. v.push_back(items[index++]);
  188. }
  189. }
  190. auto elapsed = env->NowNanos() - start_time;
  191. cout << "created " << ops << " " << name << " instances:\n\t"
  192. << "each was inserted with " << item_size << " elements\n\t"
  193. << "total time elapsed: " << elapsed << " (ns)" << endl;
  194. }
  195. template <class TVector>
  196. size_t BenchmarkSequenceAccess(std::string name, size_t ops, size_t elem_size) {
  197. TVector v;
  198. for (const auto& item : GetTestKeys(elem_size)) {
  199. v.push_back(item);
  200. }
  201. auto env = Env::Default();
  202. auto ops_remaining = ops;
  203. auto start_time = env->NowNanos();
  204. size_t total = 0;
  205. while (ops_remaining--) {
  206. auto end = v.end();
  207. for (auto pos = v.begin(); pos != end; ++pos) {
  208. total += pos->size();
  209. }
  210. }
  211. auto elapsed = env->NowNanos() - start_time;
  212. cout << "performed " << ops << " sequence access against " << name << "\n\t"
  213. << "size: " << elem_size << "\n\t" << "total time elapsed: " << elapsed
  214. << " (ns)" << endl;
  215. // HACK avoid compiler's optimization to ignore total
  216. return total;
  217. }
  218. // This test case only reports the performance between std::vector<std::string>
  219. // and autovector<std::string>. We chose string for comparison because in most
  220. // of our use cases we used std::vector<std::string>.
  221. TEST_F(AutoVectorTest, PerfBench) {
  222. // We run same operations for kOps times in order to get a more fair result.
  223. size_t kOps = 100000;
  224. // Creation and insertion test
  225. // Test the case when there is:
  226. // * no element inserted: internal array of std::vector may not really get
  227. // initialize.
  228. // * one element inserted: internal array of std::vector must have
  229. // initialized.
  230. // * kSize elements inserted. This shows the most time we'll spend if we
  231. // keep everything in stack.
  232. // * 2 * kSize elements inserted. The internal vector of
  233. // autovector must have been initialized.
  234. cout << "=====================================================" << endl;
  235. cout << "Creation and Insertion Test (value type: std::string)" << endl;
  236. cout << "=====================================================" << endl;
  237. // pre-generated unique keys
  238. auto string_keys = GetTestKeys(kOps * 2 * kSize);
  239. for (auto insertions : {0ul, 1ul, kSize / 2, kSize, 2 * kSize}) {
  240. BenchmarkVectorCreationAndInsertion<std::vector<std::string>>(
  241. "std::vector<std::string>", kOps, insertions, string_keys);
  242. BenchmarkVectorCreationAndInsertion<autovector<std::string, kSize>>(
  243. "autovector<std::string>", kOps, insertions, string_keys);
  244. cout << "-----------------------------------" << endl;
  245. }
  246. cout << "=====================================================" << endl;
  247. cout << "Creation and Insertion Test (value type: uint64_t)" << endl;
  248. cout << "=====================================================" << endl;
  249. // pre-generated unique keys
  250. std::vector<uint64_t> int_keys(kOps * 2 * kSize);
  251. for (size_t i = 0; i < kOps * 2 * kSize; ++i) {
  252. int_keys[i] = i;
  253. }
  254. for (auto insertions : {0ul, 1ul, kSize / 2, kSize, 2 * kSize}) {
  255. BenchmarkVectorCreationAndInsertion<std::vector<uint64_t>>(
  256. "std::vector<uint64_t>", kOps, insertions, int_keys);
  257. BenchmarkVectorCreationAndInsertion<autovector<uint64_t, kSize>>(
  258. "autovector<uint64_t>", kOps, insertions, int_keys);
  259. cout << "-----------------------------------" << endl;
  260. }
  261. // Sequence Access Test
  262. cout << "=====================================================" << endl;
  263. cout << "Sequence Access Test" << endl;
  264. cout << "=====================================================" << endl;
  265. for (auto elem_size : {kSize / 2, kSize, 2 * kSize}) {
  266. BenchmarkSequenceAccess<std::vector<std::string>>("std::vector", kOps,
  267. elem_size);
  268. BenchmarkSequenceAccess<autovector<std::string, kSize>>("autovector", kOps,
  269. elem_size);
  270. cout << "-----------------------------------" << endl;
  271. }
  272. }
  273. } // namespace ROCKSDB_NAMESPACE
  274. int main(int argc, char** argv) {
  275. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  276. ::testing::InitGoogleTest(&argc, argv);
  277. return RUN_ALL_TESTS();
  278. }