Skip to content

Commit a5bfdf0

Browse files
committed
[Offload] Make the RPC thread sleep briefly when idle
Summary: We start this thread if the RPC client symbol is detected in the loaded binary. We should make this sleep if there's no work to avoid the thread running at high priority when the (scarecely used) RPC call is actually required. So, right now after 25 microseconds we will assume the server is inactive and begin sleeping. This resets once we do find work. AMD supports a more intelligent way to do this. HSA signals can wake a sleeping thread from the kernel, and signals can be sent from the GPU side. This would be nice to have and I'm planning on working with it in the future to make this infrastructure more usable with existing AMD workloads.
1 parent 515924f commit a5bfdf0

File tree

1 file changed

+18
-3
lines changed
  • offload/plugins-nextgen/common/src

1 file changed

+18
-3
lines changed

offload/plugins-nextgen/common/src/RPC.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ static rpc::Status handleOffloadOpcodes(plugin::GenericDeviceTy &Device,
8383
return rpc::RPC_ERROR;
8484
}
8585

86-
static rpc::Status runServer(plugin::GenericDeviceTy &Device, void *Buffer) {
86+
static rpc::Status runServer(plugin::GenericDeviceTy &Device, void *Buffer,
87+
bool &ClientInUse) {
8788
uint64_t NumPorts =
8889
std::min(Device.requestedRPCPortCount(), rpc::MAX_PORT_COUNT);
8990
rpc::Server Server(NumPorts, Buffer);
@@ -92,14 +93,14 @@ static rpc::Status runServer(plugin::GenericDeviceTy &Device, void *Buffer) {
9293
if (!Port)
9394
return rpc::RPC_SUCCESS;
9495

96+
ClientInUse = true;
9597
rpc::Status Status =
9698
handleOffloadOpcodes(Device, *Port, Device.getWarpSize());
9799

98100
// Let the `libc` library handle any other unhandled opcodes.
99101
if (Status == rpc::RPC_UNHANDLED_OPCODE)
100102
Status = LIBC_NAMESPACE::shared::handle_libc_opcodes(*Port,
101103
Device.getWarpSize());
102-
103104
Port->close();
104105

105106
return Status;
@@ -122,7 +123,11 @@ void RPCServerTy::ServerThread::shutDown() {
122123
}
123124

124125
void RPCServerTy::ServerThread::run() {
126+
static constexpr auto IdleTime = std::chrono::microseconds(25);
127+
static constexpr auto IdleSleep = std::chrono::microseconds(250);
125128
std::unique_lock<decltype(Mutex)> Lock(Mutex);
129+
130+
auto LastUse = std::chrono::steady_clock::now();
126131
for (;;) {
127132
CV.wait(Lock, [&]() {
128133
return NumUsers.load(std::memory_order_acquire) > 0 ||
@@ -133,15 +138,25 @@ void RPCServerTy::ServerThread::run() {
133138
return;
134139

135140
Lock.unlock();
141+
bool ClientInUse = false;
136142
while (NumUsers.load(std::memory_order_relaxed) > 0 &&
137143
Running.load(std::memory_order_relaxed)) {
144+
145+
// Suspend this thread briefly if there is no current work.
146+
auto Now = std::chrono::steady_clock::now();
147+
if (!ClientInUse && Now - LastUse >= IdleTime)
148+
std::this_thread::sleep_for(IdleSleep);
149+
else if (ClientInUse)
150+
LastUse = Now;
151+
152+
ClientInUse = false;
138153
std::lock_guard<decltype(Mutex)> Lock(BufferMutex);
139154
for (const auto &[Buffer, Device] : llvm::zip_equal(Buffers, Devices)) {
140155
if (!Buffer || !Device)
141156
continue;
142157

143158
// If running the server failed, print a message but keep running.
144-
if (runServer(*Device, Buffer) != rpc::RPC_SUCCESS)
159+
if (runServer(*Device, Buffer, ClientInUse) != rpc::RPC_SUCCESS)
145160
FAILURE_MESSAGE("Unhandled or invalid RPC opcode!");
146161
}
147162
}

0 commit comments

Comments
 (0)