OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <array> |
| 6 #include <set> |
| 7 #include <unordered_set> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" |
| 11 #include "wtf/FlatSet.h" |
| 12 #include "wtf/HashSet.h" |
| 13 |
| 14 using testing::Values; |
| 15 |
| 16 template <> |
| 17 inline std::string testing::internal::GetTypeName<std::set<int>>() { |
| 18 return "std::set"; |
| 19 } |
| 20 |
| 21 template <> |
| 22 inline std::string testing::internal::GetTypeName<std::unordered_set<int>>() { |
| 23 return "std::unordered_set"; |
| 24 } |
| 25 |
| 26 template <> |
| 27 inline std::string testing::internal::GetTypeName<WTF::FlatSet<int>>() { |
| 28 return "WTF::FlatSet"; |
| 29 } |
| 30 |
| 31 template <> |
| 32 inline std::string testing::internal::GetTypeName<WTF::HashSet<int>>() { |
| 33 return "WTF::HashSet"; |
| 34 } |
| 35 |
| 36 using AssociativeSetTypes = testing::Types<std::set<int>, WTF::FlatSet<int>>; |
| 37 using SetTypes = testing::Types<std::set<int>, |
| 38 std::unordered_set<int>, |
| 39 WTF::FlatSet<int>, |
| 40 WTF::HashSet<int>>; |
| 41 |
| 42 namespace WTF { |
| 43 |
| 44 class Benchmark : public testing::Test { |
| 45 public: |
| 46 Benchmark() {} |
| 47 virtual ~Benchmark() {} |
| 48 |
| 49 enum { |
| 50 kBestOfNRuns = 10, |
| 51 kNumPermutedInts = 1 << 15, |
| 52 }; |
| 53 |
| 54 static void SetUpTestCase() { |
| 55 for (size_t i = 0; i < permuted_ints_.size(); i++) |
| 56 permuted_ints_[i] = i + 1; // WTF::HashSet doesn't like adding 0. |
| 57 |
| 58 // Apply a pseudo random permutation. |
| 59 int32_t num = 1234; |
| 60 for (size_t i = 0; i < permuted_ints_.size(); i++) { |
| 61 num = static_cast<int32_t>((num * 16807) % 2147483647L); |
| 62 std::swap(permuted_ints_[i], permuted_ints_[num % permuted_ints_.size()]); |
| 63 } |
| 64 } |
| 65 |
| 66 void RunBenchmarkSet(base::Callback<void(size_t)> init, |
| 67 base::Callback<void(size_t)> run) { |
| 68 for (size_t set_size : set_sizes_) { |
| 69 double best_time = 0; |
| 70 for (size_t i = 0; i < kBestOfNRuns; i++) { |
| 71 double time = RunBenchmark(init, run, set_size); |
| 72 if (i == 0) { |
| 73 best_time = time; |
| 74 } else if (time < best_time) { |
| 75 best_time = time; |
| 76 } |
| 77 } |
| 78 std::cout << "Set size " << set_size << " runtime (us) " << best_time |
| 79 << std::endl; |
| 80 } |
| 81 } |
| 82 |
| 83 static std::array<int, kNumPermutedInts> permuted_ints_; |
| 84 |
| 85 private: |
| 86 double RunBenchmark(base::Callback<void(size_t)> init, |
| 87 base::Callback<void(size_t)> run, |
| 88 size_t set_size) { |
| 89 init.Run(set_size); |
| 90 base::ThreadTicks start = base::ThreadTicks::Now(); |
| 91 base::ThreadTicks now; |
| 92 unsigned long long num_iterations = 0; |
| 93 do { |
| 94 run.Run(set_size); |
| 95 now = base::ThreadTicks::Now(); |
| 96 num_iterations++; |
| 97 } while (now - start < base::TimeDelta::FromMilliseconds(100)); |
| 98 return (now - start).InMicroseconds() / static_cast<double>(num_iterations); |
| 99 } |
| 100 |
| 101 std::vector<size_t> set_sizes_ = {1, 2, 4, 8, 16, 32, |
| 102 64, 128, 256, 512, 1024, 2048, |
| 103 4096, 8192, 16384, 32768}; |
| 104 }; |
| 105 |
| 106 std::array<int, Benchmark::kNumPermutedInts> Benchmark::permuted_ints_; |
| 107 |
| 108 template <typename SetType> |
| 109 class SetBenchmark : public Benchmark { |
| 110 public: |
| 111 SetType set_; |
| 112 size_t index_ = 0; |
| 113 size_t run_ = 0; |
| 114 |
| 115 void setClear() { set_.clear(); } |
| 116 |
| 117 void setInsertUnique(int i) { setInsert(i); } |
| 118 |
| 119 void setInsert(int i) { set_.insert(i); } |
| 120 |
| 121 void setErase(int i) { set_.erase(i); } |
| 122 |
| 123 void setEraseFront() { set_.erase(set_.begin()); } |
| 124 |
| 125 void setEraseBack() { set_.erase(std::prev(set_.end())); } |
| 126 }; |
| 127 |
| 128 template <> |
| 129 void SetBenchmark<FlatSet<int>>::setInsertUnique(int i) { |
| 130 set_.insertUnique(i); |
| 131 } |
| 132 |
| 133 template <> |
| 134 void SetBenchmark<FlatSet<int>>::setEraseBack() { |
| 135 set_.erase(set_.rbegin()); |
| 136 } |
| 137 |
| 138 template <> |
| 139 void SetBenchmark<HashSet<int>>::setInsert(int i) { |
| 140 set_.add(i); |
| 141 } |
| 142 |
| 143 template <> |
| 144 void SetBenchmark<HashSet<int>>::setErase(int i) { |
| 145 set_.remove(i); |
| 146 } |
| 147 |
| 148 TYPED_TEST_CASE(SetBenchmark, SetTypes); |
| 149 |
| 150 TYPED_TEST(SetBenchmark, InsertAndErasePseudoRandom) { |
| 151 Benchmark::RunBenchmarkSet( |
| 152 base::Bind([](SetBenchmark<TypeParam>* fixture, |
| 153 size_t set_size) { fixture->setClear(); }, |
| 154 base::Unretained(this)), |
| 155 base::Bind( |
| 156 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 157 const auto& permutation = Benchmark::permuted_ints_; |
| 158 size_t& index = fixture->index_; |
| 159 |
| 160 for (size_t i = 0; i < set_size; i++) { |
| 161 fixture->setInsert(permutation[index++ % permutation.size()]); |
| 162 } |
| 163 for (size_t i = 0; i < set_size; i++) { |
| 164 fixture->setErase(permutation[index++ % permutation.size()]); |
| 165 } |
| 166 }, |
| 167 base::Unretained(this))); |
| 168 } |
| 169 |
| 170 TYPED_TEST(SetBenchmark, InsertAndEraseOutsideIn) { |
| 171 Benchmark::RunBenchmarkSet( |
| 172 base::Bind([](SetBenchmark<TypeParam>* fixture, |
| 173 size_t set_size) { fixture->set_.clear(); }, |
| 174 base::Unretained(this)), |
| 175 base::Bind( |
| 176 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 177 for (size_t i = 1; i < set_size; i++) { |
| 178 fixture->setInsertUnique(set_size - i); |
| 179 fixture->setInsertUnique(set_size + i); |
| 180 } |
| 181 for (size_t i = set_size - 1; i > 0; i--) { |
| 182 fixture->setErase(set_size - i); |
| 183 fixture->setErase(set_size + i); |
| 184 } |
| 185 }, |
| 186 base::Unretained(this))); |
| 187 } |
| 188 |
| 189 TYPED_TEST(SetBenchmark, InsertAndEraseInsideOut) { |
| 190 Benchmark::RunBenchmarkSet( |
| 191 base::Bind([](SetBenchmark<TypeParam>* fixture, |
| 192 size_t set_size) { fixture->set_.clear(); }, |
| 193 base::Unretained(this)), |
| 194 base::Bind( |
| 195 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 196 for (size_t i = 1; i < set_size; i++) { |
| 197 fixture->setInsertUnique(i); |
| 198 fixture->setInsertUnique(set_size + set_size - i); |
| 199 } |
| 200 for (size_t i = set_size - 1; i > 0; i--) { |
| 201 fixture->setErase(i); |
| 202 fixture->setErase(set_size + set_size - i); |
| 203 } |
| 204 }, |
| 205 base::Unretained(this))); |
| 206 } |
| 207 |
| 208 TYPED_TEST(SetBenchmark, FixedSizeFind) { |
| 209 size_t num = 0; |
| 210 Benchmark::RunBenchmarkSet( |
| 211 base::Bind( |
| 212 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 213 const auto& permutation = Benchmark::permuted_ints_; |
| 214 |
| 215 fixture->setClear(); |
| 216 for (size_t i = 0; i < set_size; i++) |
| 217 fixture->setInsert(permutation[i % permutation.size()]); |
| 218 }, |
| 219 base::Unretained(this)), |
| 220 base::Bind( |
| 221 [](SetBenchmark<TypeParam>* fixture, size_t* num, size_t set_size) { |
| 222 const auto& permutation = Benchmark::permuted_ints_; |
| 223 |
| 224 for (size_t i = 0; i < set_size; i++) { |
| 225 if (fixture->set_.find(permutation[i % permutation.size()]) != |
| 226 fixture->set_.end()) { |
| 227 (*num)++; // Prevent the compiler from optimizing stuff away. |
| 228 } |
| 229 } |
| 230 }, |
| 231 base::Unretained(this), base::Unretained(&num))); |
| 232 } |
| 233 |
| 234 template <typename SetType> |
| 235 class AssociativeSetBenchmark : public SetBenchmark<SetType> {}; |
| 236 |
| 237 TYPED_TEST_CASE(AssociativeSetBenchmark, AssociativeSetTypes); |
| 238 |
| 239 TYPED_TEST(AssociativeSetBenchmark, FixedSizeEraseAndInsert) { |
| 240 Benchmark::RunBenchmarkSet( |
| 241 base::Bind( |
| 242 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 243 const auto& permutation = Benchmark::permuted_ints_; |
| 244 size_t& index = fixture->index_; |
| 245 |
| 246 fixture->setClear(); |
| 247 for (index = 0; index < set_size; index++) { |
| 248 fixture->setInsert(permutation[index % permutation.size()]); |
| 249 } |
| 250 }, |
| 251 base::Unretained(this)), |
| 252 base::Bind( |
| 253 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 254 const auto& permutation = Benchmark::permuted_ints_; |
| 255 size_t& index = fixture->index_; |
| 256 size_t& run = fixture->run_; // Necessary to ensure uniqueness. |
| 257 |
| 258 for (size_t i = 0; i < 10000; i++) { |
| 259 fixture->setEraseFront(); |
| 260 fixture->setInsertUnique( |
| 261 run + permutation[index++ % permutation.size()]); |
| 262 } |
| 263 |
| 264 run += permutation.size(); |
| 265 }, |
| 266 base::Unretained(this))); |
| 267 } |
| 268 |
| 269 TYPED_TEST(AssociativeSetBenchmark, InsertAndEraseOutsideIn) { |
| 270 Benchmark::RunBenchmarkSet( |
| 271 base::Bind([](SetBenchmark<TypeParam>* fixture, |
| 272 size_t set_size) { fixture->set_.clear(); }, |
| 273 base::Unretained(this)), |
| 274 base::Bind( |
| 275 [](SetBenchmark<TypeParam>* fixture, size_t set_size) { |
| 276 for (size_t i = 1; i < set_size; i++) { |
| 277 fixture->setInsertUnique(set_size - i); |
| 278 fixture->setInsertUnique(set_size + i); |
| 279 } |
| 280 for (size_t i = set_size - 1; i > 0; i--) { |
| 281 fixture->setEraseFront(); |
| 282 fixture->setEraseBack(); |
| 283 } |
| 284 }, |
| 285 base::Unretained(this))); |
| 286 EXPECT_TRUE(this->set_.empty()); |
| 287 } |
| 288 |
| 289 } // namespace WTF |
OLD | NEW |