Specialize string to identical string conversion
authorZejun Wu <watashi@fb.com>
Mon, 9 Jun 2014 17:16:31 +0000 (10:16 -0700)
committerAnton Likhtarov <alikhtarov@fb.com>
Mon, 9 Jun 2014 22:36:02 +0000 (15:36 -0700)
Summary:
Avoid copying underlying char array in to<string>(const string&) and
to<fbstring>(const fbstring&).

Test Plan: fbconfig -r $redteamisthebestteam/$nekomikoreimu && fbmake

Reviewed By: ldbrandy@fb.com

Subscribers: jonp, folly@lists

FB internal diff: D1368183

Tasks: 4263125

folly/Conv.h
folly/test/ConvTest.cpp

index 9ecc43c8e4940c3e1659a3c7d84f0b6091691b36..120eda96698e9b3869ae46c8f72125d4c273cfa8 100644 (file)
@@ -526,24 +526,56 @@ toAppendDelim(const Delimiter& delim, const T& v, const Ts&... vs) {
   toAppendDelim(delim, vs...);
 }
 
+/**
+ * to<SomeString>(SomeString str) returns itself. As both std::string and
+ * folly::fbstring use Copy-on-Write, it's much more efficient by
+ * avoiding copying the underlying char array.
+ */
+template <class Tgt, class Src>
+typename std::enable_if<
+  IsSomeString<Tgt>::value && std::is_same<Tgt, Src>::value,
+  Tgt>::type
+to(const Src & value) {
+  return value;
+}
+
 /**
  * to<SomeString>(v1, v2, ...) uses toAppend() (see below) as back-end
  * for all types.
  */
 template <class Tgt, class... Ts>
-typename std::enable_if<IsSomeString<Tgt>::value, Tgt>::type
+typename std::enable_if<
+  IsSomeString<Tgt>::value && (
+    sizeof...(Ts) != 1 ||
+    !std::is_same<Tgt, typename detail::last_element<Ts...>::type>::value),
+  Tgt>::type
 to(const Ts&... vs) {
   Tgt result;
   toAppend(vs..., &result);
   return result;
 }
 
+/**
+ * toDelim<SomeString>(SomeString str) returns itself.
+ */
+template <class Tgt, class Delim, class Src>
+typename std::enable_if<
+  IsSomeString<Tgt>::value && std::is_same<Tgt, Src>::value,
+  Tgt>::type
+toDelim(const Delim& delim, const Src & value) {
+  return value;
+}
+
 /**
  * toDelim<SomeString>(delim, v1, v2, ...) uses toAppendDelim() as
  * back-end for all types.
  */
 template <class Tgt, class Delim, class... Ts>
-typename std::enable_if<IsSomeString<Tgt>::value, Tgt>::type
+typename std::enable_if<
+  IsSomeString<Tgt>::value && (
+    sizeof...(Ts) != 1 ||
+    !std::is_same<Tgt, typename detail::last_element<Ts...>::type>::value),
+  Tgt>::type
 toDelim(const Delim& delim, const Ts&... vs) {
   Tgt result;
   toAppendDelim(delim, vs..., &result);
index 537b4f259fe3bf00ad80c4c91a9e80c6e1029a1c..1b6214dbc062d57cf77199e8be57c1be2b174ac4 100644 (file)
@@ -389,6 +389,14 @@ TEST(Conv, BadStringToIntegral) {
   }
 }
 
+template <class String>
+void testIdenticalTo() {
+  String s("Yukkuri shiteitte ne!!!");
+
+  String result = to<String>(s);
+  EXPECT_EQ(result, s);
+}
+
 template <class String>
 void testVariadicTo() {
   String s;
@@ -403,6 +411,17 @@ void testVariadicTo() {
   EXPECT_EQ(s, "Lorem ipsum 1234 dolor amet 567.89.");
 }
 
+template <class String>
+void testIdenticalToDelim() {
+  String s("Yukkuri shiteitte ne!!!");
+
+  String charDelim = toDelim<String>('$', s);
+  EXPECT_EQ(charDelim, s);
+
+  String strDelim = toDelim<String>(String(">_<"), s);
+  EXPECT_EQ(strDelim, s);
+}
+
 template <class String>
 void testVariadicToDelim() {
   String s;
@@ -427,11 +446,15 @@ TEST(Conv, NullString) {
 }
 
 TEST(Conv, VariadicTo) {
+  testIdenticalTo<string>();
+  testIdenticalTo<fbstring>();
   testVariadicTo<string>();
   testVariadicTo<fbstring>();
 }
 
 TEST(Conv, VariadicToDelim) {
+  testIdenticalToDelim<string>();
+  testIdenticalToDelim<fbstring>();
   testVariadicToDelim<string>();
   testVariadicToDelim<fbstring>();
 }
@@ -911,6 +934,35 @@ void u2aAppendFollyBM(unsigned int n, uint64_t value) {
   }
 }
 
+template <class String>
+struct StringIdenticalToBM {
+  void operator()(unsigned int n, size_t len) const {
+    String s;
+    BENCHMARK_SUSPEND { s.append(len, '0'); }
+    FOR_EACH_RANGE (i, 0, n) {
+      String result = to<String>(s);
+      doNotOptimizeAway(result.size());
+    }
+  }
+};
+
+template <class String>
+struct StringVariadicToBM {
+  void operator()(unsigned int n, size_t len) const {
+    String s;
+    BENCHMARK_SUSPEND { s.append(len, '0'); }
+    FOR_EACH_RANGE (i, 0, n) {
+      String result = to<String>(s, nullptr);
+      doNotOptimizeAway(result.size());
+    }
+  }
+};
+
+static const StringIdenticalToBM<std::string> stringIdenticalToBM;
+static const StringVariadicToBM<std::string> stringVariadicToBM;
+static const StringIdenticalToBM<fbstring> fbstringIdenticalToBM;
+static const StringVariadicToBM<fbstring> fbstringVariadicToBM;
+
 #define DEFINE_BENCHMARK_GROUP(n)                       \
   BENCHMARK_PARAM(u64ToAsciiClassicBM, n);              \
   BENCHMARK_RELATIVE_PARAM(u64ToAsciiTableBM, n);       \
@@ -969,6 +1021,20 @@ DEFINE_BENCHMARK_GROUP(19);
 
 #undef DEFINE_BENCHMARK_GROUP
 
+#define DEFINE_BENCHMARK_GROUP(T, n)                    \
+  BENCHMARK_PARAM(T ## VariadicToBM, n);                \
+  BENCHMARK_RELATIVE_PARAM(T ## IdenticalToBM, n);      \
+  BENCHMARK_DRAW_LINE();
+
+DEFINE_BENCHMARK_GROUP(string, 32);
+DEFINE_BENCHMARK_GROUP(string, 1024);
+DEFINE_BENCHMARK_GROUP(string, 32768);
+DEFINE_BENCHMARK_GROUP(fbstring, 32);
+DEFINE_BENCHMARK_GROUP(fbstring, 1024);
+DEFINE_BENCHMARK_GROUP(fbstring, 32768);
+
+#undef DEFINE_BENCHMARK_GROUP
+
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
   google::ParseCommandLineFlags(&argc, &argv, true);