Improve performance of constructing filesystem::path from strings.
authorEric Fiselier <eric@efcs.ca>
Sun, 30 Oct 2016 23:53:50 +0000 (23:53 +0000)
committerEric Fiselier <eric@efcs.ca>
Sun, 30 Oct 2016 23:53:50 +0000 (23:53 +0000)
This patch fixes a performance bug when constructing or appending to a path
from a string or c-string. Previously we called 'push_back' to append every
single character. This caused multiple re-allocation and copies when at most
one reallocation is necessary. The new behavior is to simply call
`string::append` so it can correctly handle reallocation.

For large strings this change is a ~4x improvement. This also makes our path
faster to construct than libstdc++'s.

llvm-svn: 285530

libcxx/benchmarks/filesystem.bench.cpp
libcxx/include/experimental/filesystem
libcxx/src/experimental/filesystem/path.cpp

index 8dacbe6644e615ea83be5ef0e4167966c03083ae..2cea3f69c6731b103795056716a3b0cec1035c6a 100644 (file)
@@ -21,10 +21,26 @@ void BM_PathConstructString(benchmark::State &st, GenInputs gen) {
     benchmark::DoNotOptimize(P.native().data());
   }
 }
-BENCHMARK_CAPTURE(BM_PathConstructString, iterate_elements,
+BENCHMARK_CAPTURE(BM_PathConstructString, large_string,
   getRandomStringInputs)->Arg(TestNumInputs);
 
 
+template <class GenInputs>
+void BM_PathConstructCStr(benchmark::State &st, GenInputs gen) {
+  using namespace fs;
+  const auto in = gen(st.range(0));
+  path PP;
+  for (auto& Part : in)
+    PP /= Part;
+  benchmark::DoNotOptimize(PP.native().data());
+  while (st.KeepRunning()) {
+    const path P(PP.native().c_str());
+    benchmark::DoNotOptimize(P.native().data());
+  }
+}
+BENCHMARK_CAPTURE(BM_PathConstructCStr, large_string,
+  getRandomStringInputs)->Arg(TestNumInputs);
+
 template <class GenInputs>
 void BM_PathIterateMultipleTimes(benchmark::State &st, GenInputs gen) {
   using namespace fs;
@@ -85,6 +101,4 @@ void BM_PathIterateOnceBackwards(benchmark::State &st, GenInputs gen) {
 BENCHMARK_CAPTURE(BM_PathIterateOnceBackwards, iterate_elements,
   getRandomStringInputs)->Arg(TestNumInputs);
 
-
-
 BENCHMARK_MAIN()
index 1a8dee95c8bb665f237d14510d271dee9dd4508c..c674a03695f3da6b5b4bc1b677bf51776e2313d3 100644 (file)
@@ -623,10 +623,10 @@ struct _PathCVT {
 
 template <>
 struct _PathCVT<char> {
+
     template <class _Iter>
     static void __append_range(string& __dest, _Iter __b, _Iter __e) {
-        for (; __b != __e; ++__b)
-            __dest.push_back(*__b);
+        __dest.append(__b, __e);
     }
 
     template <class _Iter>
@@ -640,7 +640,8 @@ struct _PathCVT<char> {
     static void __append_source(string& __dest, _Source const& __s)
     {
         using _Traits = __is_pathable<_Source>;
-        __append_range(__dest, _Traits::__range_begin(__s), _Traits::__range_end(__s));
+        __append_range(__dest, _Traits::__range_begin(__s),
+                               _Traits::__range_end(__s));
     }
 };
 
index be970a837d7d4a596267e390f3f93b394fbba8ba..546f3c46e04f9de5a79ebd0a1a89c574d14b6aec 100644 (file)
@@ -42,8 +42,7 @@ private:
 public:
   PathParser(string_view_t P, string_view_t E, unsigned char S)
       : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
-    assert(S != 0);
-    assert(S != PS_BeforeBegin);
+    // S cannot be '0' or PS_BeforeBegin.
   }
 
   static PathParser CreateBegin(string_view_t P) noexcept {
@@ -94,7 +93,6 @@ public:
 
     case PS_InFilenames: {
       PosPtr SepEnd = consumeSeparator(Start, End);
-      assert(SepEnd);
       if (SepEnd != End) {
         PosPtr TkEnd = consumeName(SepEnd, End);
         if (TkEnd)
@@ -131,7 +129,6 @@ public:
                          SepEnd + 1, RStart + 1);
       } else {
         PosPtr TkStart = consumeName(RStart, REnd);
-        assert(TkStart);
         if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd)
           return makeState(PS_InRootName, Path.data(), RStart + 1);
         else
@@ -192,14 +189,10 @@ public:
 
 private:
   void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
-    assert(NewState != PS_BeforeBegin && NewState != PS_AtEnd);
     State = NewState;
-    assert(Start < End);
-    assert(Start >= &Path.front() && End <= &Path.back() + 1);
     RawEntry = string_view_t(Start, End - Start);
   }
   void makeState(ParserState NewState) noexcept {
-    assert(NewState == PS_BeforeBegin || NewState == PS_AtEnd);
     State = NewState;
     RawEntry = {};
   }