| Index: third_party/WebKit/Source/wtf/SetPerfTest.cpp
|
| diff --git a/third_party/WebKit/Source/wtf/SetPerfTest.cpp b/third_party/WebKit/Source/wtf/SetPerfTest.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..466851960b53e172c7d266f3019ed8bc56019952
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/wtf/SetPerfTest.cpp
|
| @@ -0,0 +1,289 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <array>
|
| +#include <set>
|
| +#include <unordered_set>
|
| +
|
| +#include "base/bind.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "wtf/FlatSet.h"
|
| +#include "wtf/HashSet.h"
|
| +
|
| +using testing::Values;
|
| +
|
| +template <>
|
| +inline std::string testing::internal::GetTypeName<std::set<int>>() {
|
| + return "std::set";
|
| +}
|
| +
|
| +template <>
|
| +inline std::string testing::internal::GetTypeName<std::unordered_set<int>>() {
|
| + return "std::unordered_set";
|
| +}
|
| +
|
| +template <>
|
| +inline std::string testing::internal::GetTypeName<WTF::FlatSet<int>>() {
|
| + return "WTF::FlatSet";
|
| +}
|
| +
|
| +template <>
|
| +inline std::string testing::internal::GetTypeName<WTF::HashSet<int>>() {
|
| + return "WTF::HashSet";
|
| +}
|
| +
|
| +using AssociativeSetTypes = testing::Types<std::set<int>, WTF::FlatSet<int>>;
|
| +using SetTypes = testing::Types<std::set<int>,
|
| + std::unordered_set<int>,
|
| + WTF::FlatSet<int>,
|
| + WTF::HashSet<int>>;
|
| +
|
| +namespace WTF {
|
| +
|
| +class Benchmark : public testing::Test {
|
| + public:
|
| + Benchmark() {}
|
| + virtual ~Benchmark() {}
|
| +
|
| + enum {
|
| + kBestOfNRuns = 10,
|
| + kNumPermutedInts = 1 << 15,
|
| + };
|
| +
|
| + static void SetUpTestCase() {
|
| + for (size_t i = 0; i < permuted_ints_.size(); i++)
|
| + permuted_ints_[i] = i + 1; // WTF::HashSet doesn't like adding 0.
|
| +
|
| + // Apply a pseudo random permutation.
|
| + int32_t num = 1234;
|
| + for (size_t i = 0; i < permuted_ints_.size(); i++) {
|
| + num = static_cast<int32_t>((num * 16807) % 2147483647L);
|
| + std::swap(permuted_ints_[i], permuted_ints_[num % permuted_ints_.size()]);
|
| + }
|
| + }
|
| +
|
| + void RunBenchmarkSet(base::Callback<void(size_t)> init,
|
| + base::Callback<void(size_t)> run) {
|
| + for (size_t set_size : set_sizes_) {
|
| + double best_time = 0;
|
| + for (size_t i = 0; i < kBestOfNRuns; i++) {
|
| + double time = RunBenchmark(init, run, set_size);
|
| + if (i == 0) {
|
| + best_time = time;
|
| + } else if (time < best_time) {
|
| + best_time = time;
|
| + }
|
| + }
|
| + std::cout << "Set size " << set_size << " runtime (us) " << best_time
|
| + << std::endl;
|
| + }
|
| + }
|
| +
|
| + static std::array<int, kNumPermutedInts> permuted_ints_;
|
| +
|
| + private:
|
| + double RunBenchmark(base::Callback<void(size_t)> init,
|
| + base::Callback<void(size_t)> run,
|
| + size_t set_size) {
|
| + init.Run(set_size);
|
| + base::ThreadTicks start = base::ThreadTicks::Now();
|
| + base::ThreadTicks now;
|
| + unsigned long long num_iterations = 0;
|
| + do {
|
| + run.Run(set_size);
|
| + now = base::ThreadTicks::Now();
|
| + num_iterations++;
|
| + } while (now - start < base::TimeDelta::FromMilliseconds(100));
|
| + return (now - start).InMicroseconds() / static_cast<double>(num_iterations);
|
| + }
|
| +
|
| + std::vector<size_t> set_sizes_ = {1, 2, 4, 8, 16, 32,
|
| + 64, 128, 256, 512, 1024, 2048,
|
| + 4096, 8192, 16384, 32768};
|
| +};
|
| +
|
| +std::array<int, Benchmark::kNumPermutedInts> Benchmark::permuted_ints_;
|
| +
|
| +template <typename SetType>
|
| +class SetBenchmark : public Benchmark {
|
| + public:
|
| + SetType set_;
|
| + size_t index_ = 0;
|
| + size_t run_ = 0;
|
| +
|
| + void setClear() { set_.clear(); }
|
| +
|
| + void setInsertUnique(int i) { setInsert(i); }
|
| +
|
| + void setInsert(int i) { set_.insert(i); }
|
| +
|
| + void setErase(int i) { set_.erase(i); }
|
| +
|
| + void setEraseFront() { set_.erase(set_.begin()); }
|
| +
|
| + void setEraseBack() { set_.erase(std::prev(set_.end())); }
|
| +};
|
| +
|
| +template <>
|
| +void SetBenchmark<FlatSet<int>>::setInsertUnique(int i) {
|
| + set_.insertUnique(i);
|
| +}
|
| +
|
| +template <>
|
| +void SetBenchmark<FlatSet<int>>::setEraseBack() {
|
| + set_.erase(set_.rbegin());
|
| +}
|
| +
|
| +template <>
|
| +void SetBenchmark<HashSet<int>>::setInsert(int i) {
|
| + set_.add(i);
|
| +}
|
| +
|
| +template <>
|
| +void SetBenchmark<HashSet<int>>::setErase(int i) {
|
| + set_.remove(i);
|
| +}
|
| +
|
| +TYPED_TEST_CASE(SetBenchmark, SetTypes);
|
| +
|
| +TYPED_TEST(SetBenchmark, InsertAndErasePseudoRandom) {
|
| + Benchmark::RunBenchmarkSet(
|
| + base::Bind([](SetBenchmark<TypeParam>* fixture,
|
| + size_t set_size) { fixture->setClear(); },
|
| + base::Unretained(this)),
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + const auto& permutation = Benchmark::permuted_ints_;
|
| + size_t& index = fixture->index_;
|
| +
|
| + for (size_t i = 0; i < set_size; i++) {
|
| + fixture->setInsert(permutation[index++ % permutation.size()]);
|
| + }
|
| + for (size_t i = 0; i < set_size; i++) {
|
| + fixture->setErase(permutation[index++ % permutation.size()]);
|
| + }
|
| + },
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +TYPED_TEST(SetBenchmark, InsertAndEraseOutsideIn) {
|
| + Benchmark::RunBenchmarkSet(
|
| + base::Bind([](SetBenchmark<TypeParam>* fixture,
|
| + size_t set_size) { fixture->set_.clear(); },
|
| + base::Unretained(this)),
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + for (size_t i = 1; i < set_size; i++) {
|
| + fixture->setInsertUnique(set_size - i);
|
| + fixture->setInsertUnique(set_size + i);
|
| + }
|
| + for (size_t i = set_size - 1; i > 0; i--) {
|
| + fixture->setErase(set_size - i);
|
| + fixture->setErase(set_size + i);
|
| + }
|
| + },
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +TYPED_TEST(SetBenchmark, InsertAndEraseInsideOut) {
|
| + Benchmark::RunBenchmarkSet(
|
| + base::Bind([](SetBenchmark<TypeParam>* fixture,
|
| + size_t set_size) { fixture->set_.clear(); },
|
| + base::Unretained(this)),
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + for (size_t i = 1; i < set_size; i++) {
|
| + fixture->setInsertUnique(i);
|
| + fixture->setInsertUnique(set_size + set_size - i);
|
| + }
|
| + for (size_t i = set_size - 1; i > 0; i--) {
|
| + fixture->setErase(i);
|
| + fixture->setErase(set_size + set_size - i);
|
| + }
|
| + },
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +TYPED_TEST(SetBenchmark, FixedSizeFind) {
|
| + size_t num = 0;
|
| + Benchmark::RunBenchmarkSet(
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + const auto& permutation = Benchmark::permuted_ints_;
|
| +
|
| + fixture->setClear();
|
| + for (size_t i = 0; i < set_size; i++)
|
| + fixture->setInsert(permutation[i % permutation.size()]);
|
| + },
|
| + base::Unretained(this)),
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t* num, size_t set_size) {
|
| + const auto& permutation = Benchmark::permuted_ints_;
|
| +
|
| + for (size_t i = 0; i < set_size; i++) {
|
| + if (fixture->set_.find(permutation[i % permutation.size()]) !=
|
| + fixture->set_.end()) {
|
| + (*num)++; // Prevent the compiler from optimizing stuff away.
|
| + }
|
| + }
|
| + },
|
| + base::Unretained(this), base::Unretained(&num)));
|
| +}
|
| +
|
| +template <typename SetType>
|
| +class AssociativeSetBenchmark : public SetBenchmark<SetType> {};
|
| +
|
| +TYPED_TEST_CASE(AssociativeSetBenchmark, AssociativeSetTypes);
|
| +
|
| +TYPED_TEST(AssociativeSetBenchmark, FixedSizeEraseAndInsert) {
|
| + Benchmark::RunBenchmarkSet(
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + const auto& permutation = Benchmark::permuted_ints_;
|
| + size_t& index = fixture->index_;
|
| +
|
| + fixture->setClear();
|
| + for (index = 0; index < set_size; index++) {
|
| + fixture->setInsert(permutation[index % permutation.size()]);
|
| + }
|
| + },
|
| + base::Unretained(this)),
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + const auto& permutation = Benchmark::permuted_ints_;
|
| + size_t& index = fixture->index_;
|
| + size_t& run = fixture->run_; // Necessary to ensure uniqueness.
|
| +
|
| + for (size_t i = 0; i < 10000; i++) {
|
| + fixture->setEraseFront();
|
| + fixture->setInsertUnique(
|
| + run + permutation[index++ % permutation.size()]);
|
| + }
|
| +
|
| + run += permutation.size();
|
| + },
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +TYPED_TEST(AssociativeSetBenchmark, InsertAndEraseOutsideIn) {
|
| + Benchmark::RunBenchmarkSet(
|
| + base::Bind([](SetBenchmark<TypeParam>* fixture,
|
| + size_t set_size) { fixture->set_.clear(); },
|
| + base::Unretained(this)),
|
| + base::Bind(
|
| + [](SetBenchmark<TypeParam>* fixture, size_t set_size) {
|
| + for (size_t i = 1; i < set_size; i++) {
|
| + fixture->setInsertUnique(set_size - i);
|
| + fixture->setInsertUnique(set_size + i);
|
| + }
|
| + for (size_t i = set_size - 1; i > 0; i--) {
|
| + fixture->setEraseFront();
|
| + fixture->setEraseBack();
|
| + }
|
| + },
|
| + base::Unretained(this)));
|
| + EXPECT_TRUE(this->set_.empty());
|
| +}
|
| +
|
| +} // namespace WTF
|
|
|