Add more benchmarks for literal strings.
authorSamuel Benzaquen <sbenza@google.com>
Wed, 3 Apr 2019 17:40:51 +0000 (17:40 +0000)
committerSamuel Benzaquen <sbenza@google.com>
Wed, 3 Apr 2019 17:40:51 +0000 (17:40 +0000)
Summary:
Comparing against the empty string should generate much better code that
what it does today.
We can also generate better code when comparing against literals that
are larger than the SSO space.

Reviewers: EricWF

Subscribers: christof, jdoerfert, libcxx-commits

Tags: #libc

Differential Revision: https://reviews.llvm.org/D59781

llvm-svn: 357614

libcxx/benchmarks/string.bench.cpp

index 2d2b8a9..22d84af 100644 (file)
@@ -73,12 +73,12 @@ struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
                                           "ChangeMiddle", "ChangeLast"};
 };
 
-static constexpr char kSmallStringLiteral[] = "012345678";
+static constexpr char SmallStringLiteral[] = "012345678";
 
 TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
   switch (D) {
     case DiffType::Control:
-      return kSmallStringLiteral;
+      return SmallStringLiteral;
     case DiffType::ChangeFirst:
       return "-12345678";
     case DiffType::ChangeMiddle:
@@ -88,6 +88,9 @@ TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
   }
 }
 
+static constexpr char LargeStringLiteral[] =
+    "012345678901234567890123456789012345678901234567890123456789012";
+
 TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
 #define LARGE_STRING_FIRST "123456789012345678901234567890"
 #define LARGE_STRING_SECOND "234567890123456789012345678901"
@@ -263,21 +266,26 @@ struct StringRelational {
   }
 };
 
-template <class Rel, class LHLength, class DiffType>
+template <class Rel, class LHLength, class RHLength, class DiffType>
 struct StringRelationalLiteral {
   static void run(benchmark::State& state) {
     auto Lhs = makeString(LHLength(), DiffType());
     for (auto _ : state) {
       benchmark::DoNotOptimize(Lhs);
+      constexpr const char* Literal = RHLength::value == Length::Empty
+                                          ? ""
+                                          : RHLength::value == Length::Small
+                                                ? SmallStringLiteral
+                                                : LargeStringLiteral;
       switch (Rel()) {
       case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == kSmallStringLiteral);
+        benchmark::DoNotOptimize(Lhs == Literal);
         break;
       case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < kSmallStringLiteral);
+        benchmark::DoNotOptimize(Lhs < Literal);
         break;
       case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(kSmallStringLiteral));
+        benchmark::DoNotOptimize(Lhs.compare(Literal));
         break;
       }
     }
@@ -285,17 +293,17 @@ struct StringRelationalLiteral {
 
   static bool skip() {
     // Doesn't matter how they differ if they have different size.
-    if (LHLength() != Length::Small && DiffType() != ::DiffType::Control)
+    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
       return true;
     // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge)
+    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
       return true;
     return false;
   }
 
   static std::string name() {
     return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() +
-           DiffType::name();
+           RHLength::name() + DiffType::name();
   }
 };
 
@@ -393,6 +401,22 @@ void sanityCheckGeneratedStrings() {
   }
 }
 
+// Some small codegen thunks to easily see generated code.
+bool StringEqString(const std::string& a, const std::string& b) {
+  return a == b;
+}
+bool StringEqCStr(const std::string& a, const char* b) { return a == b; }
+bool CStrEqString(const char* a, const std::string& b) { return a == b; }
+bool StringEqCStrLiteralEmpty(const std::string& a) {
+  return a == "";
+}
+bool StringEqCStrLiteralSmall(const std::string& a) {
+  return a == SmallStringLiteral;
+}
+bool StringEqCStrLiteralLarge(const std::string& a) {
+  return a == LargeStringLiteral;
+}
+
 int main(int argc, char** argv) {
   benchmark::Initialize(&argc, argv);
   if (benchmark::ReportUnrecognizedArguments(argc, argv))
@@ -408,8 +432,16 @@ int main(int argc, char** argv) {
   makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths,
                                 AllLengths, AllDiffTypes>();
   makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations,
-                                AllLengths, AllDiffTypes>();
+                                AllLengths, AllLengths, AllDiffTypes>();
   makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths,
                                 AllLengths>();
   benchmark::RunSpecifiedBenchmarks();
+
+  if (argc < 0) {
+    // ODR-use the functions to force them being generated in the binary.
+    auto functions = std::make_tuple(
+        StringEqString, StringEqCStr, CStrEqString, StringEqCStrLiteralEmpty,
+        StringEqCStrLiteralSmall, StringEqCStrLiteralLarge);
+    printf("%p", &functions);
+  }
 }