Support for collecting timings from threads, and implement async task timings (#6333)

The following callbacks can now be registered in timings, to allow threads to be notified of these events:
- Turning on/off (`TimingsHandler::getToggleCallbacks()->add(...)`)
- Reset (`TimingsHandler::getReloadCallbacks()->add(...)`)
- Collect (`TimingsHandler::getCollectCallbacks()->add(...)`)

Collect callbacks must return `list<Promise>`. The promises must be `resolve()`d with `list<string>` of printed timings records, as returned by `TimingsHandler::printCurrentThreadRecords()`. It's recommended to use 1 promise per thread.

A timings report will be produced once all promises have been resolved.

This system is used internally to collect timings for async tasks (closes #6166).

For timings viewer developers:
Timings format version has been bumped to 3 to accommodate this change. Timings groups should now include a `ThreadId`  at the end of timings group names to ensure that their record IDs are segregated correctly, as they could otherwise conflict between threads. The main thread is not required to specify a thread ID. See pmmp/timings@13cefa6279 for implementation examples.

New PHPStan error is caused by phpstan/phpstan#10924
This commit is contained in:
Dylan T.
2024-12-01 14:49:27 +00:00
committed by GitHub
parent 89f88a9333
commit 61560ec375
12 changed files with 410 additions and 93 deletions

View File

@ -89,6 +89,8 @@ use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
use pocketmine\resourcepacks\ResourcePackManager;
use pocketmine\scheduler\AsyncPool;
use pocketmine\scheduler\TimingsCollectionTask;
use pocketmine\scheduler\TimingsControlTask;
use pocketmine\snooze\SleeperHandler;
use pocketmine\stats\SendUsageTask;
use pocketmine\thread\log\AttachableThreadSafeLogger;
@ -894,7 +896,36 @@ class Server{
$poolSize = max(1, (int) $poolSize);
}
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper);
$this->asyncPool->addWorkerStartHook(function(int $i) : void{
if(TimingsHandler::isEnabled()){
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled(true), $i);
}
});
TimingsHandler::getToggleCallbacks()->add(function(bool $enable) : void{
foreach($this->asyncPool->getRunningWorkers() as $workerId){
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled($enable), $workerId);
}
});
TimingsHandler::getReloadCallbacks()->add(function() : void{
foreach($this->asyncPool->getRunningWorkers() as $workerId){
$this->asyncPool->submitTaskToWorker(TimingsControlTask::reload(), $workerId);
}
});
TimingsHandler::getCollectCallbacks()->add(function() : array{
$promises = [];
foreach($this->asyncPool->getRunningWorkers() as $workerId){
$resolver = new PromiseResolver();
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
$promises[] = $resolver->getPromise();
}
return $promises;
});
$netCompressionThreshold = -1;
if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){
@ -968,9 +999,6 @@ class Server{
)));
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
DefaultPermissions::registerCorePermissions();
$this->commandMap = new SimpleCommandMap($this);