HandlerListManager: track RegisteredListenerCache directly

This change improves the performance of calling an event with 0 handlers by about 10% with no other changes.

Since we have to access the list eventually anyway, we can cut out some unnecessary work by returning the handlers from the cache directly, instead of fetching the HandlerList for no reason.

This also improves the performance of Event::hasHandlers() by about 40%, which is pretty significant (120 ns -> 80 ns).
This commit is contained in:
Dylan K. Taylor 2023-08-01 17:37:49 +01:00
parent 442d65143d
commit 2608637210
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
3 changed files with 29 additions and 8 deletions

View File

@ -55,11 +55,11 @@ abstract class Event{
$timings = Timings::getEventTimings($this);
$timings->startTiming();
$handlerList = HandlerListManager::global()->getListFor(get_class($this));
$handlers = HandlerListManager::global()->getHandlersFor(static::class);
++self::$eventCallDepth;
try{
foreach($handlerList->getListenerList() as $registration){
foreach($handlers as $registration){
$registration->callEvent($this);
}
}finally{
@ -75,6 +75,6 @@ abstract class Event{
* Usage: SomeEventClass::hasHandlers()
*/
public static function hasHandlers() : bool{
return count(HandlerListManager::global()->getListFor(static::class)->getListenerList()) > 0;
return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0;
}
}

View File

@ -33,8 +33,6 @@ class HandlerList{
/** @var RegisteredListener[][] */
private array $handlerSlots = [];
private RegisteredListenerCache $handlerCache;
/** @var RegisteredListenerCache[] */
private array $affectedHandlerCaches = [];
@ -44,9 +42,9 @@ class HandlerList{
*/
public function __construct(
private string $class,
private ?HandlerList $parentList
private ?HandlerList $parentList,
private RegisteredListenerCache $handlerCache = new RegisteredListenerCache()
){
$this->handlerCache = new RegisteredListenerCache();
for($list = $this; $list !== null; $list = $list->parentList){
$list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache;
}

View File

@ -36,6 +36,11 @@ class HandlerListManager{
/** @var HandlerList[] classname => HandlerList */
private array $allLists = [];
/**
* @var RegisteredListenerCache[] event class name => cache
* @phpstan-var array<class-string<Event>, RegisteredListenerCache>
*/
private array $handlerCaches = [];
/**
* Unregisters all the listeners
@ -98,7 +103,25 @@ class HandlerListManager{
}
$parent = self::resolveNearestHandleableParent($class);
return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null);
$cache = new RegisteredListenerCache();
$this->handlerCaches[$event] = $cache;
return $this->allLists[$event] = new HandlerList(
$event,
parentList: $parent !== null ? $this->getListFor($parent->getName()) : null,
handlerCache: $cache
);
}
/**
* @phpstan-template TEvent of Event
* @phpstan-param class-string<TEvent> $event
*
* @return RegisteredListener[]
*/
public function getHandlersFor(string $event) : array{
$cache = $this->handlerCaches[$event] ?? null;
//getListFor() will populate the cache for the next call
return $cache?->list ?? $this->getListFor($event)->getListenerList();
}
/**