Skip to content

Commit fe0fb6a

Browse files
committed
Improve perfomance of actorsystem (#15610)
1 parent a5a96d9 commit fe0fb6a

File tree

7 files changed

+125
-24
lines changed

7 files changed

+125
-24
lines changed

ydb/library/actors/core/executor_pool_base.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ namespace NActors {
7171
TExecutorPoolBase::TExecutorPoolBase(ui32 poolId, ui32 threads, TAffinity* affinity, bool useRingQueue)
7272
: TExecutorPoolBaseMailboxed(poolId)
7373
, PoolThreads(threads)
74-
, UseRingQueue(useRingQueue)
74+
, UseRingQueueValue(useRingQueue)
7575
, ThreadsAffinity(affinity)
7676
{
7777
if (useRingQueue) {
@@ -147,7 +147,7 @@ namespace NActors {
147147
}
148148

149149
void TExecutorPoolBase::ScheduleActivation(TMailbox* mailbox) {
150-
if (UseRingQueue) {
150+
if (UseRingQueue()) {
151151
ScheduleActivationEx(mailbox, 0);
152152
} else {
153153
ScheduleActivationEx(mailbox, AtomicIncrement(ActivationsRevolvingCounter));
@@ -169,9 +169,12 @@ namespace NActors {
169169
if (NFeatures::IsCommon() && IsAllowedToCapture(this) || IsTailSend(this)) {
170170
mailbox = TlsThreadContext->CaptureMailbox(mailbox);
171171
}
172-
if (mailbox && UseRingQueue) {
172+
if (!mailbox) {
173+
return;
174+
}
175+
if (UseRingQueueValue) {
173176
ScheduleActivationEx(mailbox, 0);
174-
} else if (mailbox) {
177+
} else {
175178
ScheduleActivationEx(mailbox, AtomicIncrement(ActivationsRevolvingCounter));
176179
}
177180
}
@@ -302,4 +305,8 @@ namespace NActors {
302305
TMailboxTable* TExecutorPoolBaseMailboxed::GetMailboxTable() const {
303306
return MailboxTable;
304307
}
308+
309+
bool TExecutorPoolBase::UseRingQueue() const {
310+
return UseRingQueueValue;
311+
}
305312
}

ydb/library/actors/core/executor_pool_base.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ namespace NActors {
5454
using TUnorderedCacheActivationQueue = TUnorderedCache<ui32, 512, 4>;
5555

5656
const i16 PoolThreads;
57-
const bool UseRingQueue;
58-
TIntrusivePtr<TAffinity> ThreadsAffinity;
59-
TAtomic Semaphore = 0;
60-
std::variant<TUnorderedCacheActivationQueue, TRingActivationQueue> Activations;
57+
const bool UseRingQueueValue;
58+
alignas(64) TIntrusivePtr<TAffinity> ThreadsAffinity;
59+
alignas(64) TAtomic Semaphore = 0;
60+
alignas(64) std::variant<TUnorderedCacheActivationQueue, TRingActivationQueue> Activations;
6161
TAtomic ActivationsRevolvingCounter = 0;
6262
std::atomic_bool StopFlag = false;
6363
public:
@@ -67,6 +67,7 @@ namespace NActors {
6767
void SpecificScheduleActivation(TMailbox* mailbox) override;
6868
TAffinity* Affinity() const override;
6969
ui32 GetThreads() const override;
70+
bool UseRingQueue() const;
7071
};
7172

7273
void DoActorInit(TActorSystem*, IActor*, const TActorId&, const TActorId&);

ydb/library/actors/core/executor_pool_basic.cpp

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,63 @@ namespace NActors {
264264
return nullptr;
265265
}
266266

267+
TMailbox* TBasicExecutorPool::GetReadyActivationRingQueue(ui64 revolvingCounter) {
268+
if (StopFlag.load(std::memory_order_acquire)) {
269+
return nullptr;
270+
}
271+
272+
TWorkerId workerId = TlsThreadContext->WorkerId();
273+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "");
274+
NHPTimer::STime hpnow = GetCycleCountFast();
275+
TInternalActorTypeGuard<EInternalActorSystemActivity::ACTOR_SYSTEM_GET_ACTIVATION, false> activityGuard(hpnow);
276+
277+
Y_DEBUG_ABORT_UNLESS(workerId < MaxFullThreadCount);
278+
279+
Threads[workerId].UnsetWork();
280+
if (Harmonizer) {
281+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "try to harmonize");
282+
LWPROBE(TryToHarmonize, PoolId, PoolName);
283+
Harmonizer->Harmonize(hpnow);
284+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "harmonize done");
285+
}
286+
287+
do {
288+
{
289+
TInternalActorTypeGuard<EInternalActorSystemActivity::ACTOR_SYSTEM_GET_ACTIVATION_FROM_QUEUE, false> activityGuard;
290+
if (const ui32 activation = std::visit([&revolvingCounter](auto &x) {return x.Pop(++revolvingCounter);}, Activations)) {
291+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "activation found");
292+
Threads[workerId].SetWork();
293+
AtomicDecrement(Semaphore);
294+
return MailboxTable->Get(activation);
295+
}
296+
}
297+
298+
TAtomic semaphoreRaw = AtomicGet(Semaphore);
299+
TSemaphore semaphore = TSemaphore::GetSemaphore(semaphoreRaw);
300+
if (!semaphore.OldSemaphore || workerId >= 0 && semaphore.CurrentSleepThreadCount < 0) {
301+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "semaphore.OldSemaphore == 0 or workerId >= 0 && semaphore.CurrentSleepThreadCount < 0");
302+
if (!TlsThreadContext->ExecutionContext.IsNeededToWaitNextActivation) {
303+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "wctx.ExecutionContext.IsNeededToWaitNextActivation == false");
304+
return nullptr;
305+
}
306+
307+
bool needToWait = false;
308+
bool needToBlock = false;
309+
AskToGoToSleep(&needToWait, &needToBlock);
310+
if (needToWait) {
311+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "go to sleep");
312+
if (Threads[workerId].Wait(SpinThresholdCycles, &StopFlag)) {
313+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "sleep interrupted");
314+
return nullptr;
315+
}
316+
}
317+
}
318+
SpinLockPause();
319+
} while (!StopFlag.load(std::memory_order_acquire));
320+
321+
return nullptr;
322+
}
323+
267324
TMailbox* TBasicExecutorPool::GetReadyActivationLocalQueue(ui64 revolvingCounter) {
268325
TWorkerId workerId = TlsThreadContext->WorkerId();
269326
Y_DEBUG_ABORT_UNLESS(workerId < static_cast<i32>(MaxFullThreadCount));
@@ -278,13 +335,19 @@ namespace NActors {
278335
TlsThreadContext->LocalQueueContext.LocalQueueSize = LocalQueueSize.load(std::memory_order_relaxed);
279336
}
280337
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "local queue done; moving to common");
338+
if (TlsThreadContext->UseRingQueue()) {
339+
return GetReadyActivationRingQueue(revolvingCounter);
340+
}
281341
return GetReadyActivationCommon(revolvingCounter);
282342
}
283343

284344
TMailbox* TBasicExecutorPool::GetReadyActivation(ui64 revolvingCounter) {
285345
if (MaxLocalQueueSize) {
286346
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "local queue");
287347
return GetReadyActivationLocalQueue(revolvingCounter);
348+
} else if (TlsThreadContext->UseRingQueue()) {
349+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "ring queue");
350+
return GetReadyActivationRingQueue(revolvingCounter);
288351
} else {
289352
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "");
290353
return GetReadyActivationCommon(revolvingCounter);
@@ -305,37 +368,48 @@ namespace NActors {
305368
}
306369
}
307370

308-
void TBasicExecutorPool::ScheduleActivationExCommon(TMailbox* mailbox, ui64 revolvingCounter, TAtomic x) {
309-
TSemaphore semaphore = TSemaphore::GetSemaphore(x);
310-
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "semaphore.OldSemaphore == ", semaphore.OldSemaphore, " semaphore.CurrentSleepThreadCount == ", semaphore.CurrentSleepThreadCount);
311-
std::visit([mailbox, revolvingCounter](auto &x) {
312-
x.Push(mailbox->Hint, revolvingCounter);
371+
void TBasicExecutorPool::ScheduleActivationExCommon(TMailbox* mailbox, ui64 revolvingCounter, std::optional<TAtomic> initSemaphore) {
372+
std::visit([mailbox, revolvingCounter](auto &queue) {
373+
queue.Push(mailbox->Hint, revolvingCounter);
313374
}, Activations);
314375
bool needToWakeUp = false;
315376
bool needToChangeOldSemaphore = true;
316377

317-
if (SharedPool) {
378+
TAtomic x;
379+
TSemaphore semaphore;
380+
if (!initSemaphore || SharedPool) {
318381
x = AtomicIncrement(Semaphore);
319382
needToChangeOldSemaphore = false;
383+
semaphore = TSemaphore::GetSemaphore(x);
384+
} else {
385+
x = *initSemaphore;
386+
semaphore = TSemaphore::GetSemaphore(x);
387+
}
388+
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "semaphore.OldSemaphore == ", semaphore.OldSemaphore, " semaphore.CurrentSleepThreadCount == ", semaphore.CurrentSleepThreadCount);
389+
if (SharedPool) {
320390
if (SharedPool->WakeUpLocalThreads(PoolId)) {
321391
EXECUTOR_POOL_BASIC_DEBUG(EDebugLevel::Activation, "shared pool wake up local threads");
322392
return;
323393
}
324-
semaphore = TSemaphore::GetSemaphore(x);
325394
}
326395

327396
i16 sleepThreads = 0;
328397
Y_UNUSED(sleepThreads);
329398
do {
330399
needToWakeUp = semaphore.CurrentSleepThreadCount > 0;
331400
i64 oldX = semaphore.ConvertToI64();
401+
bool changed = false;
332402
if (needToChangeOldSemaphore) {
333403
semaphore.OldSemaphore++;
404+
changed = true;
334405
}
335406
if (needToWakeUp) {
336407
sleepThreads = semaphore.CurrentSleepThreadCount--;
408+
changed = true;
409+
}
410+
if (changed) {
411+
x = AtomicGetAndCas(&Semaphore, semaphore.ConvertToI64(), oldX);
337412
}
338-
x = AtomicGetAndCas(&Semaphore, semaphore.ConvertToI64(), oldX);
339413
if (x == oldX) {
340414
break;
341415
}
@@ -383,14 +457,14 @@ namespace NActors {
383457
return;
384458
}
385459
}
386-
ScheduleActivationExCommon(mailbox, revolvingWriteCounter, AtomicGet(Semaphore));
460+
ScheduleActivationExCommon(mailbox, revolvingWriteCounter, std::nullopt);
387461
}
388462

389463
void TBasicExecutorPool::ScheduleActivationEx(TMailbox* mailbox, ui64 revolvingCounter) {
390464
if (MaxLocalQueueSize) {
391465
ScheduleActivationExLocalQueue(mailbox, revolvingCounter);
392466
} else {
393-
ScheduleActivationExCommon(mailbox, revolvingCounter, AtomicGet(Semaphore));
467+
ScheduleActivationExCommon(mailbox, revolvingCounter, std::nullopt);
394468
}
395469
}
396470

ydb/library/actors/core/executor_pool_basic.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,15 @@ namespace NActors {
234234
TMailbox* GetReadyActivation(ui64 revolvingReadCounter) override;
235235
TMailbox* GetReadyActivationCommon(ui64 revolvingReadCounter);
236236
TMailbox* GetReadyActivationShared(ui64 revolvingReadCounter);
237+
TMailbox* GetReadyActivationRingQueue(ui64 revolvingReadCounter);
237238
TMailbox* GetReadyActivationLocalQueue(ui64 revolvingReadCounter);
238239

239240
void Schedule(TInstant deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) override;
240241
void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) override;
241242
void Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) override;
242243

243244
void ScheduleActivationEx(TMailbox* mailbox, ui64 revolvingWriteCounter) override;
244-
void ScheduleActivationExCommon(TMailbox* mailbox, ui64 revolvingWriteCounter, TAtomic semaphoreValue);
245+
void ScheduleActivationExCommon(TMailbox* mailbox, ui64 revolvingWriteCounter, std::optional<TAtomic> semaphoreValue);
245246
void ScheduleActivationExLocalQueue(TMailbox* mailbox, ui64 revolvingWriteCounter);
246247

247248
void SetLocalQueueSize(ui16 size);

ydb/library/actors/core/executor_thread.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ namespace NActors {
110110
ui64 CurrentActorScheduledEventsCounter = 0;
111111

112112
// Thread-specific
113-
mutable TThreadContext ThreadCtx;
114-
mutable TExecutionStats ExecutionStats;
115-
ui64 RevolvingReadCounter = 0;
113+
alignas(64) mutable TThreadContext ThreadCtx;
114+
alignas(64) mutable TExecutionStats ExecutionStats;
115+
alignas(64) ui64 RevolvingReadCounter = 0;
116116
ui64 RevolvingWriteCounter = 0;
117117
const TString ThreadName;
118118
volatile TThreadId ThreadId = UnknownThreadId;

ydb/library/actors/core/thread_context.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
#include "thread_context.h"
22

33
#include "executor_pool.h"
4+
#include "executor_pool_base.h"
45

56
namespace NActors {
67

8+
bool UseRingQueue(IExecutorPool* pool) {
9+
if (auto* basePool = dynamic_cast<TExecutorPoolBase*>(pool)) {
10+
return basePool->UseRingQueue();
11+
}
12+
return false;
13+
}
14+
715
TWorkerContext::TWorkerContext(TWorkerId workerId, IExecutorPool* pool, IExecutorPool* sharedPool)
816
: WorkerId(workerId)
917
, Pool(pool)
1018
, OwnerPool(pool)
1119
, SharedPool(sharedPool)
20+
, UseRingQueueValue(::NActors::UseRingQueue(pool))
1221
{
1322
AssignPool(pool);
1423
}
@@ -37,6 +46,10 @@ namespace NActors {
3746
return SharedPool != nullptr;
3847
}
3948

49+
bool TWorkerContext::UseRingQueue() const {
50+
return UseRingQueueValue;
51+
}
52+
4053
void TWorkerContext::AssignPool(IExecutorPool* pool, ui64 softDeadlineTs) {
4154
Pool = pool;
4255
TimePerMailboxTs = pool ? pool->TimePerMailboxTs() : TBasicExecutorPoolConfig::DEFAULT_TIME_PER_MAILBOX.SecondsFloat() * NHPTimer::GetClockRate();
@@ -78,6 +91,10 @@ namespace NActors {
7891
return WorkerContext.IsShared();
7992
}
8093

94+
bool TThreadContext::UseRingQueue() const {
95+
return WorkerContext.UseRingQueue();
96+
}
97+
8198
ui64 TThreadContext::TimePerMailboxTs() const {
8299
return WorkerContext.TimePerMailboxTs;
83100
}

ydb/library/actors/core/thread_context.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,15 @@ namespace NActors {
5050
ui64 TimePerMailboxTs = 0;
5151
ui32 EventsPerMailbox = 0;
5252
ui64 SoftDeadlineTs = ui64(-1);
53+
bool UseRingQueueValue = false;
5354

5455
TWorkerContext(TWorkerId workerId, IExecutorPool* pool, IExecutorPool* sharedPool);
5556

5657
ui32 PoolId() const;
5758
TString PoolName() const;
5859
ui32 OwnerPoolId() const;
5960
bool IsShared() const;
60-
61+
bool UseRingQueue() const;
6162
void AssignPool(IExecutorPool* pool, ui64 softDeadlineTs = -1);
6263
void FreeMailbox(TMailbox* mailbox);
6364
};
@@ -118,7 +119,7 @@ namespace NActors {
118119
ui32 EventsPerMailbox() const;
119120
ui64 SoftDeadlineTs() const;
120121
void FreeMailbox(TMailbox* mailbox);
121-
122+
bool UseRingQueue() const;
122123
void AssignPool(IExecutorPool* pool, ui64 softDeadlineTs = Max<ui64>());
123124

124125
bool CheckSendingType(ESendingType type) const;

0 commit comments

Comments
 (0)