Add more benchmarks for integer-to-string conversion
authorMarcus Holland-Moritz <mhx@fb.com>
Fri, 24 Jun 2016 02:04:09 +0000 (19:04 -0700)
committerFacebook Github Bot 7 <facebook-github-bot-7-bot@fb.com>
Fri, 24 Jun 2016 02:08:40 +0000 (19:08 -0700)
Summary:
In preparation for some changes to the integer-to-string conversion
code, this change adds, this adds some more benchmarks, and attempts
to update the existing ones to be less prone to the optimizations
where the compiler already knows about the constant values being
passed in.

There also were a couple of flaws in the existing benchmarks, where
instead of computing the value to be converted from a start value
and the loop counter, it rather added the (constant) total iteration
count to the value, ending up with a constant. But, worse than just
using a constant, the code that was trying to evaluate the cost of
converting a single-digit would generally end up computing the cost
of a five-digit number, which resulted in the benchmarks showing
identical figures regardless of the number of digits for small digit
counts.

The change also adds benchmarks for positive/negative signed integers
and 128-bit integers.

Reviewed By: yfeldblum

Differential Revision: D3455815

fbshipit-source-id: a20821c7d460bc05453655d0c848a0c9a47520fd

folly/test/ConvBenchmark.cpp

index 8a805cc46eca02e8bed118cd6c86427f6ba1d979..48d5ea86b2a21d7295d0fc6630822709492db5d3 100644 (file)
 using namespace std;
 using namespace folly;
 
+namespace folly {
+namespace conv_bench_detail {
+
+// Keep this data global and non-const, so the compiler cannot make
+// any assumptions about the actual values at compile time
+
+uint64_t uint64Num[] = {
+    0,
+    1ULL,
+    12ULL,
+    123ULL,
+    1234ULL,
+    12345ULL,
+    123456ULL,
+    1234567ULL,
+    12345678ULL,
+    123456789ULL,
+    1234567890ULL,
+    12345678901ULL,
+    123456789012ULL,
+    1234567890123ULL,
+    12345678901234ULL,
+    123456789012345ULL,
+    1234567890123456ULL,
+    12345678901234567ULL,
+    123456789012345678ULL,
+    1234567890123456789ULL,
+    12345678901234567890ULL,
+};
+
+int64_t int64Pos[] = {
+    0,
+    1LL,
+    12LL,
+    123LL,
+    1234LL,
+    12345LL,
+    123456LL,
+    1234567LL,
+    12345678LL,
+    123456789LL,
+    1234567890LL,
+    12345678901LL,
+    123456789012LL,
+    1234567890123LL,
+    12345678901234LL,
+    123456789012345LL,
+    1234567890123456LL,
+    12345678901234567LL,
+    123456789012345678LL,
+    1234567890123456789LL,
+};
+
+int64_t int64Neg[] = {
+    0,
+    -1LL,
+    -12LL,
+    -123LL,
+    -1234LL,
+    -12345LL,
+    -123456LL,
+    -1234567LL,
+    -12345678LL,
+    -123456789LL,
+    -1234567890LL,
+    -12345678901LL,
+    -123456789012LL,
+    -1234567890123LL,
+    -12345678901234LL,
+    -123456789012345LL,
+    -1234567890123456LL,
+    -12345678901234567LL,
+    -123456789012345678LL,
+    -1234567890123456789LL,
+};
+
+#if FOLLY_HAVE_INT128_T
+
+unsigned __int128 uint128Num[] = {
+    0,
+    static_cast<unsigned __int128>(1) << 0,
+    static_cast<unsigned __int128>(1) << 4,
+    static_cast<unsigned __int128>(1) << 7,
+    static_cast<unsigned __int128>(1) << 10,
+    static_cast<unsigned __int128>(1) << 14,
+    static_cast<unsigned __int128>(1) << 17,
+    static_cast<unsigned __int128>(1) << 20,
+    static_cast<unsigned __int128>(1) << 24,
+    static_cast<unsigned __int128>(1) << 27,
+    static_cast<unsigned __int128>(1) << 30,
+    static_cast<unsigned __int128>(1) << 34,
+    static_cast<unsigned __int128>(1) << 37,
+    static_cast<unsigned __int128>(1) << 40,
+    static_cast<unsigned __int128>(1) << 44,
+    static_cast<unsigned __int128>(1) << 47,
+    static_cast<unsigned __int128>(1) << 50,
+    static_cast<unsigned __int128>(1) << 54,
+    static_cast<unsigned __int128>(1) << 57,
+    static_cast<unsigned __int128>(1) << 60,
+    static_cast<unsigned __int128>(1) << 64,
+    static_cast<unsigned __int128>(1) << 67,
+    static_cast<unsigned __int128>(1) << 70,
+    static_cast<unsigned __int128>(1) << 74,
+    static_cast<unsigned __int128>(1) << 77,
+    static_cast<unsigned __int128>(1) << 80,
+    static_cast<unsigned __int128>(1) << 84,
+    static_cast<unsigned __int128>(1) << 87,
+    static_cast<unsigned __int128>(1) << 90,
+    static_cast<unsigned __int128>(1) << 94,
+    static_cast<unsigned __int128>(1) << 97,
+    static_cast<unsigned __int128>(1) << 100,
+    static_cast<unsigned __int128>(1) << 103,
+    static_cast<unsigned __int128>(1) << 107,
+    static_cast<unsigned __int128>(1) << 110,
+    static_cast<unsigned __int128>(1) << 113,
+    static_cast<unsigned __int128>(1) << 117,
+    static_cast<unsigned __int128>(1) << 120,
+    static_cast<unsigned __int128>(1) << 123,
+    static_cast<unsigned __int128>(1) << 127,
+};
+
+__int128 int128Pos[] = {
+    0,
+    static_cast<__int128>(1) << 0,
+    static_cast<__int128>(1) << 4,
+    static_cast<__int128>(1) << 7,
+    static_cast<__int128>(1) << 10,
+    static_cast<__int128>(1) << 14,
+    static_cast<__int128>(1) << 17,
+    static_cast<__int128>(1) << 20,
+    static_cast<__int128>(1) << 24,
+    static_cast<__int128>(1) << 27,
+    static_cast<__int128>(1) << 30,
+    static_cast<__int128>(1) << 34,
+    static_cast<__int128>(1) << 37,
+    static_cast<__int128>(1) << 40,
+    static_cast<__int128>(1) << 44,
+    static_cast<__int128>(1) << 47,
+    static_cast<__int128>(1) << 50,
+    static_cast<__int128>(1) << 54,
+    static_cast<__int128>(1) << 57,
+    static_cast<__int128>(1) << 60,
+    static_cast<__int128>(1) << 64,
+    static_cast<__int128>(1) << 67,
+    static_cast<__int128>(1) << 70,
+    static_cast<__int128>(1) << 74,
+    static_cast<__int128>(1) << 77,
+    static_cast<__int128>(1) << 80,
+    static_cast<__int128>(1) << 84,
+    static_cast<__int128>(1) << 87,
+    static_cast<__int128>(1) << 90,
+    static_cast<__int128>(1) << 94,
+    static_cast<__int128>(1) << 97,
+    static_cast<__int128>(1) << 100,
+    static_cast<__int128>(1) << 103,
+    static_cast<__int128>(1) << 107,
+    static_cast<__int128>(1) << 110,
+    static_cast<__int128>(1) << 113,
+    static_cast<__int128>(1) << 117,
+    static_cast<__int128>(1) << 120,
+    static_cast<__int128>(1) << 123,
+    static_cast<__int128>(3) << 125,
+};
+
+__int128 int128Neg[] = {
+    0,
+    -(static_cast<__int128>(1) << 0),
+    -(static_cast<__int128>(1) << 4),
+    -(static_cast<__int128>(1) << 7),
+    -(static_cast<__int128>(1) << 10),
+    -(static_cast<__int128>(1) << 14),
+    -(static_cast<__int128>(1) << 17),
+    -(static_cast<__int128>(1) << 20),
+    -(static_cast<__int128>(1) << 24),
+    -(static_cast<__int128>(1) << 27),
+    -(static_cast<__int128>(1) << 30),
+    -(static_cast<__int128>(1) << 34),
+    -(static_cast<__int128>(1) << 37),
+    -(static_cast<__int128>(1) << 40),
+    -(static_cast<__int128>(1) << 44),
+    -(static_cast<__int128>(1) << 47),
+    -(static_cast<__int128>(1) << 50),
+    -(static_cast<__int128>(1) << 54),
+    -(static_cast<__int128>(1) << 57),
+    -(static_cast<__int128>(1) << 60),
+    -(static_cast<__int128>(1) << 64),
+    -(static_cast<__int128>(1) << 67),
+    -(static_cast<__int128>(1) << 70),
+    -(static_cast<__int128>(1) << 74),
+    -(static_cast<__int128>(1) << 77),
+    -(static_cast<__int128>(1) << 80),
+    -(static_cast<__int128>(1) << 84),
+    -(static_cast<__int128>(1) << 87),
+    -(static_cast<__int128>(1) << 90),
+    -(static_cast<__int128>(1) << 94),
+    -(static_cast<__int128>(1) << 97),
+    -(static_cast<__int128>(1) << 100),
+    -(static_cast<__int128>(1) << 103),
+    -(static_cast<__int128>(1) << 107),
+    -(static_cast<__int128>(1) << 110),
+    -(static_cast<__int128>(1) << 113),
+    -(static_cast<__int128>(1) << 117),
+    -(static_cast<__int128>(1) << 120),
+    -(static_cast<__int128>(1) << 123),
+    -(static_cast<__int128>(3) << 125),
+};
+
+#endif
+}
+}
+
+using namespace folly::conv_bench_detail;
+
+namespace {
+
+template <typename T>
+void checkArrayIndex(const T& array, size_t index) {
+  assert(index < sizeof(array) / sizeof(array[0]));
+}
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Benchmarks for ASCII to int conversion
 ////////////////////////////////////////////////////////////////////////////////
@@ -145,11 +366,11 @@ unsigned u64ToAsciiTable(uint64_t value, char* dst) {
   return length;
 }
 
-void u64ToAsciiTableBM(unsigned int n, uint64_t value) {
-  // This is too fast, need to do 10 times per iteration
+void u64ToAsciiTableBM(unsigned int n, size_t index) {
+  checkArrayIndex(uint64Num, index);
   char buf[20];
   FOR_EACH_RANGE(i, 0, n) {
-    doNotOptimizeAway(u64ToAsciiTable(value + n, buf));
+    doNotOptimizeAway(u64ToAsciiTable(uint64Num[index] + (i % 8), buf));
   }
 }
 
@@ -175,52 +396,77 @@ unsigned u64ToAsciiClassic(uint64_t value, char* dst) {
   return length;
 }
 
-void u64ToAsciiClassicBM(unsigned int n, uint64_t value) {
-  // This is too fast, need to do 10 times per iteration
+void u64ToAsciiClassicBM(unsigned int n, size_t index) {
+  checkArrayIndex(uint64Num, index);
   char buf[20];
   FOR_EACH_RANGE(i, 0, n) {
-    doNotOptimizeAway(u64ToAsciiClassic(value + n, buf));
+    doNotOptimizeAway(u64ToAsciiClassic(uint64Num[index] + (i % 8), buf));
   }
 }
 
-void u64ToAsciiFollyBM(unsigned int n, uint64_t value) {
-  // This is too fast, need to do 10 times per iteration
+void u64ToAsciiFollyBM(unsigned int n, size_t index) {
+  checkArrayIndex(uint64Num, index);
   char buf[20];
   FOR_EACH_RANGE(i, 0, n) {
-    doNotOptimizeAway(uint64ToBufferUnsafe(value + n, buf));
+    doNotOptimizeAway(uint64ToBufferUnsafe(uint64Num[index] + (i % 8), buf));
   }
 }
 
 // Benchmark unsigned to string conversion
 
-void u64ToStringClibMeasure(unsigned int n, uint64_t value) {
+void u64ToStringClibMeasure(unsigned int n, size_t index) {
   // FOLLY_RANGE_CHECK_TO_STRING expands to std::to_string, except on Android
   // where std::to_string is not supported
-  FOR_EACH_RANGE(i, 0, n) { FOLLY_RANGE_CHECK_TO_STRING(value + n); }
+  checkArrayIndex(uint64Num, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(
+        FOLLY_RANGE_CHECK_TO_STRING(uint64Num[index] + (i % 8)).size());
+  }
+}
+
+void u64ToStringFollyMeasure(unsigned int n, size_t index) {
+  checkArrayIndex(uint64Num, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(to<std::string>(uint64Num[index] + (i % 8)).size());
+  }
 }
 
-void u64ToStringFollyMeasure(unsigned int n, uint64_t value) {
-  FOR_EACH_RANGE(i, 0, n) { to<std::string>(value + n); }
+// Signed
+
+void i64ToStringFollyMeasurePos(unsigned int n, size_t index) {
+  checkArrayIndex(int64Pos, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(to<std::string>(int64Pos[index] + (i % 8)).size());
+  }
+}
+
+void i64ToStringFollyMeasureNeg(unsigned int n, size_t index) {
+  checkArrayIndex(int64Neg, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(to<std::string>(int64Neg[index] - (i % 8)).size());
+  }
 }
 
 // Benchmark uitoa with string append
 
-void u2aAppendClassicBM(unsigned int n, uint64_t value) {
+void u2aAppendClassicBM(unsigned int n, size_t index) {
+  checkArrayIndex(uint64Num, index);
   string s;
   FOR_EACH_RANGE(i, 0, n) {
     // auto buf = &s.back() + 1;
     char buffer[20];
-    s.append(buffer, u64ToAsciiClassic(value, buffer));
+    s.append(buffer, u64ToAsciiClassic(uint64Num[index] + (i % 8), buffer));
     doNotOptimizeAway(s.size());
   }
 }
 
-void u2aAppendFollyBM(unsigned int n, uint64_t value) {
+void u2aAppendFollyBM(unsigned int n, size_t index) {
+  checkArrayIndex(uint64Num, index);
   string s;
   FOR_EACH_RANGE(i, 0, n) {
     // auto buf = &s.back() + 1;
     char buffer[20];
-    s.append(buffer, uint64ToBufferUnsafe(value, buffer));
+    s.append(buffer, uint64ToBufferUnsafe(uint64Num[index] + (i % 8), buffer));
     doNotOptimizeAway(s.size());
   }
 }
@@ -251,31 +497,220 @@ struct StringVariadicToBM {
   }
 };
 
-static size_t bigInt = 11424545345345;
-static size_t smallInt = 104;
-static char someString[] = "this is some nice string";
-static char otherString[] = "this is a long string, so it's not so nice";
-static char reallyShort[] = "meh";
-static std::string stdString = "std::strings are very nice";
-static float fValue = 1.2355;
-static double dValue = 345345345.435;
+namespace folly {
+namespace conv_bench_detail {
+
+// Keep this data global and non-const, so the compiler cannot make
+// any assumptions about the actual values at compile time
+
+size_t bigInt = 11424545345345;
+size_t smallInt = 104;
+char someString[] = "this is some nice string";
+char otherString[] = "this is a long string, so it's not so nice";
+char reallyShort[] = "meh";
+std::string stdString = "std::strings are very nice";
+float fValue = 1.2355;
+double dValue = 345345345.435;
+}
+}
 
 BENCHMARK(preallocateTestNoFloat, n) {
   for (size_t i = 0; i < n; ++i) {
-    auto val1 = to<std::string>(bigInt, someString, stdString, otherString);
-    auto val3 = to<std::string>(reallyShort, smallInt);
-    auto val2 = to<std::string>(bigInt, stdString);
-    auto val4 = to<std::string>(bigInt, stdString, dValue, otherString);
-    auto val5 = to<std::string>(bigInt, someString, reallyShort);
+    doNotOptimizeAway(
+        to<std::string>(bigInt, someString, stdString, otherString).size());
+    doNotOptimizeAway(to<std::string>(reallyShort, smallInt).size());
+    doNotOptimizeAway(to<std::string>(bigInt, stdString).size());
+    doNotOptimizeAway(
+        to<std::string>(bigInt, stdString, dValue, otherString).size());
+    doNotOptimizeAway(to<std::string>(bigInt, someString, reallyShort).size());
   }
 }
 
 BENCHMARK(preallocateTestFloat, n) {
   for (size_t i = 0; i < n; ++i) {
-    auto val1 = to<std::string>(stdString, ',', fValue, dValue);
-    auto val2 = to<std::string>(stdString, ',', dValue);
+    doNotOptimizeAway(to<std::string>(stdString, ',', fValue, dValue).size());
+    doNotOptimizeAway(to<std::string>(stdString, ',', dValue).size());
+  }
+}
+
+namespace folly {
+namespace conv_bench_detail {
+
+// Keep this data global and non-const, so the compiler cannot make
+// any assumptions about the actual values at compile time
+
+int8_t i8s[] = {
+    -(static_cast<int8_t>(1) << 4),
+    static_cast<int8_t>(1) << 5,
+    -(static_cast<int8_t>(1) << 6),
+};
+
+uint8_t u8s[] = {
+    static_cast<uint8_t>(1) << 4,
+    static_cast<uint8_t>(1) << 5,
+    static_cast<uint8_t>(1) << 7,
+};
+
+int16_t i16s[] = {
+    -(static_cast<int16_t>(1) << 8),
+    static_cast<int16_t>(1) << 12,
+    -(static_cast<int16_t>(1) << 14),
+};
+
+uint16_t u16s[] = {
+    static_cast<uint16_t>(1) << 8,
+    static_cast<uint16_t>(1) << 12,
+    static_cast<uint16_t>(1) << 15,
+};
+
+int32_t i32s[] = {
+    -(static_cast<int32_t>(1) << 16),
+    static_cast<int32_t>(1) << 25,
+    -(static_cast<int32_t>(1) << 30),
+};
+
+uint32_t u32s[] = {
+    static_cast<uint32_t>(1) << 16,
+    static_cast<uint32_t>(1) << 25,
+    static_cast<uint32_t>(1) << 31,
+};
+
+int64_t i64s[] = {
+    -(static_cast<int64_t>(1) << 32),
+    static_cast<int64_t>(1) << 50,
+    -(static_cast<int64_t>(1) << 62),
+};
+
+uint64_t u64s[] = {
+    static_cast<uint64_t>(1) << 32,
+    static_cast<uint64_t>(1) << 50,
+    static_cast<uint64_t>(1) << 63,
+};
+}
+}
+
+BENCHMARK(preallocateTestInt8, n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(to<std::string>(
+                          i8s[0],
+                          ',',
+                          u8s[0],
+                          ',',
+                          i8s[1],
+                          ',',
+                          u8s[1],
+                          ',',
+                          i8s[2],
+                          ',',
+                          u8s[2])
+                          .size());
+  }
+}
+
+BENCHMARK(preallocateTestInt16, n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(to<std::string>(
+                          i16s[0],
+                          ',',
+                          u16s[0],
+                          ',',
+                          i16s[1],
+                          ',',
+                          u16s[1],
+                          ',',
+                          i16s[2],
+                          ',',
+                          u16s[2])
+                          .size());
+  }
+}
+
+BENCHMARK(preallocateTestInt32, n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(to<std::string>(
+                          i32s[0],
+                          ',',
+                          u32s[0],
+                          ',',
+                          i32s[1],
+                          ',',
+                          u32s[1],
+                          ',',
+                          i32s[2],
+                          ',',
+                          u32s[2])
+                          .size());
+  }
+}
+
+BENCHMARK(preallocateTestInt64, n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(to<std::string>(
+                          i64s[0],
+                          ',',
+                          u64s[0],
+                          ',',
+                          i64s[1],
+                          ',',
+                          u64s[1],
+                          ',',
+                          i64s[2],
+                          ',',
+                          u64s[2])
+                          .size());
+  }
+}
+
+#if FOLLY_HAVE_INT128_T
+namespace {
+
+__int128 i128s[] = {
+    -(static_cast<__int128>(1) << 2),
+    static_cast<__int128>(1) << 100,
+    -(static_cast<__int128>(1) << 126),
+};
+
+unsigned __int128 u128s[] = {
+    static_cast<unsigned __int128>(1) << 2,
+    static_cast<unsigned __int128>(1) << 100,
+    static_cast<unsigned __int128>(1) << 127,
+};
+}
+
+BENCHMARK(preallocateTestInt128, n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(to<std::string>(
+                          i128s[0],
+                          ',',
+                          u128s[0],
+                          ',',
+                          i128s[1],
+                          ',',
+                          u128s[1],
+                          ',',
+                          i128s[2],
+                          ',',
+                          u128s[2])
+                          .size());
   }
 }
+
+BENCHMARK(preallocateTestNoFloatWithInt128, n) {
+  for (size_t i = 0; i < n; ++i) {
+    doNotOptimizeAway(
+        to<std::string>(bigInt, someString, stdString, otherString).size());
+    doNotOptimizeAway(
+        to<std::string>(reallyShort, u128s[0], smallInt, i128s[2]).size());
+    doNotOptimizeAway(
+        to<std::string>(bigInt, i128s[0], stdString, u128s[1]).size());
+    doNotOptimizeAway(
+        to<std::string>(bigInt, stdString, dValue, otherString).size());
+    doNotOptimizeAway(
+        to<std::string>(bigInt, u128s[2], someString, reallyShort).size());
+  }
+}
+#endif
+
 BENCHMARK_DRAW_LINE();
 
 static const StringIdenticalToBM<std::string> stringIdenticalToBM;
@@ -290,56 +725,137 @@ static const StringVariadicToBM<fbstring> fbstringVariadicToBM;
   BENCHMARK_DRAW_LINE();
 
 DEFINE_BENCHMARK_GROUP(1);
+DEFINE_BENCHMARK_GROUP(2);
+DEFINE_BENCHMARK_GROUP(3);
+DEFINE_BENCHMARK_GROUP(4);
+DEFINE_BENCHMARK_GROUP(5);
+DEFINE_BENCHMARK_GROUP(6);
+DEFINE_BENCHMARK_GROUP(7);
+DEFINE_BENCHMARK_GROUP(8);
+DEFINE_BENCHMARK_GROUP(9);
+DEFINE_BENCHMARK_GROUP(10);
+DEFINE_BENCHMARK_GROUP(11);
 DEFINE_BENCHMARK_GROUP(12);
-DEFINE_BENCHMARK_GROUP(123);
-DEFINE_BENCHMARK_GROUP(1234);
-DEFINE_BENCHMARK_GROUP(12345);
-DEFINE_BENCHMARK_GROUP(123456);
-DEFINE_BENCHMARK_GROUP(1234567);
-DEFINE_BENCHMARK_GROUP(12345678);
-DEFINE_BENCHMARK_GROUP(123456789);
-DEFINE_BENCHMARK_GROUP(1234567890);
-DEFINE_BENCHMARK_GROUP(12345678901);
-DEFINE_BENCHMARK_GROUP(123456789012);
-DEFINE_BENCHMARK_GROUP(1234567890123);
-DEFINE_BENCHMARK_GROUP(12345678901234);
-DEFINE_BENCHMARK_GROUP(123456789012345);
-DEFINE_BENCHMARK_GROUP(1234567890123456);
-DEFINE_BENCHMARK_GROUP(12345678901234567);
-DEFINE_BENCHMARK_GROUP(123456789012345678);
-DEFINE_BENCHMARK_GROUP(1234567890123456789);
-DEFINE_BENCHMARK_GROUP(12345678901234567890U);
+DEFINE_BENCHMARK_GROUP(13);
+DEFINE_BENCHMARK_GROUP(14);
+DEFINE_BENCHMARK_GROUP(15);
+DEFINE_BENCHMARK_GROUP(16);
+DEFINE_BENCHMARK_GROUP(17);
+DEFINE_BENCHMARK_GROUP(18);
+DEFINE_BENCHMARK_GROUP(19);
+DEFINE_BENCHMARK_GROUP(20);
 
 #undef DEFINE_BENCHMARK_GROUP
 
-#define DEFINE_BENCHMARK_GROUP(n)                       \
-  BENCHMARK_PARAM(u64ToStringClibMeasure, n);           \
-  BENCHMARK_RELATIVE_PARAM(u64ToStringFollyMeasure, n); \
+#define DEFINE_BENCHMARK_GROUP(n)                          \
+  BENCHMARK_PARAM(u64ToStringClibMeasure, n);              \
+  BENCHMARK_RELATIVE_PARAM(u64ToStringFollyMeasure, n);    \
+  BENCHMARK_RELATIVE_PARAM(i64ToStringFollyMeasurePos, n); \
+  BENCHMARK_RELATIVE_PARAM(i64ToStringFollyMeasureNeg, n); \
   BENCHMARK_DRAW_LINE();
 
 DEFINE_BENCHMARK_GROUP(1);
+DEFINE_BENCHMARK_GROUP(2);
+DEFINE_BENCHMARK_GROUP(3);
+DEFINE_BENCHMARK_GROUP(4);
+DEFINE_BENCHMARK_GROUP(5);
+DEFINE_BENCHMARK_GROUP(6);
+DEFINE_BENCHMARK_GROUP(7);
+DEFINE_BENCHMARK_GROUP(8);
+DEFINE_BENCHMARK_GROUP(9);
+DEFINE_BENCHMARK_GROUP(10);
+DEFINE_BENCHMARK_GROUP(11);
 DEFINE_BENCHMARK_GROUP(12);
-DEFINE_BENCHMARK_GROUP(123);
-DEFINE_BENCHMARK_GROUP(1234);
-DEFINE_BENCHMARK_GROUP(12345);
-DEFINE_BENCHMARK_GROUP(123456);
-DEFINE_BENCHMARK_GROUP(1234567);
-DEFINE_BENCHMARK_GROUP(12345678);
-DEFINE_BENCHMARK_GROUP(123456789);
-DEFINE_BENCHMARK_GROUP(1234567890);
-DEFINE_BENCHMARK_GROUP(12345678901);
-DEFINE_BENCHMARK_GROUP(123456789012);
-DEFINE_BENCHMARK_GROUP(1234567890123);
-DEFINE_BENCHMARK_GROUP(12345678901234);
-DEFINE_BENCHMARK_GROUP(123456789012345);
-DEFINE_BENCHMARK_GROUP(1234567890123456);
-DEFINE_BENCHMARK_GROUP(12345678901234567);
-DEFINE_BENCHMARK_GROUP(123456789012345678);
-DEFINE_BENCHMARK_GROUP(1234567890123456789);
-DEFINE_BENCHMARK_GROUP(12345678901234567890U);
+DEFINE_BENCHMARK_GROUP(13);
+DEFINE_BENCHMARK_GROUP(14);
+DEFINE_BENCHMARK_GROUP(15);
+DEFINE_BENCHMARK_GROUP(16);
+DEFINE_BENCHMARK_GROUP(17);
+DEFINE_BENCHMARK_GROUP(18);
+DEFINE_BENCHMARK_GROUP(19);
+
+// Only for u64
+BENCHMARK_PARAM(u64ToStringClibMeasure, 20);
+BENCHMARK_RELATIVE_PARAM(u64ToStringFollyMeasure, 20);
+BENCHMARK_DRAW_LINE();
 
 #undef DEFINE_BENCHMARK_GROUP
 
+#if FOLLY_HAVE_INT128_T
+
+void u128ToStringFollyMeasure(unsigned int n, size_t index) {
+  checkArrayIndex(uint128Num, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(to<std::string>(uint128Num[index] + (i % 8)).size());
+  }
+}
+
+void i128ToStringFollyMeasurePos(unsigned int n, size_t index) {
+  checkArrayIndex(int128Pos, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(to<std::string>(int128Pos[index] + (i % 8)).size());
+  }
+}
+
+void i128ToStringFollyMeasureNeg(unsigned int n, size_t index) {
+  checkArrayIndex(int128Neg, index);
+  FOR_EACH_RANGE (i, 0, n) {
+    doNotOptimizeAway(to<std::string>(int128Neg[index] + (i % 8)).size());
+  }
+}
+
+#define DEFINE_BENCHMARK_GROUP(n)                           \
+  BENCHMARK_PARAM(u128ToStringFollyMeasure, n);             \
+  BENCHMARK_RELATIVE_PARAM(i128ToStringFollyMeasurePos, n); \
+  BENCHMARK_RELATIVE_PARAM(i128ToStringFollyMeasureNeg, n); \
+  BENCHMARK_DRAW_LINE();
+
+DEFINE_BENCHMARK_GROUP(1);
+DEFINE_BENCHMARK_GROUP(2);
+DEFINE_BENCHMARK_GROUP(3);
+DEFINE_BENCHMARK_GROUP(4);
+DEFINE_BENCHMARK_GROUP(5);
+DEFINE_BENCHMARK_GROUP(6);
+DEFINE_BENCHMARK_GROUP(7);
+DEFINE_BENCHMARK_GROUP(8);
+DEFINE_BENCHMARK_GROUP(9);
+DEFINE_BENCHMARK_GROUP(10);
+DEFINE_BENCHMARK_GROUP(11);
+DEFINE_BENCHMARK_GROUP(12);
+DEFINE_BENCHMARK_GROUP(13);
+DEFINE_BENCHMARK_GROUP(14);
+DEFINE_BENCHMARK_GROUP(15);
+DEFINE_BENCHMARK_GROUP(16);
+DEFINE_BENCHMARK_GROUP(17);
+DEFINE_BENCHMARK_GROUP(18);
+DEFINE_BENCHMARK_GROUP(19);
+DEFINE_BENCHMARK_GROUP(20);
+DEFINE_BENCHMARK_GROUP(21);
+DEFINE_BENCHMARK_GROUP(22);
+DEFINE_BENCHMARK_GROUP(23);
+DEFINE_BENCHMARK_GROUP(24);
+DEFINE_BENCHMARK_GROUP(25);
+DEFINE_BENCHMARK_GROUP(26);
+DEFINE_BENCHMARK_GROUP(27);
+DEFINE_BENCHMARK_GROUP(28);
+DEFINE_BENCHMARK_GROUP(29);
+DEFINE_BENCHMARK_GROUP(30);
+DEFINE_BENCHMARK_GROUP(31);
+DEFINE_BENCHMARK_GROUP(32);
+DEFINE_BENCHMARK_GROUP(33);
+DEFINE_BENCHMARK_GROUP(34);
+DEFINE_BENCHMARK_GROUP(35);
+DEFINE_BENCHMARK_GROUP(36);
+DEFINE_BENCHMARK_GROUP(37);
+DEFINE_BENCHMARK_GROUP(38);
+DEFINE_BENCHMARK_GROUP(39);
+
+BENCHMARK_DRAW_LINE();
+
+#undef DEFINE_BENCHMARK_GROUP
+
+#endif
+
 #define DEFINE_BENCHMARK_GROUP(n)                      \
   BENCHMARK_PARAM(clibAtoiMeasure, n);                 \
   BENCHMARK_RELATIVE_PARAM(lexicalCastMeasure, n);     \
@@ -433,7 +949,11 @@ inline size_t arithToArithClassic(const U* in, uint32_t numItems) {
 
 } // namespace
 
-namespace conv {
+namespace folly {
+namespace conv_bench_detail {
+
+// Keep this data global and non-const, so the compiler cannot make
+// any assumptions about the actual values at compile time
 
 std::array<int, 4> int2ScharGood{{-128, 127, 0, -50}};
 std::array<int, 4> int2ScharBad{{-129, 128, 255, 10000}};
@@ -455,8 +975,7 @@ std::array<double, 4> double2FloatBad{{1e100, 1e101, 1e102, 1e103}};
 std::array<double, 4> double2IntGood{{1.0, 10.0, 100.0, 1000.0}};
 std::array<double, 4> double2IntBad{{1e100, 1.25, 2.5, 100.00001}};
 }
-
-using namespace conv;
+}
 
 #define STRING_TO_TYPE_BENCHMARK(type, name, pass, fail) \
   BENCHMARK(stringTo##name##Classic, n) {                \