llvm-project/libcxx/test/benchmarks/filesystem.bench.cpp
Timothy Choi d282452e4c
[libc++] Avoid string reallocation in std::filesystem::path::lexically_relative (#152964)
Improves runtime by around 20 to 40%. (1.3x to 1.7x)

```
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
```

---------

Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
2025-08-20 16:58:21 +02:00

195 lines
5.9 KiB
C++

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
#include <filesystem>
#include "GenerateInput.h"
#include "benchmark/benchmark.h"
#include "test_iterators.h"
namespace fs = std::filesystem;
static const size_t TestNumInputs = 1024;
template <class GenInputs>
void BM_PathConstructString(benchmark::State& st, GenInputs gen) {
using fs::path;
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());
benchmark::DoNotOptimize(P.native().data());
}
st.SetComplexityN(st.range(0));
}
BENCHMARK_CAPTURE(BM_PathConstructString, large_string, getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
template <class GenInputs>
void BM_PathConstructCStr(benchmark::State& st, GenInputs gen) {
using fs::path;
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 <template <class...> class ItType, class GenInputs>
void BM_PathConstructIter(benchmark::State& st, GenInputs gen) {
using fs::path;
using Iter = ItType<std::string::const_iterator>;
const auto in = gen(st.range(0));
path PP;
for (auto& Part : in)
PP /= Part;
auto Start = Iter(PP.native().begin());
auto End = Iter(PP.native().end());
benchmark::DoNotOptimize(PP.native().data());
benchmark::DoNotOptimize(Start);
benchmark::DoNotOptimize(End);
while (st.KeepRunning()) {
const path P(Start, End);
benchmark::DoNotOptimize(P.native().data());
}
st.SetComplexityN(st.range(0));
}
template <class GenInputs>
void BM_PathConstructInputIter(benchmark::State& st, GenInputs gen) {
BM_PathConstructIter<cpp17_input_iterator>(st, gen);
}
template <class GenInputs>
void BM_PathConstructForwardIter(benchmark::State& st, GenInputs gen) {
BM_PathConstructIter<forward_iterator>(st, gen);
}
BENCHMARK_CAPTURE(BM_PathConstructInputIter, large_string, getRandomStringInputs)
->Range(8, TestNumInputs)
->Complexity();
BENCHMARK_CAPTURE(BM_PathConstructForwardIter, large_string, getRandomStringInputs)
->Range(8, TestNumInputs)
->Complexity();
template <class GenInputs>
void BM_PathIterateMultipleTimes(benchmark::State& st, GenInputs gen) {
using fs::path;
const auto in = gen(st.range(0));
path PP;
for (auto& Part : in)
PP /= Part;
benchmark::DoNotOptimize(PP.native().data());
while (st.KeepRunning()) {
for (auto const& E : PP) {
benchmark::DoNotOptimize(E.native().data());
}
benchmark::ClobberMemory();
}
st.SetComplexityN(st.range(0));
}
BENCHMARK_CAPTURE(BM_PathIterateMultipleTimes, iterate_elements, getRandomStringInputs)
->Range(8, TestNumInputs)
->Complexity();
template <class GenInputs>
void BM_PathIterateOnce(benchmark::State& st, GenInputs gen) {
using fs::path;
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();
for (auto const& E : P) {
benchmark::DoNotOptimize(E.native().data());
}
benchmark::ClobberMemory();
}
st.SetComplexityN(st.range(0));
}
BENCHMARK_CAPTURE(BM_PathIterateOnce, iterate_elements, getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
template <class GenInputs>
void BM_PathIterateOnceBackwards(benchmark::State& st, GenInputs gen) {
using fs::path;
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();
const auto B = P.begin();
auto I = P.end();
while (I != B) {
--I;
benchmark::DoNotOptimize(*I);
}
benchmark::DoNotOptimize(*I);
}
}
BENCHMARK_CAPTURE(BM_PathIterateOnceBackwards, iterate_elements, getRandomStringInputs)->Arg(TestNumInputs);
static fs::path getRandomPaths(int NumParts, int PathLen) {
fs::path Result;
while (NumParts--) {
std::string Part = getRandomString(PathLen);
Result /= Part;
}
return Result;
}
template <class GenInput>
void BM_LexicallyNormal(benchmark::State& st, GenInput gen, size_t PathLen) {
using fs::path;
auto In = gen(st.range(0), PathLen);
benchmark::DoNotOptimize(&In);
while (st.KeepRunning()) {
benchmark::DoNotOptimize(In.lexically_normal());
}
st.SetComplexityN(st.range(0));
}
BENCHMARK_CAPTURE(BM_LexicallyNormal, small_path, getRandomPaths, /*PathLen*/ 5)
->RangeMultiplier(2)
->Range(2, 256)
->Complexity();
BENCHMARK_CAPTURE(BM_LexicallyNormal, large_path, getRandomPaths, /*PathLen*/ 32)
->RangeMultiplier(2)
->Range(2, 256)
->Complexity();
template <class GenInput>
void BM_LexicallyRelative(benchmark::State& st, GenInput gen, size_t PathLen) {
auto BasePath = gen(st.range(0), PathLen);
auto TargetPath = gen(st.range(0), PathLen);
benchmark::DoNotOptimize(&BasePath);
benchmark::DoNotOptimize(&TargetPath);
for (auto _ : st) {
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();