Fix copyright lines for Bits.h and move BitsBenchmark.cpp
[folly.git] / folly / test / CachelinePaddedTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/CachelinePadded.h>
18
19 #include <type_traits>
20
21 #include <folly/lang/Align.h>
22 #include <folly/portability/GTest.h>
23
24 using folly::CachelinePadded;
25
26 static_assert(
27     std::is_standard_layout<CachelinePadded<int>>::value,
28     "CachelinePadded<T> must be standard-layout if T is.");
29
30 static constexpr int kCachelineSize =
31     folly::hardware_destructive_interference_size;
32
33 template <size_t dataSize, size_t alignment = alignof(void*)>
34 struct alignas(alignment) SizedData {
35   SizedData() {
36     size_t i = 0;
37     for (auto& datum : data) {
38       datum = i++;
39     }
40   }
41
42   void doModifications() {
43     size_t i = 0;
44     for (auto& datum : data) {
45       EXPECT_EQ(static_cast<unsigned char>(i++), datum);
46       ++datum;
47     }
48   }
49
50   ~SizedData() {
51     size_t i = 1;
52     for (auto& datum : data) {
53       EXPECT_EQ(static_cast<unsigned char>(i++), datum);
54     }
55   }
56
57   unsigned char data[dataSize];
58 };
59
60 template <typename T, size_t N = 1>
61 using SizedDataMimic = SizedData<N * sizeof(T), alignof(T)>;
62
63 template <typename T>
64 struct CachelinePaddedTests : ::testing::Test {};
65
66 using CachelinePaddedTypes = ::testing::Types<
67     SizedData<kCachelineSize>,
68     SizedData<2 * kCachelineSize>,
69     SizedData<kCachelineSize / 2>,
70     SizedData<kCachelineSize + kCachelineSize / 2>,
71     // Mimic single basic types:
72     SizedDataMimic<folly::max_align_t>,
73     SizedDataMimic<void*>,
74     SizedDataMimic<long double>,
75     SizedDataMimic<double>,
76     SizedDataMimic<float>,
77     SizedDataMimic<long long>,
78     SizedDataMimic<long>,
79     SizedDataMimic<int>,
80     SizedDataMimic<short>,
81     SizedDataMimic<char>,
82     // Mimic small arrays of basic types:
83     SizedDataMimic<folly::max_align_t, 3>,
84     SizedDataMimic<void*, 3>,
85     SizedDataMimic<long double, 3>,
86     SizedDataMimic<double, 3>,
87     SizedDataMimic<float, 3>,
88     SizedDataMimic<long long, 3>,
89     SizedDataMimic<long, 3>,
90     SizedDataMimic<int, 3>,
91     SizedDataMimic<short, 3>,
92     SizedDataMimic<char, 3>,
93     // Mimic large arrays of basic types:
94     SizedDataMimic<folly::max_align_t, kCachelineSize + 3>,
95     SizedDataMimic<void*, kCachelineSize + 3>,
96     SizedDataMimic<long double, kCachelineSize + 3>,
97     SizedDataMimic<double, kCachelineSize + 3>,
98     SizedDataMimic<float, kCachelineSize + 3>,
99     SizedDataMimic<long long, kCachelineSize + 3>,
100     SizedDataMimic<long, kCachelineSize + 3>,
101     SizedDataMimic<int, kCachelineSize + 3>,
102     SizedDataMimic<short, kCachelineSize + 3>,
103     SizedDataMimic<char, kCachelineSize + 3>>;
104 TYPED_TEST_CASE(CachelinePaddedTests, CachelinePaddedTypes);
105
106 TYPED_TEST(CachelinePaddedTests, alignment) {
107   EXPECT_EQ(alignof(TypeParam), alignof(CachelinePadded<TypeParam>));
108 }
109
110 TYPED_TEST(CachelinePaddedTests, integrity) {
111   CachelinePadded<TypeParam> item;
112   item.get()->doModifications();
113 }
114
115 TYPED_TEST(CachelinePaddedTests, size) {
116   EXPECT_GT(
117       sizeof(TypeParam) + 2 * kCachelineSize,
118       sizeof(CachelinePadded<TypeParam>));
119   size_t const rawSize = sizeof(TypeParam);
120   size_t const rawAlign = alignof(TypeParam);
121   size_t const expectedPadding = kCachelineSize - (rawAlign % kCachelineSize);
122   size_t const expectedPaddedSize = rawSize + 2 * expectedPadding;
123   EXPECT_EQ(expectedPaddedSize, sizeof(CachelinePadded<TypeParam>));
124 }
125
126 TEST(CachelinePadded, PtrOperator) {
127   CachelinePadded<int> padded;
128   EXPECT_TRUE(padded.get() == padded.operator->());
129   EXPECT_TRUE(&*padded == padded.get());
130   const CachelinePadded<int> constPadded;
131   EXPECT_TRUE(constPadded.get() == constPadded.operator->());
132   EXPECT_TRUE(constPadded.get() == &*constPadded.get());
133 }
134
135 TEST(CachelinePadded, PropagatesConstness) {
136   struct OverloadedOnConst {
137     void assign(int* dst) {
138       *dst = 31415;
139     }
140     void assign(int* dst) const {
141       *dst = 271828;
142     }
143   };
144
145   CachelinePadded<OverloadedOnConst> padded;
146
147   int i = 0;
148   padded->assign(&i);
149   EXPECT_EQ(31415, i);
150
151   const CachelinePadded<OverloadedOnConst> constPadded;
152   constPadded->assign(&i);
153   EXPECT_EQ(271828, i);
154 }
155
156 TEST(CachelinePadded, ConstructsAndDestructs) {
157   enum LifetimeStatus {
158     kNone,
159     kConstructed,
160     kDestroyed,
161   };
162   struct WriteOnLifetimeOp {
163     explicit WriteOnLifetimeOp(LifetimeStatus* dst) : dst_(dst) {
164       *dst = kConstructed;
165     }
166     ~WriteOnLifetimeOp() {
167       *dst_ = kDestroyed;
168     }
169     LifetimeStatus* dst_;
170   };
171   LifetimeStatus status = kNone;
172   CachelinePadded<WriteOnLifetimeOp>* ptr =
173       new CachelinePadded<WriteOnLifetimeOp>(&status);
174   EXPECT_EQ(kConstructed, status);
175   delete ptr;
176   EXPECT_EQ(kDestroyed, status);
177 }
178
179 TEST(CachelinePadded, ConstructsAndDestructsArrays) {
180   static thread_local int numConstructions;
181   static thread_local int numDestructions;
182   numConstructions = 0;
183   numDestructions = 0;
184   struct LifetimeCountingClass {
185     LifetimeCountingClass() {
186       ++numConstructions;
187     }
188     ~LifetimeCountingClass() {
189       ++numDestructions;
190     }
191   };
192   const static int kNumItems = 123;
193   CachelinePadded<LifetimeCountingClass>* ptr =
194       new CachelinePadded<LifetimeCountingClass>[kNumItems];
195   EXPECT_EQ(kNumItems, numConstructions);
196   delete[] ptr;
197   EXPECT_EQ(kNumItems, numDestructions);
198 }
199
200 TEST(CachelinePadded, ForwardsCorrectly) {
201   struct RvalueOverloadedConstructor {
202     RvalueOverloadedConstructor(int* dst, int& /* ignored */) {
203       *dst = 0;
204     }
205     RvalueOverloadedConstructor(int* dst, int&& /* ignored */) {
206       *dst = 1;
207     }
208   };
209   int shouldBeZero = 12345;
210   int shouldBeOne = 67890;
211   {
212     int ignored = 42;
213     CachelinePadded<RvalueOverloadedConstructor> padded1(
214         &shouldBeZero, ignored);
215     CachelinePadded<RvalueOverloadedConstructor> padded2(
216         &shouldBeOne, static_cast<int&&>(ignored));
217   }
218   EXPECT_EQ(0, shouldBeZero);
219   EXPECT_EQ(1, shouldBeOne);
220 }