| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).#include <atomic>#include <iostream>#include <string>#include <utility>#include "rocksdb/env.h"#include "test_util/testharness.h"#include "test_util/testutil.h"#include "util/autovector.h"#include "util/string_util.h"using std::cout;using std::endl;namespace ROCKSDB_NAMESPACE {class AutoVectorTest : public testing::Test {};const unsigned long kSize = 8;namespace {template <class T>void AssertAutoVectorOnlyInStack(autovector<T, kSize>* vec, bool result) {#ifndef ROCKSDB_LITE  ASSERT_EQ(vec->only_in_stack(), result);#else  (void) vec;  (void) result;#endif  // !ROCKSDB_LITE}}  // namespaceTEST_F(AutoVectorTest, PushBackAndPopBack) {  autovector<size_t, kSize> vec;  ASSERT_TRUE(vec.empty());  ASSERT_EQ(0ul, vec.size());  for (size_t i = 0; i < 1000 * kSize; ++i) {    vec.push_back(i);    ASSERT_TRUE(!vec.empty());    if (i < kSize) {      AssertAutoVectorOnlyInStack(&vec, true);    } else {      AssertAutoVectorOnlyInStack(&vec, false);    }    ASSERT_EQ(i + 1, vec.size());    ASSERT_EQ(i, vec[i]);    ASSERT_EQ(i, vec.at(i));  }  size_t size = vec.size();  while (size != 0) {    vec.pop_back();    // will always be in heap    AssertAutoVectorOnlyInStack(&vec, false);    ASSERT_EQ(--size, vec.size());  }  ASSERT_TRUE(vec.empty());}TEST_F(AutoVectorTest, EmplaceBack) {  typedef std::pair<size_t, std::string> ValType;  autovector<ValType, kSize> vec;  for (size_t i = 0; i < 1000 * kSize; ++i) {    vec.emplace_back(i, ToString(i + 123));    ASSERT_TRUE(!vec.empty());    if (i < kSize) {      AssertAutoVectorOnlyInStack(&vec, true);    } else {      AssertAutoVectorOnlyInStack(&vec, false);    }    ASSERT_EQ(i + 1, vec.size());    ASSERT_EQ(i, vec[i].first);    ASSERT_EQ(ToString(i + 123), vec[i].second);  }  vec.clear();  ASSERT_TRUE(vec.empty());  AssertAutoVectorOnlyInStack(&vec, false);}TEST_F(AutoVectorTest, Resize) {  autovector<size_t, kSize> vec;  vec.resize(kSize);  AssertAutoVectorOnlyInStack(&vec, true);  for (size_t i = 0; i < kSize; ++i) {    vec[i] = i;  }  vec.resize(kSize * 2);  AssertAutoVectorOnlyInStack(&vec, false);  for (size_t i = 0; i < kSize; ++i) {    ASSERT_EQ(vec[i], i);  }  for (size_t i = 0; i < kSize; ++i) {    vec[i + kSize] = i;  }  vec.resize(1);  ASSERT_EQ(1U, vec.size());}namespace {void AssertEqual(    const autovector<size_t, kSize>& a, const autovector<size_t, kSize>& b) {  ASSERT_EQ(a.size(), b.size());  ASSERT_EQ(a.empty(), b.empty());#ifndef ROCKSDB_LITE  ASSERT_EQ(a.only_in_stack(), b.only_in_stack());#endif  // !ROCKSDB_LITE  for (size_t i = 0; i < a.size(); ++i) {    ASSERT_EQ(a[i], b[i]);  }}}  // namespaceTEST_F(AutoVectorTest, CopyAndAssignment) {  // Test both heap-allocated and stack-allocated cases.  for (auto size : { kSize / 2, kSize * 1000 }) {    autovector<size_t, kSize> vec;    for (size_t i = 0; i < size; ++i) {      vec.push_back(i);    }    {      autovector<size_t, kSize> other;      other = vec;      AssertEqual(other, vec);    }    {      autovector<size_t, kSize> other(vec);      AssertEqual(other, vec);    }  }}TEST_F(AutoVectorTest, Iterators) {  autovector<std::string, kSize> vec;  for (size_t i = 0; i < kSize * 1000; ++i) {    vec.push_back(ToString(i));  }  // basic operator test  ASSERT_EQ(vec.front(), *vec.begin());  ASSERT_EQ(vec.back(), *(vec.end() - 1));  ASSERT_TRUE(vec.begin() < vec.end());  // non-const iterator  size_t index = 0;  for (const auto& item : vec) {    ASSERT_EQ(vec[index++], item);  }  index = vec.size() - 1;  for (auto pos = vec.rbegin(); pos != vec.rend(); ++pos) {    ASSERT_EQ(vec[index--], *pos);  }  // const iterator  const auto& cvec = vec;  index = 0;  for (const auto& item : cvec) {    ASSERT_EQ(cvec[index++], item);  }  index = vec.size() - 1;  for (auto pos = cvec.rbegin(); pos != cvec.rend(); ++pos) {    ASSERT_EQ(cvec[index--], *pos);  }  // forward and backward  auto pos = vec.begin();  while (pos != vec.end()) {    auto old_val = *pos;    auto old = pos++;    // HACK: make sure -> works    ASSERT_TRUE(!old->empty());    ASSERT_EQ(old_val, *old);    ASSERT_TRUE(pos == vec.end() || old_val != *pos);  }  pos = vec.begin();  for (size_t i = 0; i < vec.size(); i += 2) {    // Cannot use ASSERT_EQ since that macro depends on iostream serialization    ASSERT_TRUE(pos + 2 - 2 == pos);    pos += 2;    ASSERT_TRUE(pos >= vec.begin());    ASSERT_TRUE(pos <= vec.end());    size_t diff = static_cast<size_t>(pos - vec.begin());    ASSERT_EQ(i + 2, diff);  }}namespace {std::vector<std::string> GetTestKeys(size_t size) {  std::vector<std::string> keys;  keys.resize(size);  int index = 0;  for (auto& key : keys) {    key = "item-" + ROCKSDB_NAMESPACE::ToString(index++);  }  return keys;}}  // namespacetemplate <class TVector>void BenchmarkVectorCreationAndInsertion(    std::string name, size_t ops, size_t item_size,    const std::vector<typename TVector::value_type>& items) {  auto env = Env::Default();  int index = 0;  auto start_time = env->NowNanos();  auto ops_remaining = ops;  while(ops_remaining--) {    TVector v;    for (size_t i = 0; i < item_size; ++i) {      v.push_back(items[index++]);    }  }  auto elapsed = env->NowNanos() - start_time;  cout << "created " << ops << " " << name << " instances:\n\t"       << "each was inserted with " << item_size << " elements\n\t"       << "total time elapsed: " << elapsed << " (ns)" << endl;}template <class TVector>size_t BenchmarkSequenceAccess(std::string name, size_t ops, size_t elem_size) {  TVector v;  for (const auto& item : GetTestKeys(elem_size)) {    v.push_back(item);  }  auto env = Env::Default();  auto ops_remaining = ops;  auto start_time = env->NowNanos();  size_t total = 0;  while (ops_remaining--) {    auto end = v.end();    for (auto pos = v.begin(); pos != end; ++pos) {      total += pos->size();    }  }  auto elapsed = env->NowNanos() - start_time;  cout << "performed " << ops << " sequence access against " << name << "\n\t"       << "size: " << elem_size << "\n\t"       << "total time elapsed: " << elapsed << " (ns)" << endl;  // HACK avoid compiler's optimization to ignore total  return total;}// This test case only reports the performance between std::vector<std::string>// and autovector<std::string>. We chose string for comparison because in most// of our use cases we used std::vector<std::string>.TEST_F(AutoVectorTest, PerfBench) {  // We run same operations for kOps times in order to get a more fair result.  size_t kOps = 100000;  // Creation and insertion test  // Test the case when there is:  //  * no element inserted: internal array of std::vector may not really get  //    initialize.  //  * one element inserted: internal array of std::vector must have  //    initialized.  //  * kSize elements inserted. This shows the most time we'll spend if we  //    keep everything in stack.  //  * 2 * kSize elements inserted. The internal vector of  //    autovector must have been initialized.  cout << "=====================================================" << endl;  cout << "Creation and Insertion Test (value type: std::string)" << endl;  cout << "=====================================================" << endl;  // pre-generated unique keys  auto string_keys = GetTestKeys(kOps * 2 * kSize);  for (auto insertions : { 0ul, 1ul, kSize / 2, kSize, 2 * kSize }) {    BenchmarkVectorCreationAndInsertion<std::vector<std::string>>(        "std::vector<std::string>", kOps, insertions, string_keys);    BenchmarkVectorCreationAndInsertion<autovector<std::string, kSize>>(        "autovector<std::string>", kOps, insertions, string_keys);    cout << "-----------------------------------" << endl;  }  cout << "=====================================================" << endl;  cout << "Creation and Insertion Test (value type: uint64_t)" << endl;  cout << "=====================================================" << endl;  // pre-generated unique keys  std::vector<uint64_t> int_keys(kOps * 2 * kSize);  for (size_t i = 0; i < kOps * 2 * kSize; ++i) {    int_keys[i] = i;  }  for (auto insertions : { 0ul, 1ul, kSize / 2, kSize, 2 * kSize }) {    BenchmarkVectorCreationAndInsertion<std::vector<uint64_t>>(        "std::vector<uint64_t>", kOps, insertions, int_keys);    BenchmarkVectorCreationAndInsertion<autovector<uint64_t, kSize>>(      "autovector<uint64_t>", kOps, insertions, int_keys    );    cout << "-----------------------------------" << endl;  }  // Sequence Access Test  cout << "=====================================================" << endl;  cout << "Sequence Access Test" << endl;  cout << "=====================================================" << endl;  for (auto elem_size : { kSize / 2, kSize, 2 * kSize }) {    BenchmarkSequenceAccess<std::vector<std::string>>("std::vector", kOps,                                                      elem_size);    BenchmarkSequenceAccess<autovector<std::string, kSize>>("autovector", kOps,                                                            elem_size);    cout << "-----------------------------------" << endl;  }}}  // namespace ROCKSDB_NAMESPACEint main(int argc, char** argv) {  ::testing::InitGoogleTest(&argc, argv);  return RUN_ALL_TESTS();}
 |