From 0629d11e130ba3d9b2b240c97dfbecfa79e121a1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Apr 2023 20:51:55 +0100 Subject: [PATCH] Avoid unnecessary events work in handleDataPacket if the events have no registered handlers this particular optimisation became possible thanks to changes in 4.19. I observed that the allocation of Event objects and calling ->call() was costing us a significant percentage of the time taken in PlayerAuthInputPacket handlers. This change produces a measurable 2 microsecond reduction in overhead for PlayerAuthInputPacket handling when players are not moving (10.7 -> 8.7 microseconds). On a server with 200 players, this translates into a 1% reduction in CPU load for PlayerAuthInputPacket alone. It will also benefit other packets, but not to the extent that PlayerAuthInputPacket benefits. --- src/event/Event.php | 11 +++++++++ src/network/mcpe/NetworkSession.php | 35 ++++++++++++++++------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/event/Event.php b/src/event/Event.php index 1ae7bb96f..6264d8525 100644 --- a/src/event/Event.php +++ b/src/event/Event.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace pocketmine\event; use pocketmine\timings\Timings; +use function count; use function get_class; abstract class Event{ @@ -67,4 +68,14 @@ abstract class Event{ $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()->getListFor(static::class)->getListenerList()) > 0; + } } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 7f07fbd77..dfba61bab 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -401,10 +401,12 @@ class NetworkSession{ $timings->startTiming(); try{ - $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); - $ev->call(); - if($ev->isCancelled()){ - return; + if(DataPacketDecodeEvent::hasHandlers()){ + $ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer); + $ev->call(); + if($ev->isCancelled()){ + return; + } } $decodeTimings = Timings::getDecodeDataPacketTimings($packet); @@ -424,19 +426,22 @@ class NetworkSession{ $decodeTimings->stopTiming(); } - $ev = new DataPacketReceiveEvent($this, $packet); - $ev->call(); - if(!$ev->isCancelled()){ - $handlerTimings = Timings::getHandleDataPacketTimings($packet); - $handlerTimings->startTiming(); - try{ - if($this->handler === null || !$packet->handle($this->handler)){ - $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); - } - }finally{ - $handlerTimings->stopTiming(); + if(DataPacketReceiveEvent::hasHandlers()){ + $ev = new DataPacketReceiveEvent($this, $packet); + $ev->call(); + if($ev->isCancelled()){ + return; } } + $handlerTimings = Timings::getHandleDataPacketTimings($packet); + $handlerTimings->startTiming(); + try{ + if($this->handler === null || !$packet->handle($this->handler)){ + $this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer())); + } + }finally{ + $handlerTimings->stopTiming(); + } }finally{ $timings->stopTiming(); }