[libcxx] Speeding up partition_point/lower_bound/upper_bound

This is a re-application of r345525, which had been reverted by fear of
a regression.

Reviewed as https://reviews.llvm.org/D53994.
Thanks to Denis Yaroshevskiy for the patch.

llvm-svn: 349358
diff --git a/libcxx/benchmarks/algorithms.partition_point.bench.cpp b/libcxx/benchmarks/algorithms.partition_point.bench.cpp
new file mode 100644
index 0000000..00a3bb2
--- /dev/null
+++ b/libcxx/benchmarks/algorithms.partition_point.bench.cpp
@@ -0,0 +1,124 @@
+#include <array>
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+#include "CartesianBenchmarks.hpp"
+#include "GenerateInput.hpp"
+
+namespace {
+
+template <typename I, typename N>
+std::array<I, 10> every_10th_percentile_N(I first, N n) {
+  N step = n / 10;
+  std::array<I, 10> res;
+
+  for (size_t i = 0; i < 10; ++i) {
+    res[i] = first;
+    std::advance(first, step);
+  }
+
+  return res;
+}
+
+template <class IntT>
+struct TestIntBase {
+  static std::vector<IntT> generateInput(size_t size) {
+    std::vector<IntT> Res(size);
+    std::generate(Res.begin(), Res.end(),
+                  [] { return getRandomInteger<IntT>(); });
+    return Res;
+  }
+};
+
+struct TestInt32 : TestIntBase<std::int32_t> {
+  static constexpr const char* Name = "TestInt32";
+};
+
+struct TestInt64 : TestIntBase<std::int64_t> {
+  static constexpr const char* Name = "TestInt64";
+};
+
+struct TestUint32 : TestIntBase<std::uint32_t> {
+  static constexpr const char* Name = "TestUint32";
+};
+
+struct TestMediumString {
+  static constexpr const char* Name = "TestMediumString";
+  static constexpr size_t StringSize = 32;
+
+  static std::vector<std::string> generateInput(size_t size) {
+    std::vector<std::string> Res(size);
+    std::generate(Res.begin(), Res.end(), [] { return getRandomString(StringSize); });
+    return Res;
+  }
+};
+
+using AllTestTypes = std::tuple<TestInt32, TestInt64, TestUint32, TestMediumString>;
+
+struct LowerBoundAlg {
+  template <class I, class V>
+  I operator()(I first, I last, const V& value) const {
+    return std::lower_bound(first, last, value);
+  }
+
+  static constexpr const char* Name = "LowerBoundAlg";
+};
+
+struct UpperBoundAlg {
+  template <class I, class V>
+  I operator()(I first, I last, const V& value) const {
+    return std::upper_bound(first, last, value);
+  }
+
+  static constexpr const char* Name = "UpperBoundAlg";
+};
+
+struct EqualRangeAlg {
+  template <class I, class V>
+  std::pair<I, I> operator()(I first, I last, const V& value) const {
+    return std::equal_range(first, last, value);
+  }
+
+  static constexpr const char* Name = "EqualRangeAlg";
+};
+
+using AllAlgs = std::tuple<LowerBoundAlg, UpperBoundAlg, EqualRangeAlg>;
+
+template <class Alg, class TestType>
+struct PartitionPointBench {
+  size_t Quantity;
+
+  std::string name() const {
+    return std::string("PartitionPointBench_") + Alg::Name + "_" +
+           TestType::Name + '/' + std::to_string(Quantity);
+  }
+
+  void run(benchmark::State& state) const {
+    auto Data = TestType::generateInput(Quantity);
+    std::sort(Data.begin(), Data.end());
+    auto Every10Percentile = every_10th_percentile_N(Data.begin(), Data.size());
+
+    for (auto _ : state) {
+      for (auto Test : Every10Percentile)
+        benchmark::DoNotOptimize(Alg{}(Data.begin(), Data.end(), *Test));
+    }
+  }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+  benchmark::Initialize(&argc, argv);
+  if (benchmark::ReportUnrecognizedArguments(argc, argv))
+    return 1;
+
+  const std::vector<size_t> Quantities = {1 << 8, 1 << 10, 1 << 20};
+  makeCartesianProductBenchmark<PartitionPointBench, AllAlgs, AllTestTypes>(
+      Quantities);
+  benchmark::RunSpecifiedBenchmarks();
+}