blob: c2b29f07a3eee22d3c9236a750dabcd08d840e00 [file] [edit]
//===- 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();