autovector_test.cc 9.4 KB

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