Skip to content

Commit ed654fb

Browse files
committed
queue-runner: limit parallelism of CPU intensive operations
My current theory is that running more parallel xz than available CPU cores is reducing our overall throughput by requiring more scheduling overhead and more cache thrashing.
1 parent a596d6c commit ed654fb

File tree

4 files changed

+27
-0
lines changed

4 files changed

+27
-0
lines changed

src/hydra-queue-runner/build-remote.cc

+18
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,16 @@ void RemoteResult::updateWithBuildResult(const nix::BuildResult & buildResult)
494494

495495
}
496496

497+
/* Utility guard object to auto-release a semaphore on destruction. */
498+
template <typename T>
499+
class SemaphoreReleaser {
500+
public:
501+
SemaphoreReleaser(T* s) : sem(s) {}
502+
~SemaphoreReleaser() { sem->release(); }
503+
504+
private:
505+
T* sem;
506+
};
497507

498508
void State::buildRemote(ref<Store> destStore,
499509
::Machine::ptr machine, Step::ptr step,
@@ -612,6 +622,14 @@ void State::buildRemote(ref<Store> destStore,
612622
result.logFile = "";
613623
}
614624

625+
/* Throttle CPU-bound work. Opportunistically skip updating the current
626+
* step, since this requires a DB roundtrip. */
627+
if (!localWorkThrottler.try_acquire()) {
628+
updateStep(ssWaitingForLocalSlot);
629+
localWorkThrottler.acquire();
630+
}
631+
SemaphoreReleaser releaser(&localWorkThrottler);
632+
615633
StorePathSet outputs;
616634
for (auto & [_, realisation] : buildResult.builtOutputs)
617635
outputs.insert(realisation.outPath);

src/hydra-queue-runner/hydra-queue-runner.cc

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ State::State(std::optional<std::string> metricsAddrOpt)
8585
: config(std::make_unique<HydraConfig>())
8686
, maxUnsupportedTime(config->getIntOption("max_unsupported_time", 0))
8787
, dbPool(config->getIntOption("max_db_connections", 128))
88+
, localWorkThrottler(config->getIntOption("max_local_worker_threads", std::min(maxSupportedLocalWorkers, std::min(4u, std::thread::hardware_concurrency()) - 2)))
8889
, maxOutputSize(config->getIntOption("max_output_size", 2ULL << 30))
8990
, maxLogSize(config->getIntOption("max_log_size", 64ULL << 20))
9091
, uploadLogsToBinaryCache(config->getBoolOption("upload_logs_to_binary_cache", false))

src/hydra-queue-runner/state.hh

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <memory>
88
#include <queue>
99
#include <regex>
10+
#include <semaphore>
1011

1112
#include <prometheus/counter.h>
1213
#include <prometheus/gauge.h>
@@ -56,6 +57,7 @@ typedef enum {
5657
ssConnecting = 10,
5758
ssSendingInputs = 20,
5859
ssBuilding = 30,
60+
ssWaitingForLocalSlot = 35,
5961
ssReceivingOutputs = 40,
6062
ssPostProcessing = 50,
6163
} StepState;
@@ -387,6 +389,10 @@ private:
387389
typedef std::map<std::string, Machine::ptr> Machines;
388390
nix::Sync<Machines> machines; // FIXME: use atomic_shared_ptr
389391

392+
/* Throttler for CPU-bound local work. */
393+
static constexpr unsigned int maxSupportedLocalWorkers = 1024;
394+
std::counting_semaphore<maxSupportedLocalWorkers> localWorkThrottler;
395+
390396
/* Various stats. */
391397
time_t startedAt;
392398
counter nrBuildsRead{0};

src/root/build.tt

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ END;
6969
<strong>Sending inputs</strong>
7070
[% ELSIF step.busy == 30 %]
7171
<strong>Building</strong>
72+
[% ELSIF step.busy == 35 %]
73+
<strong>Waiting to receive outputs</strong>
7274
[% ELSIF step.busy == 40 %]
7375
<strong>Receiving outputs</strong>
7476
[% ELSIF step.busy == 50 %]

0 commit comments

Comments
 (0)