[libcxx-commits] [libcxx] [libc++] Avoid string reallocation in `std::filesystem::path::lexically_relative` (PR #152964)

Timothy Choi via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 11 01:12:13 PDT 2025


https://github.com/tinnamchoi updated https://github.com/llvm/llvm-project/pull/152964

>From 38c17f320bcba4d58aa03f7ca4722ee54ac6b701 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Mon, 11 Aug 2025 12:59:06 +0800
Subject: [PATCH 1/4] [libc++] Add benchmarks for
 `std::filesystem::path::lexically_relative`

---
 libcxx/test/benchmarks/filesystem.bench.cpp | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/libcxx/test/benchmarks/filesystem.bench.cpp b/libcxx/test/benchmarks/filesystem.bench.cpp
index dc6b0ac537f7e..f2e596238a922 100644
--- a/libcxx/test/benchmarks/filesystem.bench.cpp
+++ b/libcxx/test/benchmarks/filesystem.bench.cpp
@@ -171,4 +171,25 @@ BENCHMARK_CAPTURE(BM_LexicallyNormal, large_path, getRandomPaths, /*PathLen*/ 32
     ->Range(2, 256)
     ->Complexity();
 
+template <class GenInput>
+void BM_LexicallyRelative(benchmark::State& st, GenInput gen, size_t PathLen) {
+  using fs::path;
+  auto BasePath = gen(st.range(0), PathLen);
+  auto TargetPath = gen(st.range(0), PathLen);
+  benchmark::DoNotOptimize(&BasePath);
+  benchmark::DoNotOptimize(&TargetPath);
+  while (st.KeepRunning()) {
+    benchmark::DoNotOptimize(TargetPath.lexically_relative(BasePath));
+  }
+  st.SetComplexityN(st.range(0));
+}
+BENCHMARK_CAPTURE(BM_LexicallyRelative, small_path, getRandomPaths, /*PathLen*/ 5)
+    ->RangeMultiplier(2)
+    ->Range(2, 256)
+    ->Complexity();
+BENCHMARK_CAPTURE(BM_LexicallyRelative, large_path, getRandomPaths, /*PathLen*/ 32)
+    ->RangeMultiplier(2)
+    ->Range(2, 256)
+    ->Complexity();
+
 BENCHMARK_MAIN();

>From bb563429d91c4255ee7324ee44b66abe1e8b23bd Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Mon, 11 Aug 2025 12:52:31 +0800
Subject: [PATCH 2/4] [libc++] Avoid string reallocation in
 `std::filesystem::path::lexically_relative`

```
Benchmark                                                           Time             CPU      Time Old      Time New       CPU Old       CPU New
------------------------------------------------------------------------------------------------------------------------------------------------
BM_LexicallyRelative/small_path/2                                -0.2111         -0.2082           229           181           228           180
BM_LexicallyRelative/small_path/4                                -0.2579         -0.2550           455           338           452           337
BM_LexicallyRelative/small_path/8                                -0.2643         -0.2616           844           621           838           619
BM_LexicallyRelative/small_path/16                               -0.2582         -0.2556          1562          1158          1551          1155
BM_LexicallyRelative/small_path/32                               -0.2518         -0.2496          3023          2262          3004          2254
BM_LexicallyRelative/small_path/64                               -0.2806         -0.2775          6344          4564          6295          4549
BM_LexicallyRelative/small_path/128                              -0.2165         -0.2137         11762          9216         11683          9186
BM_LexicallyRelative/small_path/256                              -0.2672         -0.2645         24499         17953         24324         17891
BM_LexicallyRelative/large_path/2                                -0.3268         -0.3236           426           287           422           285
BM_LexicallyRelative/large_path/4                                -0.3274         -0.3248           734           494           729           492
BM_LexicallyRelative/large_path/8                                -0.3586         -0.3560          1409           904          1399           901
BM_LexicallyRelative/large_path/16                               -0.3978         -0.3951          2764          1665          2743          1659
BM_LexicallyRelative/large_path/32                               -0.3934         -0.3908          5323          3229          5283          3218
BM_LexicallyRelative/large_path/64                               -0.3629         -0.3605         10340          6587         10265          6564
BM_LexicallyRelative/large_path/128                              -0.3450         -0.3423         19379         12694         19233         12649
BM_LexicallyRelative/large_path/256                              -0.3097         -0.3054         36293         25052         35943         24965
```
---
 libcxx/src/filesystem/path.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index 9f7dc54fdf156..624cf785577eb 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -292,7 +292,9 @@ path path::lexically_relative(const path& base) const {
   // return a path constructed with 'n' dot-dot elements, followed by the
   // elements of '*this' after the mismatch.
   path Result;
-  // FIXME: Reserve enough room in Result that it won't have to re-allocate.
+  constexpr size_t ElemSize = 2; // ".."
+  constexpr size_t SeparatorSize = 1; // separator is always a single char
+  Result.__reserve(ElemCount * (ElemSize + SeparatorSize) + SeparatorSize + PP.Path.size());
   while (ElemCount--)
     Result /= PATHSTR("..");
   for (; PP; ++PP)

>From aeeccdacf116034e87ab32c9ca154e0f7764a1b7 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Mon, 11 Aug 2025 15:47:05 +0800
Subject: [PATCH 3/4] [libc++] Fix style

Forgot to run `clang-format` before.
---
 libcxx/src/filesystem/path.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index 624cf785577eb..400b6e8988569 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -292,7 +292,7 @@ path path::lexically_relative(const path& base) const {
   // return a path constructed with 'n' dot-dot elements, followed by the
   // elements of '*this' after the mismatch.
   path Result;
-  constexpr size_t ElemSize = 2; // ".."
+  constexpr size_t ElemSize      = 2; // ".."
   constexpr size_t SeparatorSize = 1; // separator is always a single char
   Result.__reserve(ElemCount * (ElemSize + SeparatorSize) + SeparatorSize + PP.Path.size());
   while (ElemCount--)

>From dfd2f63c5eb0a4b9368afafcd8b4935efbb44fc0 Mon Sep 17 00:00:00 2001
From: tinnamchoi <tinnam.choi at gmail.com>
Date: Mon, 11 Aug 2025 16:10:24 +0800
Subject: [PATCH 4/4] [libc++] Address PR feedback

---
 libcxx/test/benchmarks/filesystem.bench.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/benchmarks/filesystem.bench.cpp b/libcxx/test/benchmarks/filesystem.bench.cpp
index f2e596238a922..9c20bc47b480d 100644
--- a/libcxx/test/benchmarks/filesystem.bench.cpp
+++ b/libcxx/test/benchmarks/filesystem.bench.cpp
@@ -173,12 +173,11 @@ BENCHMARK_CAPTURE(BM_LexicallyNormal, large_path, getRandomPaths, /*PathLen*/ 32
 
 template <class GenInput>
 void BM_LexicallyRelative(benchmark::State& st, GenInput gen, size_t PathLen) {
-  using fs::path;
   auto BasePath = gen(st.range(0), PathLen);
   auto TargetPath = gen(st.range(0), PathLen);
   benchmark::DoNotOptimize(&BasePath);
   benchmark::DoNotOptimize(&TargetPath);
-  while (st.KeepRunning()) {
+  for (auto _ : st) {
     benchmark::DoNotOptimize(TargetPath.lexically_relative(BasePath));
   }
   st.SetComplexityN(st.range(0));



More information about the libcxx-commits mailing list