[*] Clean up grug and auasync scheduler / improved idle cpu usage

This commit is contained in:
Reece Wilson 2024-03-26 18:37:24 +00:00
parent 8a9c45c213
commit 30fa15b726
6 changed files with 84 additions and 73 deletions

View File

@ -111,11 +111,11 @@ namespace Aurora
struct AsyncConfig
{
bool bStartSchedularOnStartup { true }; // turn this off to make your application lighter-weight; turn this on for higher performance (+expensive) scheduling
bool bEnableLegacyTicks { false }; // turn this on to enable an async apps singleton threadpool to SysPump on worker id zero. Alternatively, use SetMainThreadForSysPumpScheduling once you have a thread pool and worker id.
bool bStartSchedularOnStartup { true }; // spawns the sched thread during the runtime initialization process, otherwise delegate it until the last min
bool bEnableLegacyTicks { false }; // turn this on to enable an async apps singleton threadpool to SysPump on worker id zero. Alternatively, use SetMainThreadForSysPumpScheduling once you have a thread pool and worker id.
AuUInt32 threadPoolDefaultStackSize { };
AuUInt32 dwSchedulerRateLimitMS { 2 }; //
AuUInt32 dwLegacyMainThreadSystemTickMS { 25 }; // nowadays this is primarily used to dispatch main-thread posted (AuConsole) commands
AuUInt32 dwSchedulerRateLimitNS { AuMSToNS<AuUInt64>(2) }; //
AuUInt32 dwLegacyMainThreadSystemTickMS { 60 }; // nowadays this is primarily used to dispatch main-thread posted (AuConsole) commands
};
struct FIOConfig

View File

@ -81,32 +81,35 @@ namespace Aurora::Async
static void SchedNextTime(AuUInt64 uNSAbs)
{
if (uNextSysTickGuessed > uNSAbs ||
!uNextSysTickGuessed)
while (true)
{
while (true)
auto uCurrent = AuAtomicLoad(&uNextSysTickGuessed);
if (uCurrent &&
uCurrent <= uNSAbs)
{
auto uCurrent = uNextSysTickGuessed;
return;
}
if (uCurrent &&
uCurrent <= uNSAbs)
{
break;
}
auto uRateLimit = AuAtomicLoad(&uNextWakeuptimeRateLimit);
if (uRateLimit &&
uRateLimit > uNSAbs)
{
uNSAbs = uRateLimit;
if (uNextWakeuptimeRateLimit > uNSAbs)
if (uRateLimit == uCurrent)
{
uNSAbs = uNextWakeuptimeRateLimit;
}
if (AuAtomicCompareExchange(&uNextSysTickGuessed, uNSAbs, uCurrent) == uCurrent)
{
break;
return;
}
}
gSchedCondvar->Signal();
if (AuAtomicCompareExchange(&uNextSysTickGuessed, uNSAbs, uCurrent) == uCurrent)
{
break;
}
}
gSchedCondvar->Signal();
}
static void SchedThread()
@ -123,7 +126,7 @@ namespace Aurora::Async
{
AU_LOCK_GUARD(gSchedLock);
auto uNextTick = uNextSysTickGuessed;
auto uNextTick = AuAtomicLoad(&uNextSysTickGuessed);
auto uNow = AuTime::SteadyClockNS();
if (uNow < uNextTick)
@ -142,9 +145,13 @@ namespace Aurora::Async
0);
}
GetDispatchableTasks(pending);
if (gRuntimeConfig.async.dwSchedulerRateLimitNS)
{
uNextWakeuptimeRateLimit =
AuTime::SteadyClockNS() + gRuntimeConfig.async.dwSchedulerRateLimitNS;
}
uNextWakeuptimeRateLimit = AuTime::SteadyClockNS() + AuMSToNS<AuUInt64>(gRuntimeConfig.async.dwSchedulerRateLimitMS);
GetDispatchableTasks(pending);
}
for (auto &entry : pending)
@ -155,7 +162,7 @@ namespace Aurora::Async
}
catch (...)
{
Debug::PrintError();
SysPushErrorCatch();
}
}

View File

@ -18,6 +18,24 @@ namespace Aurora::Async
{
}
ThreadStateBase::ThreadStateBase()
{
}
ThreadStateBase::~ThreadStateBase()
{
if (this->asyncLoop)
{
this->asyncLoop->pParent = nullptr;
if (this->asyncLoop)
{
(void)this->asyncLoop->SourceRemove(this->sync.eventLs);
}
}
}
bool ThreadStateSync::Init()
{
this->eventLs = AuLoop::NewLSAsync();

View File

@ -62,6 +62,9 @@ namespace Aurora::Async
struct ThreadStateBase
{
ThreadStateBase();
~ThreadStateBase();
AuWPtr<GroupState> parent;
/////////////////////////////////
ThreadStateShutdown shutdown;

View File

@ -54,6 +54,10 @@ namespace Aurora::Async
void AsyncLoop::Schedule()
{
if (!this->pParent)
{
return;
}
if (AuThreads::GetThread() != this->pParent->thread.pThread.get())
{
AuAtomicAdd(&this->commitPending_, 1u);

View File

@ -27,13 +27,12 @@
namespace Aurora::Grug
{
static const auto kGrugSleepMs = 100;
static const auto kGrugFlushMs = 500;
static const auto kGrugSleepMs = 300;
static const auto kGrugFlushMs = 600;
static AuThreads::ThreadUnique_t gGrugsBigWorld;
static AuCondMutex gMutex; // ^ that
static AuConditionVariable gCondVar(AuUnsafeRaiiToShared(gMutex.AsPointer())); // slow logger work queue
static AuSemaphore gArrows;
static AuBinarySemaphore gArrowsRetarded(false, true, true);
static AuMutex gOtherMutex;
static AuList<AuPair<AuUInt, bool>> gHandlesToClose;
static AuList<AuThreadPrimitives::IEvent *> gEventsToTrigger;
@ -46,16 +45,16 @@ namespace Aurora::Grug
// grug require only 1 strand
static void GrugWorld()
{
// ignorerino
#if defined(AURORA_IS_LINUX_DERIVED)
LinuxSuperSecretFuckGlibc();
::LinuxSuperSecretFuckGlibc();
#endif
// grug surive first night
SlowStartupTasks();
AU_LOCK_GUARD(gMutex);
AuDebug::AddMemoryCrunch(); // this thread must never encounter an out of memory condition. begin out of memory mitigations.
// this thread must never encounter an out of memory condition. begin out of memory mitigations.
AuDebug::AddMemoryCrunch();
Utility::RateLimiter limiter;
limiter.SetNextStep(AuMSToNS<AuUInt64>(kGrugFlushMs));
@ -63,16 +62,7 @@ namespace Aurora::Grug
// grug has cave
while (AuIsThreadRunning())
{
// * -> grug wake up from 100ms (kGrugSleepMs)
// grug smashy alarm
gCondVar->WaitForSignal(kGrugFlushMs);
// grug anoy
if (gArrows && gArrows->TryLock())
{
// grug yeet
DequeueOneArrow();
}
// grug anoyd
// grug pump all async log messages
GrugFlushWrites();
@ -80,29 +70,24 @@ namespace Aurora::Grug
// adhd grug wonder around the log pool every 500ms (rate limited to 100ms sleeps)
if (limiter.CheckExchangePass())
{
// poo2loo
GrugFlushFlushs();
}
// grug give up
// grug yield for at least 100ms -> *
if (gArrows)
// grug sleep for 100ms or until poked
if (gArrowsRetarded->TryLock() ||
gArrows->LockMS(kGrugSleepMs))
{
// grug sleep for 100ms or until poked
if (gArrows->LockMS(kGrugSleepMs))
{
DequeueOneArrow();
}
}
else
{
// grug sleep 100ms
AuThreading::Sleep(kGrugSleepMs);
DequeueOneArrow();
}
// grug has to carry stupid platforms
#if defined(AURORA_IS_LINUX_DERIVED)
::LinuxSuperSecretIOTick();
#endif
// grug done
GrugDoIoWork();
}
}
@ -114,11 +99,16 @@ namespace Aurora::Grug
static void InitFlushThread()
{
if (gGrugsBigWorld)
{
return;
}
// Startup a runner thread that will take care of all the stress of triggering IO every so often on a remote thread
gGrugsBigWorld = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(GrugWorld)),
AuThreads::IThreadVectorsFunctional::OnExit_t{}),
"Cave Grug"
gGrugsBigWorld = AuMove(AuThreads::Spawn(std::bind(GrugWorld),
false,
{},
"Cave Grug"
));
if (!gGrugsBigWorld)
@ -129,7 +119,6 @@ namespace Aurora::Grug
// he's a lazy bastard
gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient);
gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow);
gGrugsBigWorld->Run();
}
void InitGrug()
@ -152,22 +141,12 @@ namespace Aurora::Grug
void NotifyGrugOfTelemetry()
{
if (gArrows)
{
gArrows->Unlock(1);
}
DrachenlordScreech();
gArrows->Unlock(1);
}
void DrachenlordScreech()
{
if (!gCondVar)
{
return;
}
gCondVar->Signal();
gArrowsRetarded->Set();
}
bool IsGrug()