| //===- 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 <string> |
| #include <thread> |
| #include <vector> |
| |
| 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<SmallString<128>> 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<std::thread> 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<const char *>(&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(); |