//===- writeToOutputInParallel.cpp - Parallel file writing benchmark ------===// // // 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 // //===----------------------------------------------------------------------===// #include "benchmark/benchmark.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; // Benchmark parallel file writing using writeToOutput. This simulates scenarios // where multiple threads are writing files concurrently, which is common in // parallel compilation scenarios. The goal of this benchmark is to ensure that // LLVM's global signal handling state updates aren't too expensive. static void BM_WriteToOutputInParallel(benchmark::State &State) { const int NumThreads = State.range(0); const int FilesPerThread = State.range(1); const int BytesPerFile = 40 * 1024; // ~40KB per file for (auto _ : State) { // Create one top-level unique directory SmallString<128> TopLevelDir; if (sys::fs::createUniqueDirectory("writeToOutputBM", TopLevelDir)) { State.SkipWithError("Failed to create temporary directory"); return; } auto Cleanup = llvm::scope_exit([&]() { sys::fs::remove_directories(TopLevelDir); }); // Create subdirectories for each thread within the top-level directory std::vector> ThreadDirs; for (int I = 0; I < NumThreads; ++I) { SmallString<128> ThreadDir(TopLevelDir); sys::path::append(ThreadDir, "thread_" + std::to_string(I)); if (sys::fs::create_directory(ThreadDir)) { State.SkipWithError("Failed to create thread directory"); return; } ThreadDirs.push_back(ThreadDir); } // Launch threads, each writing multiple files std::vector Threads; for (int ThreadIdx = 0; ThreadIdx < NumThreads; ++ThreadIdx) { Threads.emplace_back([&, ThreadIdx]() { const auto &ThreadDir = ThreadDirs[ThreadIdx]; for (int FileIdx = 0; FileIdx < FilesPerThread; ++FileIdx) { SmallString<128> Path(ThreadDir); sys::path::append(Path, "file_" + std::to_string(FileIdx) + ".bin"); Error E = writeToOutput(Path, [=](raw_ostream &Out) -> Error { // Write 32-bit integers up to BytesPerFile const int NumInts = BytesPerFile / sizeof(int32_t); for (int32_t I = 0; I < NumInts; ++I) { Out.write(reinterpret_cast(&I), sizeof(I)); } return Error::success(); }); if (E) { State.SkipWithError("Failed to create outputfile " + Path.str().str()); return; } } }); } // Wait for all threads to complete for (auto &Thread : Threads) { Thread.join(); } // Cleanup happens automatically via scope_exit } // Report throughput metrics const int64_t TotalFiles = NumThreads * FilesPerThread; const int64_t TotalBytes = TotalFiles * BytesPerFile; State.SetItemsProcessed(State.iterations() * TotalFiles); State.SetBytesProcessed(State.iterations() * TotalBytes); State.counters["threads"] = NumThreads; State.counters["files_per_thread"] = FilesPerThread; State.counters["total_files"] = TotalFiles; } // Test various combinations of thread counts and files per thread // These represent different parallelism scenarios: // - Low parallelism, many files per thread (serial-like workload) // - High parallelism, few files per thread (highly parallel workload) // - Balanced scenarios BENCHMARK(BM_WriteToOutputInParallel) ->Args({1, 1000}) // 1 thread, 1000 files ->Args({2, 500}) // 2 threads, 500 files each ->Args({4, 250}) // 4 threads, 250 files each ->Args({8, 125}) // 8 threads, 125 files each ->Args({10, 100}) // 10 threads, 100 files each ->Args({10, 1000}) // 10 threads, 1000 files each (stress test) ->Unit(benchmark::kMillisecond); BENCHMARK_MAIN();