mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-06 01:51:51 +00:00
Merge pull request #5707 from pmmp/hot-events-optimisation
Avoid unnecessary event-related work in hot paths when the events have no registered handlers
This commit is contained in:
commit
6000bcccdd
@ -27,6 +27,7 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\event;
|
namespace pocketmine\event;
|
||||||
|
|
||||||
use pocketmine\timings\Timings;
|
use pocketmine\timings\Timings;
|
||||||
|
use function count;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
|
|
||||||
abstract class Event{
|
abstract class Event{
|
||||||
@ -54,11 +55,11 @@ abstract class Event{
|
|||||||
$timings = Timings::getEventTimings($this);
|
$timings = Timings::getEventTimings($this);
|
||||||
$timings->startTiming();
|
$timings->startTiming();
|
||||||
|
|
||||||
$handlerList = HandlerListManager::global()->getListFor(get_class($this));
|
$handlers = HandlerListManager::global()->getHandlersFor(static::class);
|
||||||
|
|
||||||
++self::$eventCallDepth;
|
++self::$eventCallDepth;
|
||||||
try{
|
try{
|
||||||
foreach($handlerList->getListenerList() as $registration){
|
foreach($handlers as $registration){
|
||||||
$registration->callEvent($this);
|
$registration->callEvent($this);
|
||||||
}
|
}
|
||||||
}finally{
|
}finally{
|
||||||
@ -66,4 +67,14 @@ abstract class Event{
|
|||||||
$timings->stopTiming();
|
$timings->stopTiming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current class context has any registered global handlers.
|
||||||
|
* This can be used in hot code paths to avoid unnecessary event object creation.
|
||||||
|
*
|
||||||
|
* Usage: SomeEventClass::hasHandlers()
|
||||||
|
*/
|
||||||
|
public static function hasHandlers() : bool{
|
||||||
|
return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,6 @@ class HandlerList{
|
|||||||
/** @var RegisteredListener[][] */
|
/** @var RegisteredListener[][] */
|
||||||
private array $handlerSlots = [];
|
private array $handlerSlots = [];
|
||||||
|
|
||||||
private RegisteredListenerCache $handlerCache;
|
|
||||||
|
|
||||||
/** @var RegisteredListenerCache[] */
|
/** @var RegisteredListenerCache[] */
|
||||||
private array $affectedHandlerCaches = [];
|
private array $affectedHandlerCaches = [];
|
||||||
|
|
||||||
@ -44,9 +42,9 @@ class HandlerList{
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $class,
|
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){
|
for($list = $this; $list !== null; $list = $list->parentList){
|
||||||
$list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache;
|
$list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,11 @@ class HandlerListManager{
|
|||||||
|
|
||||||
/** @var HandlerList[] classname => HandlerList */
|
/** @var HandlerList[] classname => HandlerList */
|
||||||
private array $allLists = [];
|
private array $allLists = [];
|
||||||
|
/**
|
||||||
|
* @var RegisteredListenerCache[] event class name => cache
|
||||||
|
* @phpstan-var array<class-string<Event>, RegisteredListenerCache>
|
||||||
|
*/
|
||||||
|
private array $handlerCaches = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters all the listeners
|
* Unregisters all the listeners
|
||||||
@ -98,7 +103,25 @@ class HandlerListManager{
|
|||||||
}
|
}
|
||||||
|
|
||||||
$parent = self::resolveNearestHandleableParent($class);
|
$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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -406,11 +406,13 @@ class NetworkSession{
|
|||||||
$timings->startTiming();
|
$timings->startTiming();
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
if(DataPacketDecodeEvent::hasHandlers()){
|
||||||
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
|
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
|
||||||
$ev->call();
|
$ev->call();
|
||||||
if($ev->isCancelled()){
|
if($ev->isCancelled()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
|
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
|
||||||
$decodeTimings->startTiming();
|
$decodeTimings->startTiming();
|
||||||
@ -429,9 +431,13 @@ class NetworkSession{
|
|||||||
$decodeTimings->stopTiming();
|
$decodeTimings->stopTiming();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(DataPacketReceiveEvent::hasHandlers()){
|
||||||
$ev = new DataPacketReceiveEvent($this, $packet);
|
$ev = new DataPacketReceiveEvent($this, $packet);
|
||||||
$ev->call();
|
$ev->call();
|
||||||
if(!$ev->isCancelled()){
|
if($ev->isCancelled()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
$handlerTimings = Timings::getHandleDataPacketTimings($packet);
|
$handlerTimings = Timings::getHandleDataPacketTimings($packet);
|
||||||
$handlerTimings->startTiming();
|
$handlerTimings->startTiming();
|
||||||
try{
|
try{
|
||||||
@ -441,7 +447,6 @@ class NetworkSession{
|
|||||||
}finally{
|
}finally{
|
||||||
$handlerTimings->stopTiming();
|
$handlerTimings->stopTiming();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}finally{
|
}finally{
|
||||||
$timings->stopTiming();
|
$timings->stopTiming();
|
||||||
}
|
}
|
||||||
@ -459,12 +464,16 @@ class NetworkSession{
|
|||||||
$timings = Timings::getSendDataPacketTimings($packet);
|
$timings = Timings::getSendDataPacketTimings($packet);
|
||||||
$timings->startTiming();
|
$timings->startTiming();
|
||||||
try{
|
try{
|
||||||
|
if(DataPacketSendEvent::hasHandlers()){
|
||||||
$ev = new DataPacketSendEvent([$this], [$packet]);
|
$ev = new DataPacketSendEvent([$this], [$packet]);
|
||||||
$ev->call();
|
$ev->call();
|
||||||
if($ev->isCancelled()){
|
if($ev->isCancelled()){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$packets = $ev->getPackets();
|
$packets = $ev->getPackets();
|
||||||
|
}else{
|
||||||
|
$packets = [$packet];
|
||||||
|
}
|
||||||
|
|
||||||
foreach($packets as $evPacket){
|
foreach($packets as $evPacket){
|
||||||
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket));
|
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket));
|
||||||
|
@ -44,12 +44,14 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
|||||||
public function broadcastPackets(array $recipients, array $packets) : void{
|
public function broadcastPackets(array $recipients, array $packets) : void{
|
||||||
//TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative
|
//TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative
|
||||||
//implementation that doesn't fire events
|
//implementation that doesn't fire events
|
||||||
|
if(DataPacketSendEvent::hasHandlers()){
|
||||||
$ev = new DataPacketSendEvent($recipients, $packets);
|
$ev = new DataPacketSendEvent($recipients, $packets);
|
||||||
$ev->call();
|
$ev->call();
|
||||||
if($ev->isCancelled()){
|
if($ev->isCancelled()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$packets = $ev->getPackets();
|
$packets = $ev->getPackets();
|
||||||
|
}
|
||||||
|
|
||||||
$compressors = [];
|
$compressors = [];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user