This patch introduces support for the jobserver protocol to control parallelism for device offloading tasks. When running a parallel build with a modern build system like `make -jN` or `ninja -jN`, each Clang process might also be configured to use multiple threads for its own tasks (e.g., via `--offload-jobs=4`). This can lead to an explosion of threads (N * 4), causing heavy system load, CPU contention, and ultimately slowing down the entire build. This patch allows Clang to act as a cooperative client of the build system's jobserver. It extends the `--offload-jobs` option to accept the value 'jobserver'. With the recent addition of jobserver support to the Ninja build system, this functionality now benefits users of both Make and Ninja. When `--offload-jobs=jobserver` is specified, Clang's thread pool will: 1. Parse the MAKEFLAGS environment variable to find the jobserver details. 2. Before dispatching a task, acquire a job slot from the jobserver. If none are available, the worker thread will block. 3. Release the job slot once the task is complete. This ensures that the total number of active offload tasks across all Clang processes does not exceed the limit defined by the parent build system, leading to more efficient and controlled parallel builds. Implementation: - A new library, `llvm/Support/Jobserver`, is added to provide a platform-agnostic client for the jobserver protocol, with backends for Unix (FIFO) and Windows (semaphores). - `llvm/Support/ThreadPool` and `llvm/Support/Parallel` are updated with a `jobserver_concurrency` strategy to integrate this logic. - The Clang driver and linker-wrapper are modified to recognize the 'jobserver' argument and enable the new thread pool strategy. - New unit and integration tests are added to validate the feature.
120 lines
3.9 KiB
C++
120 lines
3.9 KiB
C++
//===-- llvm/Support/Threading.cpp- Control multithreading mode --*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines helper functions for running LLVM in a multi-threaded
|
|
// environment.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/Threading.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/Config/llvm-config.h"
|
|
#include "llvm/Support/Jobserver.h"
|
|
|
|
#include <cassert>
|
|
#include <optional>
|
|
#include <stdlib.h>
|
|
|
|
using namespace llvm;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
//=== WARNING: Implementation here must contain only TRULY operating system
|
|
//=== independent code.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if LLVM_ENABLE_THREADS == 0 || \
|
|
(!defined(_WIN32) && !defined(HAVE_PTHREAD_H))
|
|
uint64_t llvm::get_threadid() { return 0; }
|
|
|
|
uint32_t llvm::get_max_thread_name_length() { return 0; }
|
|
|
|
void llvm::set_thread_name(const Twine &Name) {}
|
|
|
|
void llvm::get_thread_name(SmallVectorImpl<char> &Name) { Name.clear(); }
|
|
|
|
llvm::BitVector llvm::get_thread_affinity_mask() { return {}; }
|
|
|
|
unsigned llvm::ThreadPoolStrategy::compute_thread_count() const {
|
|
// When threads are disabled, ensure clients will loop at least once.
|
|
return 1;
|
|
}
|
|
|
|
// Unknown if threading turned off
|
|
int llvm::get_physical_cores() { return -1; }
|
|
|
|
#else
|
|
|
|
static int computeHostNumHardwareThreads();
|
|
|
|
unsigned llvm::ThreadPoolStrategy::compute_thread_count() const {
|
|
if (UseJobserver)
|
|
if (auto JS = JobserverClient::getInstance())
|
|
return JS->getNumJobs();
|
|
|
|
int MaxThreadCount =
|
|
UseHyperThreads ? computeHostNumHardwareThreads() : get_physical_cores();
|
|
if (MaxThreadCount <= 0)
|
|
MaxThreadCount = 1;
|
|
if (ThreadsRequested == 0)
|
|
return MaxThreadCount;
|
|
if (!Limit)
|
|
return ThreadsRequested;
|
|
return std::min((unsigned)MaxThreadCount, ThreadsRequested);
|
|
}
|
|
|
|
// Include the platform-specific parts of this class.
|
|
#ifdef LLVM_ON_UNIX
|
|
#include "Unix/Threading.inc"
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include "Windows/Threading.inc"
|
|
#endif
|
|
|
|
// Must be included after Threading.inc to provide definition for llvm::thread
|
|
// because FreeBSD's condvar.h (included by user.h) misuses the "thread"
|
|
// keyword.
|
|
#include "llvm/Support/thread.h"
|
|
|
|
#if defined(__APPLE__)
|
|
// Darwin's default stack size for threads except the main one is only 512KB,
|
|
// which is not enough for some/many normal LLVM compilations. This implements
|
|
// the same interface as std::thread but requests the same stack size as the
|
|
// main thread (8MB) before creation.
|
|
const std::optional<unsigned> llvm::thread::DefaultStackSize = 8 * 1024 * 1024;
|
|
#elif defined(_AIX)
|
|
// On AIX, the default pthread stack size limit is ~192k for 64-bit programs.
|
|
// This limit is easily reached when doing link-time thinLTO. AIX library
|
|
// developers have used 4MB, so we'll do the same.
|
|
const std::optional<unsigned> llvm::thread::DefaultStackSize = 4 * 1024 * 1024;
|
|
#else
|
|
const std::optional<unsigned> llvm::thread::DefaultStackSize;
|
|
#endif
|
|
|
|
|
|
#endif
|
|
|
|
std::optional<ThreadPoolStrategy>
|
|
llvm::get_threadpool_strategy(StringRef Num, ThreadPoolStrategy Default) {
|
|
if (Num == "all")
|
|
return llvm::hardware_concurrency();
|
|
if (Num.empty())
|
|
return Default;
|
|
unsigned V;
|
|
if (Num.getAsInteger(10, V))
|
|
return std::nullopt; // malformed 'Num' value
|
|
if (V == 0)
|
|
return Default;
|
|
|
|
// Do not take the Default into account. This effectively disables
|
|
// heavyweight_hardware_concurrency() if the user asks for any number of
|
|
// threads on the cmd-line.
|
|
ThreadPoolStrategy S = llvm::hardware_concurrency();
|
|
S.ThreadsRequested = V;
|
|
return S;
|
|
}
|