aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/core/lib/gtl/inlined_vector_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/core/lib/gtl/inlined_vector_test.cc')
-rw-r--r--tensorflow/core/lib/gtl/inlined_vector_test.cc905
1 files changed, 905 insertions, 0 deletions
diff --git a/tensorflow/core/lib/gtl/inlined_vector_test.cc b/tensorflow/core/lib/gtl/inlined_vector_test.cc
new file mode 100644
index 0000000000..ec5fe1eaa8
--- /dev/null
+++ b/tensorflow/core/lib/gtl/inlined_vector_test.cc
@@ -0,0 +1,905 @@
+#include "tensorflow/core/lib/gtl/inlined_vector.h"
+
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/platform/port.h"
+#include "tensorflow/core/platform/test_benchmark.h"
+#include <gtest/gtest.h>
+
+namespace tensorflow {
+
+typedef tensorflow::gtl::InlinedVector<int, 8> IntVec;
+
+// A type that counts number of live occurrences of the type
+static int64 instances = 0;
+class Instance {
+ public:
+ int value_;
+ explicit Instance(int x) : value_(x) { instances++; }
+ Instance(const Instance& x) : value_(x.value_) { instances++; }
+ ~Instance() { instances--; }
+
+ friend inline void swap(Instance& a, Instance& b) {
+ using std::swap;
+ swap(a.value_, b.value_);
+ }
+
+ friend std::ostream& operator<<(std::ostream& o, const Instance& v) {
+ return o << "[value:" << v.value_ << "]";
+ }
+};
+
+typedef tensorflow::gtl::InlinedVector<Instance, 8> InstanceVec;
+
+// A simple reference counted class to make sure that the proper elements are
+// destroyed in the erase(begin, end) test.
+class RefCounted {
+ public:
+ RefCounted(int value, int* count) : value_(value), count_(count) { Ref(); }
+
+ RefCounted(const RefCounted& v) : value_(v.value_), count_(v.count_) {
+ VLOG(5) << "[RefCounted: copy"
+ << " from count @" << v.count_ << "]";
+ Ref();
+ }
+
+ ~RefCounted() {
+ Unref();
+ count_ = NULL;
+ }
+
+ friend void swap(RefCounted& a, RefCounted& b) {
+ using std::swap;
+ swap(a.value_, b.value_);
+ swap(a.count_, b.count_);
+ }
+
+ RefCounted& operator=(RefCounted v) {
+ using std::swap;
+ swap(*this, v);
+ return *this;
+ }
+
+ void Ref() const {
+ CHECK(count_ != NULL);
+ ++(*count_);
+ VLOG(5) << "[Ref: refcount " << *count_ << " on count @" << count_ << "]";
+ }
+
+ void Unref() const {
+ --(*count_);
+ CHECK_GE(*count_, 0);
+ VLOG(5) << "[Unref: refcount " << *count_ << " on count @" << count_ << "]";
+ }
+
+ int count() const { return *count_; }
+
+ friend std::ostream& operator<<(std::ostream& o, const RefCounted& v) {
+ return o << "[value:" << v.value_ << ", count:" << *v.count_ << "]";
+ }
+
+ int value_;
+ int* count_;
+};
+
+typedef tensorflow::gtl::InlinedVector<RefCounted, 8> RefCountedVec;
+
+// A class with a vtable pointer
+class Dynamic {
+ public:
+ virtual ~Dynamic() {}
+
+ friend std::ostream& operator<<(std::ostream& o, const Dynamic& v) {
+ return o << "[Dynamic]";
+ }
+};
+
+typedef tensorflow::gtl::InlinedVector<Dynamic, 8> DynamicVec;
+
+// Append 0..len-1 to *v
+static void Fill(IntVec* v, int len, int offset = 0) {
+ for (int i = 0; i < len; i++) {
+ v->push_back(i + offset);
+ }
+}
+
+static IntVec Fill(int len, int offset = 0) {
+ IntVec v;
+ Fill(&v, len, offset);
+ return v;
+}
+
+TEST(IntVec, SimpleOps) {
+ for (int len = 0; len < 20; len++) {
+ IntVec v;
+ const IntVec& cv = v; // const alias
+
+ Fill(&v, len);
+ EXPECT_EQ(len, v.size());
+ EXPECT_LE(len, v.capacity());
+
+ for (int i = 0; i < len; i++) {
+ EXPECT_EQ(i, v[i]);
+ }
+ EXPECT_EQ(v.begin(), v.array());
+ EXPECT_EQ(v.begin(), v.mutable_array());
+
+ EXPECT_EQ(v.begin(), v.data());
+ EXPECT_EQ(cv.begin(), cv.data());
+
+ int counter = 0;
+ for (IntVec::iterator iter = v.begin(); iter != v.end(); ++iter) {
+ EXPECT_EQ(counter, *iter);
+ counter++;
+ }
+ EXPECT_EQ(counter, len);
+
+ counter = 0;
+ for (IntVec::const_iterator iter = v.begin(); iter != v.end(); ++iter) {
+ EXPECT_EQ(counter, *iter);
+ counter++;
+ }
+ EXPECT_EQ(counter, len);
+
+ if (len > 0) {
+ EXPECT_EQ(0, v.front());
+ EXPECT_EQ(len - 1, v.back());
+ v.pop_back();
+ EXPECT_EQ(len - 1, v.size());
+ for (size_t i = 0; i < v.size(); ++i) {
+ EXPECT_EQ(i, v[i]);
+ }
+ }
+ }
+}
+
+TEST(IntVec, Erase) {
+ for (int len = 1; len < 20; len++) {
+ for (int i = 0; i < len; ++i) {
+ IntVec v;
+ Fill(&v, len);
+ v.erase(v.begin() + i);
+ EXPECT_EQ(len - 1, v.size());
+ for (int j = 0; j < i; ++j) {
+ EXPECT_EQ(j, v[j]);
+ }
+ for (int j = i; j < len - 1; ++j) {
+ EXPECT_EQ(j + 1, v[j]);
+ }
+ }
+ }
+}
+
+// At the end of this test loop, the elements between [erase_begin, erase_end)
+// should have reference counts == 0, and all others elements should have
+// reference counts == 1.
+TEST(RefCountedVec, EraseBeginEnd) {
+ for (int len = 1; len < 20; ++len) {
+ for (int erase_begin = 0; erase_begin < len; ++erase_begin) {
+ for (int erase_end = erase_begin; erase_end <= len; ++erase_end) {
+ std::vector<int> counts(len, 0);
+ RefCountedVec v;
+ for (int i = 0; i < len; ++i) {
+ v.push_back(RefCounted(i, &counts[i]));
+ }
+
+ int erase_len = erase_end - erase_begin;
+
+ v.erase(v.begin() + erase_begin, v.begin() + erase_end);
+
+ EXPECT_EQ(len - erase_len, v.size());
+
+ // Check the elements before the first element erased.
+ for (int i = 0; i < erase_begin; ++i) {
+ EXPECT_EQ(i, v[i].value_);
+ }
+
+ // Check the elements after the first element erased.
+ for (size_t i = erase_begin; i < v.size(); ++i) {
+ EXPECT_EQ(i + erase_len, v[i].value_);
+ }
+
+ // Check that the elements at the beginning are preserved.
+ for (int i = 0; i < erase_begin; ++i) {
+ EXPECT_EQ(1, counts[i]);
+ }
+
+ // Check that the erased elements are destroyed
+ for (int i = erase_begin; i < erase_end; ++i) {
+ EXPECT_EQ(0, counts[i]);
+ }
+
+ // Check that the elements at the end are preserved.
+ for (int i = erase_end; i < len; ++i) {
+ EXPECT_EQ(1, counts[i]);
+ }
+ }
+ }
+ }
+}
+
+struct NoDefaultCtor {
+ explicit NoDefaultCtor(int /* x */) {}
+};
+struct NoCopy {
+ NoCopy() {}
+ NoCopy(const NoCopy& /* x */) = delete;
+};
+struct NoAssign {
+ NoAssign() {}
+ NoAssign& operator=(const NoAssign& /* x */) = delete;
+};
+TEST(InlinedVectorTest, NoDefaultCtor) {
+ tensorflow::gtl::InlinedVector<NoDefaultCtor, 1> v(10, NoDefaultCtor(2));
+ (void)v;
+}
+TEST(InlinedVectorTest, NoCopy) {
+ tensorflow::gtl::InlinedVector<NoCopy, 1> v(10);
+ (void)v;
+}
+TEST(InlinedVectorTest, NoAssign) {
+ tensorflow::gtl::InlinedVector<NoAssign, 1> v(10);
+ (void)v;
+}
+
+TEST(IntVec, Insert) {
+ for (int len = 0; len < 20; len++) {
+ for (int pos = 0; pos <= len; pos++) {
+ IntVec v;
+ Fill(&v, len);
+ v.insert(v.begin() + pos, 9999);
+ EXPECT_EQ(v.size(), len + 1);
+ for (int i = 0; i < pos; i++) {
+ EXPECT_EQ(v[i], i);
+ }
+ EXPECT_EQ(v[pos], 9999);
+ for (size_t i = pos + 1; i < v.size(); i++) {
+ EXPECT_EQ(v[i], i - 1);
+ }
+ }
+ }
+}
+
+TEST(RefCountedVec, InsertConstructorDestructor) {
+ // Make sure the proper construction/destruction happen during insert
+ // operations.
+ for (int len = 0; len < 20; len++) {
+ SCOPED_TRACE(len);
+ for (int pos = 0; pos <= len; pos++) {
+ SCOPED_TRACE(pos);
+ std::vector<int> counts(len, 0);
+ RefCountedVec v;
+ for (int i = 0; i < len; ++i) {
+ SCOPED_TRACE(i);
+ v.push_back(RefCounted(i, &counts[i]));
+ }
+
+ for (auto elem : counts) {
+ EXPECT_EQ(1, elem);
+ }
+
+ int inserted_count = 0;
+ RefCounted insert_element(9999, &inserted_count);
+ EXPECT_EQ(1, inserted_count);
+ v.insert(v.begin() + pos, insert_element);
+ EXPECT_EQ(2, inserted_count);
+ // Check that the elements at the end are preserved.
+ for (auto elem : counts) {
+ EXPECT_EQ(1, elem);
+ }
+ EXPECT_EQ(2, inserted_count);
+ }
+ }
+}
+
+TEST(IntVec, Resize) {
+ for (int len = 0; len < 20; len++) {
+ IntVec v;
+ Fill(&v, len);
+
+ // Try resizing up and down by k elements
+ static const int kResizeElem = 1000000;
+ for (int k = 0; k < 10; k++) {
+ // Enlarging resize
+ v.resize(len + k, kResizeElem);
+ EXPECT_EQ(len + k, v.size());
+ EXPECT_LE(len + k, v.capacity());
+ for (int i = 0; i < len + k; i++) {
+ if (i < len) {
+ EXPECT_EQ(i, v[i]);
+ } else {
+ EXPECT_EQ(kResizeElem, v[i]);
+ }
+ }
+
+ // Shrinking resize
+ v.resize(len, kResizeElem);
+ EXPECT_EQ(len, v.size());
+ EXPECT_LE(len, v.capacity());
+ for (int i = 0; i < len; i++) {
+ EXPECT_EQ(i, v[i]);
+ }
+ }
+ }
+}
+
+TEST(IntVec, InitWithLength) {
+ for (int len = 0; len < 20; len++) {
+ IntVec v(len, 7);
+ EXPECT_EQ(len, v.size());
+ EXPECT_LE(len, v.capacity());
+ for (int i = 0; i < len; i++) {
+ EXPECT_EQ(7, v[i]);
+ }
+ }
+}
+
+TEST(IntVec, CopyConstructorAndAssignment) {
+ for (int len = 0; len < 20; len++) {
+ IntVec v;
+ Fill(&v, len);
+ EXPECT_EQ(len, v.size());
+ EXPECT_LE(len, v.capacity());
+
+ IntVec v2(v);
+ EXPECT_EQ(v, v2);
+
+ for (int start_len = 0; start_len < 20; start_len++) {
+ IntVec v3;
+ Fill(&v3, start_len, 99); // Add dummy elements that should go away
+ v3 = v;
+ EXPECT_EQ(v, v3);
+ }
+ }
+}
+
+TEST(OverheadTest, Storage) {
+ // Check for size overhead.
+ // In particular, ensure that std::allocator doesn't cost anything to store.
+ // The union should be absorbing some of the allocation bookkeeping overhead
+ // in the larger vectors, leaving only the size_ field as overhead.
+ using tensorflow::gtl::InlinedVector;
+ EXPECT_EQ(3 * sizeof(int*),
+ sizeof(InlinedVector<int*, 1>) - 1 * sizeof(int*));
+ EXPECT_EQ(2 * sizeof(int*),
+ sizeof(InlinedVector<int*, 2>) - 2 * sizeof(int*));
+ EXPECT_EQ(1 * sizeof(int*),
+ sizeof(InlinedVector<int*, 3>) - 3 * sizeof(int*));
+ EXPECT_EQ(1 * sizeof(int*),
+ sizeof(InlinedVector<int*, 4>) - 4 * sizeof(int*));
+ EXPECT_EQ(1 * sizeof(int*),
+ sizeof(InlinedVector<int*, 5>) - 5 * sizeof(int*));
+ EXPECT_EQ(1 * sizeof(int*),
+ sizeof(InlinedVector<int*, 6>) - 6 * sizeof(int*));
+ EXPECT_EQ(1 * sizeof(int*),
+ sizeof(InlinedVector<int*, 7>) - 7 * sizeof(int*));
+ EXPECT_EQ(1 * sizeof(int*),
+ sizeof(InlinedVector<int*, 8>) - 8 * sizeof(int*));
+}
+
+TEST(IntVec, Clear) {
+ for (int len = 0; len < 20; len++) {
+ SCOPED_TRACE(len);
+ IntVec v;
+ Fill(&v, len);
+ v.clear();
+ EXPECT_EQ(0, v.size());
+ EXPECT_EQ(v.begin(), v.end());
+ }
+}
+
+TEST(IntVec, Reserve) {
+ for (size_t len = 0; len < 20; len++) {
+ IntVec v;
+ Fill(&v, len);
+
+ for (size_t newlen = 0; newlen < 100; newlen++) {
+ const int* start_rep = v.array();
+ v.reserve(newlen);
+ const int* final_rep = v.array();
+ if (newlen <= len) {
+ EXPECT_EQ(start_rep, final_rep);
+ }
+ EXPECT_LE(newlen, v.capacity());
+
+ // Filling up to newlen should not change rep
+ while (v.size() < newlen) {
+ v.push_back(0);
+ }
+ EXPECT_EQ(final_rep, v.array());
+ }
+ }
+}
+
+template <typename T>
+static std::vector<typename T::value_type> Vec(const T& src) {
+ std::vector<typename T::value_type> result;
+ for (const auto& elem : src) {
+ result.push_back(elem);
+ }
+ return result;
+}
+
+TEST(IntVec, SelfRefPushBack) {
+ std::vector<string> std_v;
+ tensorflow::gtl::InlinedVector<string, 4> v;
+ const string s = "A very long string to ensure heap.";
+ std_v.push_back(s);
+ v.push_back(s);
+ for (int i = 0; i < 20; ++i) {
+ EXPECT_EQ(std_v, Vec(v));
+
+ v.push_back(v.back());
+ std_v.push_back(std_v.back());
+ }
+ EXPECT_EQ(std_v, Vec(v));
+}
+
+TEST(IntVec, Swap) {
+ for (int l1 = 0; l1 < 20; l1++) {
+ SCOPED_TRACE(l1);
+ for (int l2 = 0; l2 < 20; l2++) {
+ SCOPED_TRACE(l2);
+ IntVec a = Fill(l1, 0);
+ IntVec b = Fill(l2, 100);
+ {
+ using std::swap;
+ swap(a, b);
+ }
+ EXPECT_EQ(l1, b.size());
+ EXPECT_EQ(l2, a.size());
+ for (int i = 0; i < l1; i++) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(i, b[i]);
+ }
+ for (int i = 0; i < l2; i++) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(100 + i, a[i]);
+ }
+ }
+ }
+}
+
+TEST(InstanceVec, Swap) {
+ for (int l1 = 0; l1 < 20; l1++) {
+ for (int l2 = 0; l2 < 20; l2++) {
+ InstanceVec a, b;
+ for (int i = 0; i < l1; i++) a.push_back(Instance(i));
+ for (int i = 0; i < l2; i++) b.push_back(Instance(100 + i));
+ EXPECT_EQ(l1 + l2, instances);
+ {
+ using std::swap;
+ swap(a, b);
+ }
+ EXPECT_EQ(l1 + l2, instances);
+ EXPECT_EQ(l1, b.size());
+ EXPECT_EQ(l2, a.size());
+ for (int i = 0; i < l1; i++) {
+ EXPECT_EQ(i, b[i].value_);
+ }
+ for (int i = 0; i < l2; i++) {
+ EXPECT_EQ(100 + i, a[i].value_);
+ }
+ }
+ }
+}
+
+TEST(IntVec, EqualAndNotEqual) {
+ IntVec a, b;
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+
+ a.push_back(3);
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+
+ b.push_back(3);
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+
+ b.push_back(7);
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+
+ a.push_back(6);
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+
+ a.clear();
+ b.clear();
+ for (int i = 0; i < 100; i++) {
+ a.push_back(i);
+ b.push_back(i);
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+
+ b[i] = b[i] + 1;
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+
+ b[i] = b[i] - 1; // Back to before
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+ }
+}
+
+TEST(IntVec, RelationalOps) {
+ IntVec a, b;
+ EXPECT_FALSE(a < b);
+ EXPECT_FALSE(b < a);
+ EXPECT_FALSE(a > b);
+ EXPECT_FALSE(b > a);
+ EXPECT_TRUE(a <= b);
+ EXPECT_TRUE(b <= a);
+ EXPECT_TRUE(a >= b);
+ EXPECT_TRUE(b >= a);
+ b.push_back(3);
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(b < a);
+ EXPECT_FALSE(a > b);
+ EXPECT_TRUE(b > a);
+ EXPECT_TRUE(a <= b);
+ EXPECT_FALSE(b <= a);
+ EXPECT_FALSE(a >= b);
+ EXPECT_TRUE(b >= a);
+}
+
+TEST(InstanceVec, CountConstructorsDestructors) {
+ const int start = instances;
+ for (int len = 0; len < 20; len++) {
+ InstanceVec v;
+ for (int i = 0; i < len; i++) {
+ v.push_back(Instance(i));
+ }
+ EXPECT_EQ(start + len, instances);
+
+ { // Copy constructor should create 'len' more instances.
+ InstanceVec v_copy(v);
+ EXPECT_EQ(start + len + len, instances);
+ }
+ EXPECT_EQ(start + len, instances);
+
+ // Enlarging resize() must construct some objects
+ v.resize(len + 10, Instance(100));
+ EXPECT_EQ(start + len + 10, instances);
+
+ // Shrinking resize() must destroy some objects
+ v.resize(len, Instance(100));
+ EXPECT_EQ(start + len, instances);
+
+ // reserve() must not increase the number of initialized objects
+ v.reserve(len + 1000);
+ EXPECT_EQ(start + len, instances);
+
+ // pop_back() and erase() must destroy one object
+ if (len > 0) {
+ v.pop_back();
+ EXPECT_EQ(start + len - 1, instances);
+ if (!v.empty()) {
+ v.erase(v.begin());
+ EXPECT_EQ(start + len - 2, instances);
+ }
+ }
+ }
+ EXPECT_EQ(start, instances);
+}
+
+TEST(InstanceVec, CountConstructorsDestructorsOnAssignment) {
+ const int start = instances;
+ for (int len = 0; len < 20; len++) {
+ for (int longorshort = 0; longorshort <= 1; ++longorshort) {
+ InstanceVec longer, shorter;
+ for (int i = 0; i < len; i++) {
+ longer.push_back(Instance(i));
+ shorter.push_back(Instance(i));
+ }
+ longer.push_back(Instance(len));
+ EXPECT_EQ(start + len + len + 1, instances);
+
+ if (longorshort) {
+ shorter = longer;
+ EXPECT_EQ(start + (len + 1) + (len + 1), instances);
+ } else {
+ longer = shorter;
+ EXPECT_EQ(start + len + len, instances);
+ }
+ }
+ }
+ EXPECT_EQ(start, instances);
+}
+
+TEST(RangedConstructor, SimpleType) {
+ std::vector<int> source_v = {4, 5, 6};
+ // First try to fit in inline backing
+ tensorflow::gtl::InlinedVector<int, 4> v(source_v.begin(), source_v.end());
+ EXPECT_EQ(3, v.size());
+ EXPECT_EQ(4, v.capacity()); // Indication that we're still on inlined storage
+ EXPECT_EQ(4, v[0]);
+ EXPECT_EQ(5, v[1]);
+ EXPECT_EQ(6, v[2]);
+
+ // Now, force a re-allocate
+ tensorflow::gtl::InlinedVector<int, 2> realloc_v(source_v.begin(),
+ source_v.end());
+ EXPECT_EQ(3, realloc_v.size());
+ EXPECT_LT(2, realloc_v.capacity());
+ EXPECT_EQ(4, realloc_v[0]);
+ EXPECT_EQ(5, realloc_v[1]);
+ EXPECT_EQ(6, realloc_v[2]);
+}
+
+TEST(RangedConstructor, ComplexType) {
+ // We also use a list here to pass a different flavor of iterator (e.g. not
+ // random-access).
+ std::list<Instance> source_v = {Instance(0)};
+
+ // First try to fit in inline backing
+ tensorflow::gtl::InlinedVector<Instance, 1> v(source_v.begin(),
+ source_v.end());
+ EXPECT_EQ(1, v.size());
+ EXPECT_EQ(1, v.capacity()); // Indication that we're still on inlined storage
+ EXPECT_EQ(0, v[0].value_);
+
+ std::list<Instance> source_v2 = {Instance(0), Instance(1)};
+ // Now, force a re-allocate
+ tensorflow::gtl::InlinedVector<Instance, 1> realloc_v(source_v2.begin(),
+ source_v2.end());
+ EXPECT_EQ(2, realloc_v.size());
+ EXPECT_LT(1, realloc_v.capacity());
+ EXPECT_EQ(0, realloc_v[0].value_);
+ EXPECT_EQ(1, realloc_v[1].value_);
+}
+
+TEST(RangedConstructor, ElementsAreConstructed) {
+ std::vector<string> source_v = {"cat", "dog"};
+
+ // Force expansion and re-allocation of v. Ensures that when the vector is
+ // expanded that new elements are constructed.
+ tensorflow::gtl::InlinedVector<string, 1> v(source_v.begin(), source_v.end());
+ EXPECT_EQ("cat", v[0]);
+ EXPECT_EQ("dog", v[1]);
+}
+
+TEST(InitializerListConstructor, SimpleTypeWithInlineBacking) {
+ auto vec = tensorflow::gtl::InlinedVector<int, 4>{4, 5, 6};
+ EXPECT_EQ(3, vec.size());
+ EXPECT_EQ(4, vec.capacity());
+ EXPECT_EQ(4, vec[0]);
+ EXPECT_EQ(5, vec[1]);
+ EXPECT_EQ(6, vec[2]);
+}
+
+TEST(InitializerListConstructor, SimpleTypeWithReallocationRequired) {
+ auto vec = tensorflow::gtl::InlinedVector<int, 2>{4, 5, 6};
+ EXPECT_EQ(3, vec.size());
+ EXPECT_LE(3, vec.capacity());
+ EXPECT_EQ(4, vec[0]);
+ EXPECT_EQ(5, vec[1]);
+ EXPECT_EQ(6, vec[2]);
+}
+
+TEST(InitializerListConstructor, DisparateTypesInList) {
+ EXPECT_EQ((std::vector<int>{-7, 8}),
+ Vec(tensorflow::gtl::InlinedVector<int, 2>{-7, 8ULL}));
+
+ EXPECT_EQ(
+ (std::vector<string>{"foo", "bar"}),
+ Vec(tensorflow::gtl::InlinedVector<string, 2>{"foo", string("bar")}));
+}
+
+TEST(InitializerListConstructor, ComplexTypeWithInlineBacking) {
+ auto vec = tensorflow::gtl::InlinedVector<Instance, 1>{Instance(0)};
+ EXPECT_EQ(1, vec.size());
+ EXPECT_EQ(1, vec.capacity());
+ EXPECT_EQ(0, vec[0].value_);
+}
+
+TEST(InitializerListConstructor, ComplexTypeWithReallocationRequired) {
+ auto vec =
+ tensorflow::gtl::InlinedVector<Instance, 1>{Instance(0), Instance(1)};
+ EXPECT_EQ(2, vec.size());
+ EXPECT_LE(2, vec.capacity());
+ EXPECT_EQ(0, vec[0].value_);
+ EXPECT_EQ(1, vec[1].value_);
+}
+
+TEST(DynamicVec, DynamicVecCompiles) {
+ DynamicVec v;
+ (void)v;
+}
+
+#ifdef INLINED_VECTOR_HAS_ALLOC
+TEST(AllocatorSupportTest, Constructors) {
+ typedef STLCountingAllocator<int> MyAlloc;
+ typedef tensorflow::gtl::InlinedVector<int, 4, MyAlloc> AllocVec;
+ const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
+ int64 allocated = 0;
+ MyAlloc alloc(&allocated);
+ { AllocVec TF_ATTRIBUTE_UNUSED v; }
+ { AllocVec TF_ATTRIBUTE_UNUSED v(alloc); }
+ { AllocVec TF_ATTRIBUTE_UNUSED v(ia, ia + arraysize(ia), alloc); }
+#ifdef LANG_CXX11
+ { AllocVec TF_ATTRIBUTE_UNUSED v({1, 2, 3}, alloc); }
+#endif // LANG_CXX11
+}
+
+TEST(AllocatorSupportTest, CountAllocations) {
+ typedef STLCountingAllocator<int> MyAlloc;
+ typedef tensorflow::gtl::InlinedVector<int, 4, MyAlloc> AllocVec;
+ const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
+ int64 allocated = 0;
+ MyAlloc alloc(&allocated);
+ {
+ AllocVec TF_ATTRIBUTE_UNUSED v(ia, ia + 4, alloc);
+ EXPECT_THAT(allocated, 0);
+ }
+ EXPECT_THAT(allocated, 0);
+ {
+ AllocVec TF_ATTRIBUTE_UNUSED v(ia, ia + arraysize(ia), alloc);
+ EXPECT_THAT(allocated, v.size() * sizeof(int));
+ }
+ EXPECT_THAT(allocated, 0);
+}
+
+TEST(AllocatorSupportTest, SwapBothAllocated) {
+ typedef STLCountingAllocator<int> MyAlloc;
+ typedef tensorflow::gtl::InlinedVector<int, 4, MyAlloc> AllocVec;
+ int64 allocated1 = 0;
+ int64 allocated2 = 0;
+ {
+ const std::vector<int> ia1 = {0, 1, 2, 3, 4, 5, 6, 7};
+ const std::vector<int> ia2 = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ MyAlloc a1(&allocated1);
+ MyAlloc a2(&allocated2);
+ AllocVec v1(ia1.data(), ia1.data() + ia1.size(), a1);
+ AllocVec v2(ia2.data(), ia2.data() + ia2.size(), a2);
+ EXPECT_LT(v1.capacity(), v2.capacity());
+ EXPECT_THAT(allocated1, v1.capacity() * sizeof(int));
+ EXPECT_THAT(allocated2, v2.capacity() * sizeof(int));
+ v1.swap(v2);
+ EXPECT_EQ(ia2, Vec(v1));
+ EXPECT_EQ(ia1, Vec(v2));
+ EXPECT_THAT(allocated1, v2.capacity() * sizeof(int));
+ EXPECT_THAT(allocated2, v1.capacity() * sizeof(int));
+ }
+ EXPECT_THAT(allocated1, 0);
+ EXPECT_THAT(allocated2, 0);
+}
+
+TEST(AllocatorSupportTest, SwapOneAllocated) {
+ typedef STLCountingAllocator<int> MyAlloc;
+ typedef tensorflow::gtl::InlinedVector<int, 4, MyAlloc> AllocVec;
+ int64 allocated1 = 0;
+ int64 allocated2 = 0;
+ {
+ const std::vector<int> ia1 = {0, 1, 2, 3, 4, 5, 6, 7};
+ const std::vector<int> ia2 = {0, 1, 2, 3};
+ MyAlloc a1(&allocated1);
+ MyAlloc a2(&allocated2);
+ AllocVec v1(ia1.data(), ia1.data() + ia1.size(), a1);
+ AllocVec v2(ia2.data(), ia2.data() + ia2.size(), a2);
+ EXPECT_THAT(allocated1, v1.capacity() * sizeof(int));
+ EXPECT_THAT(allocated2, 0);
+ v1.swap(v2);
+ EXPECT_EQ(ia2, Vec(v1));
+ EXPECT_EQ(ia1, Vec(v2));
+ EXPECT_THAT(allocated1, v2.capacity() * sizeof(int));
+ EXPECT_THAT(allocated2, 0);
+ EXPECT_TRUE(v2.get_allocator() == a1);
+ EXPECT_TRUE(v1.get_allocator() == a2);
+ }
+ EXPECT_THAT(allocated1, 0);
+ EXPECT_THAT(allocated2, 0);
+}
+#endif // INLINED_VECTOR_HAS_ALLOC
+
+static void BM_InlinedVectorFill(int iters, int len) {
+ for (int i = 0; i < iters; i++) {
+ IntVec v;
+ for (int j = 0; j < len; j++) {
+ v.push_back(j);
+ }
+ }
+ testing::BytesProcessed((static_cast<int64>(iters) * len) * sizeof(int));
+}
+BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024);
+
+static void BM_InlinedVectorFillRange(int iters, int len) {
+ std::unique_ptr<int[]> ia(new int[len]);
+ for (int j = 0; j < len; j++) {
+ ia[j] = j;
+ }
+ for (int i = 0; i < iters; i++) {
+ IntVec TF_ATTRIBUTE_UNUSED v(ia.get(), ia.get() + len);
+ }
+ testing::BytesProcessed((static_cast<int64>(iters) * len) * sizeof(int));
+}
+BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024);
+
+static void BM_StdVectorFill(int iters, int len) {
+ for (int i = 0; i < iters; i++) {
+ std::vector<int> v;
+ for (int j = 0; j < len; j++) {
+ v.push_back(j);
+ }
+ }
+ testing::BytesProcessed((static_cast<int64>(iters) * len) * sizeof(int));
+}
+BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
+
+namespace {
+struct Buffer { // some arbitrary structure for benchmarking.
+ char* base;
+ int length;
+ int capacity;
+ void* user_data;
+};
+} // anonymous namespace
+
+static void BM_InlinedVectorTenAssignments(int iters, int len) {
+ typedef tensorflow::gtl::InlinedVector<Buffer, 2> BufferVec;
+
+ BufferVec src;
+ src.resize(len);
+
+ iters *= 10;
+ BufferVec dst;
+ for (int i = 0; i < iters; i++) {
+ dst = src;
+ }
+}
+BENCHMARK(BM_InlinedVectorTenAssignments)
+ ->Arg(0)
+ ->Arg(1)
+ ->Arg(2)
+ ->Arg(3)
+ ->Arg(4)
+ ->Arg(20);
+
+static void BM_CreateFromInitializerList(int iters) {
+ for (; iters > 0; iters--) {
+ tensorflow::gtl::InlinedVector<int, 4> x{1, 2, 3};
+ (void)x[0];
+ }
+}
+BENCHMARK(BM_CreateFromInitializerList);
+
+namespace {
+
+struct LargeSwappable {
+ LargeSwappable() : d_(1024, 17) {}
+ ~LargeSwappable() {}
+ LargeSwappable(const LargeSwappable& o) : d_(o.d_) {}
+
+ friend void swap(LargeSwappable& a, LargeSwappable& b) {
+ using std::swap;
+ swap(a.d_, b.d_);
+ }
+
+ LargeSwappable& operator=(LargeSwappable o) {
+ using std::swap;
+ swap(*this, o);
+ return *this;
+ }
+
+ std::vector<int> d_;
+};
+
+} // namespace
+
+static void BM_LargeSwappableElements(int iters, int len) {
+ typedef tensorflow::gtl::InlinedVector<LargeSwappable, 32> Vec;
+ Vec a(len);
+ Vec b;
+ while (--iters >= 0) {
+ using std::swap;
+ swap(a, b);
+ }
+}
+BENCHMARK(BM_LargeSwappableElements)->Range(0, 1024);
+
+} // namespace tensorflow