2 * Copyright 2012 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 // @author: Xin Liu <xliux@fb.com>
22 #include <system_error>
24 #include <glog/logging.h>
25 #include <gflags/gflags.h>
26 #include "folly/ConcurrentSkipList.h"
27 #include "folly/Foreach.h"
28 #include "folly/String.h"
29 #include "gtest/gtest.h"
31 DEFINE_int32(num_threads, 12, "num concurrent threads to test");
35 using namespace folly;
38 typedef int ValueType;
39 typedef detail::SkipListNode<ValueType> SkipListNodeType;
40 typedef ConcurrentSkipList<ValueType> SkipListType;
41 typedef SkipListType::Accessor SkipListAccessor;
42 typedef vector<ValueType> VectorType;
43 typedef std::set<ValueType> SetType;
45 static const int kHeadHeight = 2;
46 static const int kMaxValue = 5000;
48 static void randomAdding(int size,
49 SkipListAccessor skipList,
51 int maxValue = kMaxValue) {
52 for (int i = 0; i < size; ++i) {
53 int32_t r = rand() % maxValue;
59 static void randomRemoval(int size,
60 SkipListAccessor skipList,
62 int maxValue=kMaxValue) {
63 for (int i = 0; i < size; ++i) {
64 int32_t r = rand() % maxValue;
70 static void sumAllValues(SkipListAccessor skipList, int64_t *sum) {
72 FOR_EACH(it, skipList) {
75 VLOG(20) << "sum = " << sum;
78 static void concurrentSkip(const vector<ValueType> *values,
79 SkipListAccessor skipList) {
81 SkipListAccessor::Skipper skipper(skipList);
82 FOR_EACH(it, *values) {
83 if (skipper.to(*it)) sum += *it;
85 VLOG(20) << "sum = " << sum;
88 bool verifyEqual(SkipListAccessor skipList,
89 const SetType &verifier) {
90 EXPECT_EQ(verifier.size(), skipList.size());
91 FOR_EACH(it, verifier) {
92 CHECK(skipList.contains(*it)) << *it;
93 SkipListType::const_iterator iter = skipList.find(*it);
94 CHECK(iter != skipList.end());
95 EXPECT_EQ(*iter, *it);
97 EXPECT_TRUE(std::equal(verifier.begin(), verifier.end(), skipList.begin()));
101 TEST(ConcurrentSkipList, SequentialAccess) {
103 LOG(INFO) << "nodetype size=" << sizeof(SkipListNodeType);
105 auto skipList(SkipListType::create(kHeadHeight));
106 EXPECT_TRUE(skipList.first() == NULL);
107 EXPECT_TRUE(skipList.last() == NULL);
110 EXPECT_TRUE(skipList.contains(3));
111 EXPECT_FALSE(skipList.contains(2));
112 EXPECT_EQ(3, *skipList.first());
113 EXPECT_EQ(3, *skipList.last());
115 EXPECT_EQ(3, *skipList.find(3));
116 EXPECT_FALSE(skipList.find(3) == skipList.end());
117 EXPECT_TRUE(skipList.find(2) == skipList.end());
120 SkipListAccessor::Skipper skipper(skipList);
122 CHECK_EQ(3, *skipper);
126 EXPECT_EQ(2, *skipList.first());
127 EXPECT_EQ(3, *skipList.last());
129 EXPECT_EQ(5, *skipList.last());
131 EXPECT_EQ(5, *skipList.last());
132 auto ret = skipList.insert(9);
133 EXPECT_EQ(9, *ret.first);
134 EXPECT_TRUE(ret.second);
136 ret = skipList.insert(5);
137 EXPECT_EQ(5, *ret.first);
138 EXPECT_FALSE(ret.second);
140 EXPECT_EQ(2, *skipList.first());
141 EXPECT_EQ(9, *skipList.last());
142 EXPECT_TRUE(skipList.pop_back());
143 EXPECT_EQ(5, *skipList.last());
144 EXPECT_TRUE(skipList.pop_back());
145 EXPECT_EQ(3, *skipList.last());
150 CHECK(skipList.contains(2));
151 CHECK(skipList.contains(3));
152 CHECK(skipList.contains(5));
153 CHECK(skipList.contains(9));
154 CHECK(!skipList.contains(4));
157 auto it = skipList.lower_bound(5);
159 it = skipList.lower_bound(4);
161 it = skipList.lower_bound(9);
163 it = skipList.lower_bound(12);
164 EXPECT_FALSE(it.good());
166 it = skipList.begin();
170 SkipListAccessor::Skipper skipper(skipList);
172 EXPECT_EQ(3, skipper.data());
174 EXPECT_EQ(5, skipper.data());
175 CHECK(!skipper.to(7));
179 CHECK(skipper.to(9));
180 EXPECT_EQ(9, skipper.data());
182 CHECK(!skipList.contains(3));
184 CHECK(skipList.contains(3));
186 FOR_EACH(it, skipList) {
187 LOG(INFO) << "pos= " << pos++ << " value= " << *it;
192 auto skipList(SkipListType::create(kHeadHeight));
195 randomAdding(10000, skipList, &verifier);
196 verifyEqual(skipList, verifier);
199 SkipListAccessor::Skipper skipper(skipList);
200 int num_skips = 1000;
201 for (int i = 0; i < num_skips; ++i) {
202 int n = i * kMaxValue / num_skips;
203 bool found = skipper.to(n);
204 EXPECT_EQ(found, (verifier.find(n) != verifier.end()));
210 void testConcurrentAdd(int numThreads) {
211 auto skipList(SkipListType::create(kHeadHeight));
213 vector<std::thread> threads;
214 vector<SetType> verifiers(numThreads);
216 for (int i = 0; i < numThreads; ++i) {
217 threads.push_back(std::thread(
218 &randomAdding, 100, skipList, &verifiers[i], kMaxValue));
220 } catch (const std::system_error& e) {
222 << "Caught " << exceptionStr(e)
223 << ": could only create " << threads.size() << " threads out of "
226 for (int i = 0; i < threads.size(); ++i) {
231 FOR_EACH(s, verifiers) {
232 all.insert(s->begin(), s->end());
234 verifyEqual(skipList, all);
237 TEST(ConcurrentSkipList, ConcurrentAdd) {
238 // test it many times
239 for (int numThreads = 10; numThreads < 10000; numThreads += 1000) {
240 testConcurrentAdd(numThreads);
244 void testConcurrentRemoval(int numThreads, int maxValue) {
245 auto skipList = SkipListType::create(kHeadHeight);
246 for (int i = 0; i < maxValue; ++i) {
250 vector<std::thread> threads;
251 vector<SetType > verifiers(numThreads);
253 for (int i = 0; i < numThreads; ++i) {
254 threads.push_back(std::thread(
255 &randomRemoval, 100, skipList, &verifiers[i], maxValue));
257 } catch (const std::system_error& e) {
259 << "Caught " << exceptionStr(e)
260 << ": could only create " << threads.size() << " threads out of "
263 FOR_EACH(t, threads) {
268 FOR_EACH(s, verifiers) {
269 all.insert(s->begin(), s->end());
272 CHECK_EQ(maxValue, all.size() + skipList.size());
273 for (int i = 0; i < maxValue; ++i) {
274 if (all.find(i) != all.end()) {
275 CHECK(!skipList.contains(i)) << i;
277 CHECK(skipList.contains(i)) << i;
282 TEST(ConcurrentSkipList, ConcurrentRemove) {
283 for (int numThreads = 10; numThreads < 1000; numThreads += 100) {
284 testConcurrentRemoval(numThreads, 100 * numThreads);
288 static void testConcurrentAccess(
289 int numInsertions, int numDeletions, int maxValue) {
290 auto skipList = SkipListType::create(kHeadHeight);
292 vector<SetType> verifiers(FLAGS_num_threads);
293 vector<int64_t> sums(FLAGS_num_threads);
294 vector<vector<ValueType> > skipValues(FLAGS_num_threads);
296 for (int i = 0; i < FLAGS_num_threads; ++i) {
297 for (int j = 0; j < numInsertions; ++j) {
298 skipValues[i].push_back(rand() % (maxValue + 1));
300 std::sort(skipValues[i].begin(), skipValues[i].end());
303 vector<std::thread> threads;
304 for (int i = 0; i < FLAGS_num_threads; ++i) {
308 threads.push_back(std::thread(
309 randomAdding, numInsertions, skipList, &verifiers[i], maxValue));
312 threads.push_back(std::thread(
313 randomRemoval, numDeletions, skipList, &verifiers[i], maxValue));
316 threads.push_back(std::thread(
317 concurrentSkip, &skipValues[i], skipList));
320 threads.push_back(std::thread(sumAllValues, skipList, &sums[i]));
325 FOR_EACH(t, threads) {
328 // just run through it, no need to verify the correctness.
331 TEST(ConcurrentSkipList, ConcurrentAccess) {
332 testConcurrentAccess(10000, 100, kMaxValue);
333 testConcurrentAccess(100000, 10000, kMaxValue * 10);
334 testConcurrentAccess(1000000, 100000, kMaxValue);
339 int main(int argc, char* argv[]) {
340 testing::InitGoogleTest(&argc, argv);
341 google::InitGoogleLogging(argv[0]);
342 google::ParseCommandLineFlags(&argc, &argv, true);
344 return RUN_ALL_TESTS();