filesystem_benchmark.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include <benchmark/benchmark.h>
  5. #include <fstream>
  6. #include <system_error>
  7. #include "absl/random/random.h"
  8. #include "common/filesystem.h"
  9. #include "llvm/ADT/Sequence.h"
  10. #include "llvm/ADT/StringExtras.h"
  11. namespace Carbon::Filesystem {
  12. namespace {
  13. // Alternative implementation strategies to allow comparing performance.
  14. //
  15. // WHen implementing benchmarks below, we try to make them templates on this
  16. // enum and then switch in the body between different implementations. This
  17. // allows us to share the framework of each benchmark but select different
  18. // implementations for different instantiations. The different instantiations
  19. // get these enumerators in their names in the output, so we keep them short.
  20. enum BenchmarkComparables {
  21. Carbon,
  22. Std,
  23. };
  24. // Filler text.
  25. constexpr llvm::StringLiteral Text =
  26. "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
  27. "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
  28. "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
  29. "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
  30. "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
  31. "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
  32. "mollit anim id est laborum.";
  33. // Gets the filler text repeated up to a specific length.
  34. static auto GetText(int length) -> std::string {
  35. std::string content;
  36. content.reserve(length);
  37. while (static_cast<int>(content.size()) < length) {
  38. content += Text.substr(0, length - content.size());
  39. }
  40. CARBON_CHECK(static_cast<int>(content.size()) == length);
  41. return content;
  42. }
  43. // We build a collection of file paths to use across different benchmarks in
  44. // batches to avoid looking at the same file over and over again. We can even
  45. // shuffle the file orders to further avoid hiding performance cost. If there
  46. // are specific cases where we want to measure the cached / predicted speed, we
  47. // can write those benchmarks against a specific file, but most often we instead
  48. // look at the worst case scenario for wall-clock time and use cycle counters
  49. // and instruction counters to measure aspects of the best case. The exact
  50. // number here was chosen arbitrarily to not make running benchmarks excessively
  51. // slow due to the large batches.
  52. constexpr int NumFiles = 64;
  53. // A common set of context used in benchmarks below. A separate context object
  54. // works better than the benchmark fixture support in practice.
  55. //
  56. // This is a struct as there are no invariants or contracts enforced. This is
  57. // just a container of commonly useful data and commonly useful helper
  58. // functions.
  59. struct BenchContext {
  60. RemovingDir tmpdir;
  61. absl::BitGen rng;
  62. std::array<std::filesystem::path, NumFiles> file_paths;
  63. std::array<std::filesystem::path, NumFiles> missing_paths;
  64. BenchContext() : tmpdir(*MakeTmpDir()) {
  65. for (int i : llvm::seq(NumFiles)) {
  66. file_paths[i] = llvm::formatv("file_{0}", i).str();
  67. auto result = tmpdir.WriteFileFromString(file_paths[i], Text);
  68. CARBON_CHECK(result.ok(), "{0}", result.error());
  69. missing_paths[i] = llvm::formatv("missing_{0}", i).str();
  70. }
  71. ShuffleFilePaths();
  72. ShuffleMissingPaths();
  73. }
  74. auto ShuffleFilePaths() -> void {
  75. std::shuffle(file_paths.begin(), file_paths.end(), rng);
  76. }
  77. auto ShuffleMissingPaths() -> void {
  78. std::shuffle(missing_paths.begin(), missing_paths.end(), rng);
  79. }
  80. // Create a tree of files and directories starting from a `base` new directory
  81. // in our tmp directory, and containing `entries` total entries with
  82. // `entries_per_dir` in each directory. These will be a mixture of further
  83. // subdirectories and files.
  84. auto CreateTree(std::filesystem::path base, int entries, int entries_per_dir)
  85. -> void {
  86. CARBON_CHECK(entries >= 1);
  87. CARBON_CHECK(entries_per_dir >= 1);
  88. int num_subdirs = std::max<int>(entries_per_dir / 2, 1);
  89. struct DirStackEntry {
  90. Dir dir;
  91. int num_entries;
  92. int subdir_count;
  93. };
  94. llvm::SmallVector<DirStackEntry> dir_stack;
  95. auto d = tmpdir.OpenDir(base, CreationOptions::CreateNew);
  96. CARBON_CHECK(d.ok(), "{0}", d.error());
  97. dir_stack.push_back({std::move(*d), entries, 0});
  98. while (!dir_stack.empty()) {
  99. auto& [dir, num_entries, subdir_count] = dir_stack.back();
  100. // We want `num_entries` transitively in this directory, and
  101. // `entries_per_dir` directly. Spread the remaining entries across
  102. // `num_subdirs`.
  103. int entries_per_subdir = ((num_entries - entries_per_dir) / num_subdirs);
  104. CARBON_CHECK(entries_per_subdir < num_entries);
  105. // While we'll still put entries in a subdirectory, and we still need more
  106. // subdirectories in this directory, create another subdirectory, push it
  107. // on the stack, and recurse to it by continuing.
  108. if (entries_per_subdir >= entries_per_dir && subdir_count < num_subdirs) {
  109. auto name = llvm::formatv("dir_{0}", subdir_count).str();
  110. auto subdir = dir.OpenDir(name, CreationOptions::CreateNew);
  111. CARBON_CHECK(subdir.ok(), "{0}", subdir.error());
  112. ++subdir_count;
  113. // Note we have to continue after `push_back` as this will invalidate
  114. // the current references.
  115. dir_stack.push_back({std::move(*subdir), entries_per_subdir, 0});
  116. continue;
  117. }
  118. // Otherwise, we're finished with subdirectories and just need to create
  119. // direct files.
  120. int num_files = entries_per_dir - subdir_count;
  121. CARBON_CHECK(num_files >= 0);
  122. for (int i = 0; i < num_files; ++i) {
  123. auto name = llvm::formatv("file_{0}", i).str();
  124. auto f = dir.OpenWriteOnly(name, CreationOptions::CreateNew);
  125. CARBON_CHECK(f.ok(), "{0}", f.error());
  126. auto close_result = std::move(*f).Close();
  127. CARBON_CHECK(close_result.ok(), "{0}", close_result.error());
  128. }
  129. dir_stack.pop_back();
  130. }
  131. }
  132. };
  133. template <BenchmarkComparables Comp>
  134. auto BM_Access(benchmark::State& state) -> void {
  135. BenchContext context;
  136. while (state.KeepRunningBatch(NumFiles)) {
  137. for (int i : llvm::seq(NumFiles)) {
  138. if constexpr (Comp == Carbon) {
  139. auto result = context.tmpdir.Access(context.file_paths[i]);
  140. CARBON_CHECK(result.ok(), "{0}", result.error());
  141. } else if constexpr (Comp == Std) {
  142. std::error_code ec;
  143. bool exists = std::filesystem::exists(
  144. context.tmpdir.path() / context.file_paths[i], ec);
  145. CARBON_CHECK(!ec, "{0}", ec.message());
  146. CARBON_CHECK(exists);
  147. } else {
  148. static_assert(false, "Invalid benchmark comparable");
  149. }
  150. }
  151. }
  152. }
  153. BENCHMARK(BM_Access<Carbon>)->UseRealTime();
  154. BENCHMARK(BM_Access<Std>)->UseRealTime();
  155. template <BenchmarkComparables Comp>
  156. auto BM_AccessMissing(benchmark::State& state) -> void {
  157. BenchContext context;
  158. while (state.KeepRunningBatch(NumFiles)) {
  159. for (int i : llvm::seq(NumFiles)) {
  160. if constexpr (Comp == Carbon) {
  161. auto result = context.tmpdir.Access(context.missing_paths[i]);
  162. CARBON_CHECK(result.error().no_entity());
  163. } else if constexpr (Comp == Std) {
  164. std::error_code ec;
  165. auto exists = std::filesystem::exists(
  166. context.tmpdir.path() / context.missing_paths[i], ec);
  167. CARBON_CHECK(!ec, "{0}", ec.message());
  168. CARBON_CHECK(!exists);
  169. } else {
  170. static_assert(false, "Invalid benchmark comparable");
  171. }
  172. }
  173. }
  174. }
  175. BENCHMARK(BM_AccessMissing<Carbon>)->UseRealTime();
  176. BENCHMARK(BM_AccessMissing<Std>)->UseRealTime();
  177. template <BenchmarkComparables Comp>
  178. auto BM_Stat(benchmark::State& state) -> void {
  179. BenchContext context;
  180. while (state.KeepRunningBatch(NumFiles)) {
  181. for (int i : llvm::seq(NumFiles)) {
  182. if constexpr (Comp == Carbon) {
  183. auto status = context.tmpdir.Stat(context.file_paths[i]);
  184. CARBON_CHECK(status.ok(), "{0}", status.error());
  185. benchmark::DoNotOptimize(status->permissions());
  186. } else if constexpr (Comp == Std) {
  187. std::error_code ec;
  188. auto status = std::filesystem::status(
  189. context.tmpdir.path() / context.file_paths[i], ec);
  190. CARBON_CHECK(!ec, "{0}", ec.message());
  191. benchmark::DoNotOptimize(status.permissions());
  192. } else {
  193. static_assert(false, "Invalid benchmark comparable");
  194. }
  195. }
  196. }
  197. }
  198. BENCHMARK(BM_Stat<Carbon>)->UseRealTime();
  199. BENCHMARK(BM_Stat<Std>)->UseRealTime();
  200. template <BenchmarkComparables Comp>
  201. auto BM_StatMissing(benchmark::State& state) -> void {
  202. BenchContext context;
  203. while (state.KeepRunningBatch(NumFiles)) {
  204. for (int i : llvm::seq(NumFiles)) {
  205. if constexpr (Comp == Carbon) {
  206. auto status = context.tmpdir.Stat(context.missing_paths[i]);
  207. CARBON_CHECK(status.error().no_entity());
  208. } else if constexpr (Comp == Std) {
  209. std::error_code ec;
  210. auto status = std::filesystem::status(
  211. context.tmpdir.path() / context.missing_paths[i], ec);
  212. CARBON_CHECK(ec.value() == ENOENT, "{0}", ec.message());
  213. } else {
  214. static_assert(false, "Invalid benchmark comparable");
  215. }
  216. }
  217. }
  218. }
  219. BENCHMARK(BM_StatMissing<Carbon>)->UseRealTime();
  220. BENCHMARK(BM_StatMissing<Std>)->UseRealTime();
  221. template <BenchmarkComparables Comp>
  222. auto BM_OpenMissing(benchmark::State& state) -> void {
  223. BenchContext context;
  224. while (state.KeepRunningBatch(NumFiles)) {
  225. for (int i : llvm::seq(NumFiles)) {
  226. if constexpr (Comp == Carbon) {
  227. auto f = context.tmpdir.OpenReadOnly(context.missing_paths[i]);
  228. CARBON_CHECK(f.error().no_entity());
  229. } else if constexpr (Comp == Std) {
  230. std::ifstream f(context.tmpdir.path() / context.missing_paths[i]);
  231. CARBON_CHECK(!f.is_open());
  232. } else {
  233. static_assert(false, "Invalid benchmark comparable");
  234. }
  235. }
  236. }
  237. }
  238. BENCHMARK(BM_OpenMissing<Carbon>)->UseRealTime();
  239. BENCHMARK(BM_OpenMissing<Std>)->UseRealTime();
  240. template <BenchmarkComparables Comp>
  241. auto BM_OpenClose(benchmark::State& state) -> void {
  242. BenchContext context;
  243. while (state.KeepRunningBatch(NumFiles)) {
  244. for (int i : llvm::seq(NumFiles)) {
  245. if constexpr (Comp == Carbon) {
  246. auto f = context.tmpdir.OpenReadOnly(context.file_paths[i]);
  247. CARBON_CHECK(f.ok(), "{0}", f.error());
  248. auto close_result = std::move(*f).Close();
  249. CARBON_CHECK(close_result.ok(), "{0}", close_result.error());
  250. } else if constexpr (Comp == Std) {
  251. std::ifstream f(context.tmpdir.path() / context.file_paths[i]);
  252. CARBON_CHECK(f.is_open());
  253. } else {
  254. static_assert(false, "Invalid benchmark comparable");
  255. }
  256. }
  257. }
  258. }
  259. BENCHMARK(BM_OpenClose<Carbon>)->UseRealTime();
  260. BENCHMARK(BM_OpenClose<Std>)->UseRealTime();
  261. template <BenchmarkComparables Comp>
  262. auto BM_CreateRemove(benchmark::State& state) -> void {
  263. BenchContext context;
  264. while (state.KeepRunningBatch(NumFiles)) {
  265. for (int i : llvm::seq(NumFiles)) {
  266. if constexpr (Comp == Carbon) {
  267. // Create the file by opening it.
  268. auto f = context.tmpdir.OpenWriteOnly(context.missing_paths[i],
  269. CreationOptions::CreateNew);
  270. CARBON_CHECK(f.ok(), "{0}", f.error());
  271. // Close it right away.
  272. auto close_result = std::move(*f).Close();
  273. CARBON_CHECK(close_result.ok(), "{0}", close_result.error());
  274. // Remove it.
  275. auto remove_result = context.tmpdir.Unlink(context.missing_paths[i]);
  276. CARBON_CHECK(remove_result.ok(), "{0}", remove_result.error());
  277. } else if constexpr (Comp == Std) {
  278. auto path = context.tmpdir.path() / context.missing_paths[i];
  279. // Create the file by opening it.
  280. std::ofstream f(path);
  281. CARBON_CHECK(f.is_open());
  282. // Close it right away.
  283. f.close();
  284. // Remove it.
  285. std::error_code ec;
  286. std::filesystem::remove(path, ec);
  287. CARBON_CHECK(!ec, "{0}", ec.message());
  288. } else {
  289. static_assert(false, "Invalid benchmark comparable");
  290. }
  291. }
  292. }
  293. }
  294. BENCHMARK(BM_CreateRemove<Carbon>)->UseRealTime();
  295. BENCHMARK(BM_CreateRemove<Std>)->UseRealTime();
  296. template <BenchmarkComparables Comp>
  297. auto BM_Read(benchmark::State& state) -> void {
  298. BenchContext context;
  299. int length = state.range(0);
  300. std::string content = GetText(length);
  301. for (int i : llvm::seq(NumFiles)) {
  302. auto result =
  303. context.tmpdir.WriteFileFromString(context.file_paths[i], content);
  304. CARBON_CHECK(result.ok(), "{0}", result.error());
  305. }
  306. while (state.KeepRunningBatch(NumFiles)) {
  307. // Re-shuffle the order of the files for each batch to avoid exact cache
  308. // hits.
  309. state.PauseTiming();
  310. context.ShuffleFilePaths();
  311. state.ResumeTiming();
  312. for (int i : llvm::seq(NumFiles)) {
  313. if constexpr (Comp == Carbon) {
  314. auto read_result =
  315. context.tmpdir.ReadFileToString(context.file_paths[i]);
  316. CARBON_CHECK(read_result.ok(), "{0}", read_result.error());
  317. benchmark::DoNotOptimize(*read_result);
  318. } else if constexpr (Comp == Std) {
  319. std::ifstream f(context.tmpdir.path() / context.file_paths[i],
  320. std::ios::binary);
  321. CARBON_CHECK(f.is_open());
  322. // This may be a somewhat surprising implementation, but benchmarking
  323. // against several other ways of reading the file with `std::ifstream`
  324. // all have the same or worse performance.
  325. std::string read_content((std::istreambuf_iterator<char>(f)),
  326. (std::istreambuf_iterator<char>()));
  327. benchmark::DoNotOptimize(read_content);
  328. } else {
  329. static_assert(false, "Invalid benchmark comparable");
  330. }
  331. }
  332. }
  333. }
  334. BENCHMARK(BM_Read<Carbon>)->Range(4, 1024LL * 1024)->UseRealTime();
  335. BENCHMARK(BM_Read<Std>)->Range(4, 1024LL * 1024)->UseRealTime();
  336. template <BenchmarkComparables Comp>
  337. auto BM_Write(benchmark::State& state) -> void {
  338. BenchContext context;
  339. int length = state.range(0);
  340. std::string content = GetText(length);
  341. while (state.KeepRunningBatch(NumFiles)) {
  342. // Re-shuffle the order of the files for each batch to avoid exact cache
  343. // hits.
  344. state.PauseTiming();
  345. context.ShuffleFilePaths();
  346. state.ResumeTiming();
  347. for (int i : llvm::seq(NumFiles)) {
  348. if constexpr (Comp == Carbon) {
  349. auto write_result =
  350. context.tmpdir.WriteFileFromString(context.file_paths[i], content);
  351. CARBON_CHECK(write_result.ok(), "{0}", write_result.error());
  352. } else if constexpr (Comp == Std) {
  353. std::ofstream f(context.tmpdir.path() / context.file_paths[i],
  354. std::ios::binary | std::ios::trunc);
  355. CARBON_CHECK(f.is_open());
  356. f.write(content.data(), content.length());
  357. } else {
  358. static_assert(false, "Invalid benchmark comparable");
  359. }
  360. }
  361. }
  362. }
  363. BENCHMARK(BM_Write<Carbon>)->Range(4, 1024LL * 1024)->UseRealTime();
  364. BENCHMARK(BM_Write<Std>)->Range(4, 1024LL * 1024)->UseRealTime();
  365. template <BenchmarkComparables Comp>
  366. auto BM_Rmtree(benchmark::State& state) -> void {
  367. BenchContext context;
  368. int entries = state.range(0);
  369. int depth = state.range(1);
  370. // Configure our batch size based on the number of entries. Creating large
  371. // numbers of entries in the filesystem can cause problems, and is also very
  372. // slow. We don't need that much accuracy once the trees get large.
  373. int batch_size = entries <= 1024 ? 10 : entries <= (32 * 1024) ? 5 : 1;
  374. while (state.KeepRunningBatch(batch_size)) {
  375. state.PauseTiming();
  376. for (int i : llvm::seq(batch_size)) {
  377. context.CreateTree(llvm::formatv("tree_{0}", i).str(), entries, depth);
  378. }
  379. state.ResumeTiming();
  380. for (int i : llvm::seq(batch_size)) {
  381. std::string tree = llvm::formatv("tree_{0}", i).str();
  382. if constexpr (Comp == Carbon) {
  383. auto rmdir_result = context.tmpdir.Rmtree(tree);
  384. CARBON_CHECK(rmdir_result.ok(), "{0}", rmdir_result.error());
  385. } else if constexpr (Comp == Std) {
  386. std::error_code ec;
  387. std::filesystem::remove_all(context.tmpdir.path() / tree, ec);
  388. CARBON_CHECK(!ec, "{0}", ec.message());
  389. } else {
  390. static_assert(false, "Invalid benchmark comparable");
  391. }
  392. }
  393. }
  394. }
  395. BENCHMARK(BM_Rmtree<Carbon>)
  396. ->Ranges({{1, 256}, {1, 32}})
  397. ->Ranges({{2 * 1024, 256 * 1024}, {512, 1024}})
  398. ->Unit(benchmark::kMicrosecond)
  399. ->UseRealTime();
  400. BENCHMARK(BM_Rmtree<Std>)
  401. ->Ranges({{1, 256}, {1, 32}})
  402. ->Ranges({{2 * 1024, 256 * 1024}, {512, 1024}})
  403. ->Unit(benchmark::kMicrosecond)
  404. ->UseRealTime();
  405. template <BenchmarkComparables Comp>
  406. auto BM_CreateDirectories(benchmark::State& state) -> void {
  407. BenchContext context;
  408. int depth = state.range(0);
  409. int existing_depth = state.range(1);
  410. CARBON_CHECK(existing_depth <= depth);
  411. CARBON_CHECK(depth > 0);
  412. // Use a batch size of 10 to get avoid completely swamping the measurements
  413. // with overhead from creating existing directories and cleaning up.
  414. constexpr int BatchSize = 10;
  415. // Pre-build both the paths and the existing paths. Note that we use
  416. // relatively short paths here, which if anything makes the benefits of the
  417. // Carbon library smaller.
  418. llvm::SmallVector<std::string> paths;
  419. llvm::SmallVector<std::string> existing_paths;
  420. for (int i : llvm::seq(BatchSize)) {
  421. RawStringOstream path;
  422. llvm::ListSeparator sep("/");
  423. for (int j = 0; j < existing_depth; ++j) {
  424. path << sep << "exists_" << (j == 0 ? i : j);
  425. }
  426. existing_paths.push_back(path.TakeStr());
  427. path << existing_paths.back();
  428. for (int k = existing_depth; k < depth; ++k) {
  429. path << sep << "dir_" << (k == 0 ? i : k);
  430. }
  431. paths.push_back(path.TakeStr());
  432. }
  433. while (state.KeepRunningBatch(BatchSize)) {
  434. state.PauseTiming();
  435. for (int i : llvm::seq(BatchSize)) {
  436. if (existing_depth > 0) {
  437. auto result = context.tmpdir.CreateDirectories(existing_paths[i]);
  438. CARBON_CHECK(result.ok(), "{0}", result.error());
  439. }
  440. }
  441. state.ResumeTiming();
  442. for (int i : llvm::seq(BatchSize)) {
  443. if constexpr (Comp == Carbon) {
  444. auto result = context.tmpdir.CreateDirectories(paths[i]);
  445. CARBON_CHECK(result.ok(), "Failed to create '{0}': {1}", paths[i],
  446. result.error());
  447. // Create a file in the provided directory. This adds some baseline
  448. // overhead but matches the realistic use case and ensures that there
  449. // isn't some laziness that makes just creating a directory have an
  450. // unusually low cost.
  451. auto f = result->OpenWriteOnly("test", CreationOptions::CreateNew);
  452. CARBON_CHECK(f.ok(), "{0}", f.error());
  453. auto close_result = std::move(*f).Close();
  454. CARBON_CHECK(close_result.ok(), "{0}", close_result.error());
  455. } else if constexpr (Comp == Std) {
  456. std::filesystem::path path = context.tmpdir.path() / paths[i];
  457. std::error_code ec;
  458. std::filesystem::create_directories(path, ec);
  459. CARBON_CHECK(!ec, "{0}", ec.message());
  460. // Create a file in the directory, similar to above. This has a (much)
  461. // bigger effect though because the C++ APIs don't open the created
  462. // directory, and so the creation cost of it can very much be hidden
  463. // from the benchmark if we don't use it. This also lets us see the
  464. // benefit of not needing to re-walk the path to create the file.
  465. std::ofstream f(path / "test");
  466. CARBON_CHECK(f.is_open());
  467. f.close();
  468. } else {
  469. static_assert(false, "Invalid benchmark comparable");
  470. }
  471. }
  472. state.PauseTiming();
  473. for (int i : llvm::seq(BatchSize)) {
  474. auto result = context.tmpdir.Rmtree(
  475. llvm::formatv("{0}_{1}", existing_depth > 0 ? "exists" : "dir", i)
  476. .str());
  477. CARBON_CHECK(result.ok(), "{0}", result.error());
  478. }
  479. state.ResumeTiming();
  480. }
  481. }
  482. static auto CreateDirectoriesBenchArgs(benchmark::Benchmark* b) {
  483. // The first argument is the depth of directory to create. We mostly care
  484. // about reasonably small depths here. It must be >= 1 for there to be
  485. // something to benchmark. The second number is the depth of pre-existing
  486. // directories which can vary from 0 to equal to the depth to benchmark the
  487. // case of no new directory being needed.
  488. for (int i = 1; i <= 8; i *= 2) {
  489. b->Args({i, 0});
  490. for (int j = 1; j <= i; j *= 2) {
  491. b->Args({i, j});
  492. }
  493. }
  494. }
  495. BENCHMARK(BM_CreateDirectories<Carbon>)
  496. ->Apply(CreateDirectoriesBenchArgs)
  497. ->UseRealTime();
  498. BENCHMARK(BM_CreateDirectories<Std>)
  499. ->Apply(CreateDirectoriesBenchArgs)
  500. ->UseRealTime();
  501. } // namespace
  502. } // namespace Carbon::Filesystem