Skip to content

Instantly share code, notes, and snippets.

@beberlei
Created March 7, 2011 19:32
Show Gist options
  • Save beberlei/859052 to your computer and use it in GitHub Desktop.
Save beberlei/859052 to your computer and use it in GitHub Desktop.
Performance of bschussek/doctrine-common EventManager changes
<?php
require_once "Doctrine/Common/ClassLoader.php";
use Doctrine\Common\ClassLoader;
use Doctrine\Common\EventArgs;
use Doctrine\Common\EventManager;
$loader = new ClassLoader();
$loader->register();
$evm = new EventManager();
$nevm = new EventManagerNew();
$benchmarks = array();
class PropagationEventArgs extends EventArgs
{
private $propagation = false;
public function isPropagationStopped()
{
return $this->propagation;
}
}
for ($i = 0; $i < 25; $i++) {
$evm->addEventListener(array("onEvent"), new SomeEventListener);
$nevm->addEventListener(array("onEvent"), new SomeEventListener);
for ($j = 0; $j < 100; $j++) {
$benchmarks['evm'][$i][$j] = microtime(true);
for ($k = 0; $k < 100; $k++) {
$evm->dispatchEvent('onEvent', new EventArgs());
}
$benchmarks['evm'][$i][$j] = microtime(true) - $benchmarks['evm'][$i][$j];
usleep(500);
$benchmarks['nevm'][$i][$j] = microtime(true);
for ($k = 0; $k < 100; $k++) {
$nevm->dispatchEvent('onEvent', new PropagationEventArgs());
}
$benchmarks['nevm'][$i][$j] = microtime(true) - $benchmarks['nevm'][$i][$j];
usleep(500);
}
}
foreach ($benchmarks AS $name => $data) {
foreach ($data AS $listenerCount => $repetitionData) {
echo $name . "\t" . sprintf("%4d", $listenerCount) . "\t" . (array_sum($repetitionData)/count($repetitionData)) . "\n";
}
}
class SomeEventListener
{
public function onEvent(EventArgs $args)
{
}
}
class EventManagerNew
{
/**
* Map of registered listeners.
* <event> => (<objecthash> => <listener>)
*
* @var array
*/
private $_listeners = array();
/**
* Map of priorities by the object hashes of their listeners.
* <event> => (<objecthash> => <priority>)
*
* This property is used for listener sorting.
*
* @var array
*/
private $_priorities = array();
/**
* Stores which event listener lists are currently sorted.
* <event> => <sorted>
*
* @var array
*/
private $_sorted = array();
/**
* Dispatches an event to all registered listeners.
*
* @param string $eventName The name of the event to dispatch. The name of the event is
* the name of the method that is invoked on listeners.
* @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners.
* If not supplied, the single empty EventArgs instance is used.
*/
public function dispatchEvent($eventName, EventArgs $eventArgs = null)
{
if (isset($this->_listeners[$eventName])) {
$eventArgs = $eventArgs === null ? new EventArgs() : $eventArgs;
$this->sortListeners($eventName);
foreach ($this->_listeners[$eventName] as $listener) {
$this->triggerListener($listener, $eventName, $eventArgs);
if ($eventArgs->isPropagationStopped()) {
break;
}
}
}
}
/**
* Triggers the listener method for an event.
*
* This method can be overridden to add functionality that is executed
* for each listener.
*
* @param object $listener The event listener on which to invoke the listener method.
* @param string $eventName The name of the event to dispatch. The name of the event is
* the name of the method that is invoked on listeners.
* @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners.
*/
protected function triggerListener($listener, $eventName, EventArgs $eventArgs)
{
if ($listener instanceof \Closure) {
$listener->__invoke($eventArgs);
} else {
$listener->$eventName($eventArgs);
}
}
/**
* Sorts the internal list of listeners for the given event by priority.
*
* Calling this method multiple times will not cause overhead unless you
* add new listeners. As long as no listener is added, the list for an
* event name won't be sorted twice.
*
* @param string $event The name of the event.
*/
private function sortListeners($eventName)
{
if (!$this->_sorted[$eventName]) {
$p = $this->_priorities[$eventName];
uasort($this->_listeners[$eventName], function ($a, $b) use ($p) {
return $p[spl_object_hash($b)] - $p[spl_object_hash($a)];
});
$this->_sorted[$eventName] = true;
}
}
/**
* Gets the listeners of a specific event or all listeners.
*
* @param string $event The name of the event.
* @return array The event listeners for the specified event, or all event listeners.
*/
public function getListeners($event = null)
{
if ($event) {
$this->sortListeners($event);
return $this->_listeners[$event];
}
foreach ($this->_listeners as $event => $listeners) {
$this->sortListeners($event);
}
return $this->_listeners;
}
/**
* Checks whether an event has any registered listeners.
*
* @param string $event
* @return boolean TRUE if the specified event has any listeners, FALSE otherwise.
*/
public function hasListeners($event)
{
return isset($this->_listeners[$event]) && $this->_listeners[$event];
}
/**
* Adds an event listener that listens on the specified events.
*
* @param string|array $events The event(s) to listen on.
* @param object $listener The listener object.
* @param integer $priority The higher this value, the earlier an event listener
* will be triggered in the chain. Defaults to 0.
*/
public function addEventListener($events, $listener, $priority = 0)
{
// Picks the hash code related to that listener
$hash = spl_object_hash($listener);
foreach ((array) $events as $event) {
if (!isset($this->_listeners[$event])) {
$this->_listeners[$event] = array();
$this->_priorities[$event] = array();
}
// Prevents duplicate listeners on same event (same instance only)
$this->_listeners[$event][$hash] = $listener;
$this->_priorities[$event][$hash] = $priority;
$this->_sorted[$event] = false;
}
}
/**
* Removes an event listener from the specified events.
*
* @param string|array $events
* @param object $listener
*/
public function removeEventListener($events, $listener)
{
// Picks the hash code related to that listener
$hash = spl_object_hash($listener);
foreach ((array) $events as $event) {
// Check if actually have this listener associated
if (isset($this->_listeners[$event][$hash])) {
unset($this->_listeners[$event][$hash]);
unset($this->_priorities[$event][$hash]);
}
}
}
/**
* Adds an EventSubscriber. The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param Doctrine\Common\EventSubscriber $subscriber The subscriber.
* @param integer $priority The higher this value, the earlier an event listener
* will be triggered in the chain. Defaults to 0.
*/
public function addEventSubscriber(EventSubscriber $subscriber, $priority = 0)
{
$this->addEventListener($subscriber->getSubscribedEvents(), $subscriber, $priority);
}
}
evm 0 0.00048770189285278
evm 1 0.00059050321578979
evm 2 0.00068733215332031
evm 3 0.00078628063201904
evm 4 0.00084335088729858
evm 5 0.00095219135284424
evm 6 0.0010335898399353
evm 7 0.0011562895774841
evm 8 0.0012545847892761
evm 9 0.001274037361145
evm 10 0.0014402270317078
evm 11 0.0014760065078735
evm 12 0.0012823748588562
evm 13 0.0012816596031189
evm 14 0.0015170240402222
evm 15 0.0014918971061707
evm 16 0.0016846013069153
evm 17 0.0019244766235352
evm 18 0.0020828008651733
evm 19 0.0019659638404846
evm 20 0.0020429754257202
evm 21 0.0021235156059265
evm 22 0.0019197344779968
evm 23 0.0022525453567505
evm 24 0.0020547080039978
nevm 0 0.0008044958114624
nevm 1 0.001168360710144
nevm 2 0.0015089774131775
nevm 3 0.0018552470207214
nevm 4 0.0020939707756042
nevm 5 0.002463595867157
nevm 6 0.0027560544013977
nevm 7 0.0031801915168762
nevm 8 0.003525767326355
nevm 9 0.003646252155304
nevm 10 0.0041954660415649
nevm 11 0.0043569731712341
nevm 12 0.0038117527961731
nevm 13 0.0038837623596191
nevm 14 0.0046020936965942
nevm 15 0.0045461463928223
nevm 16 0.0051713013648987
nevm 17 0.0059885501861572
nevm 18 0.0065212893486023
nevm 19 0.0061679720878601
nevm 20 0.0064382171630859
nevm 21 0.006737904548645
nevm 22 0.0060815143585205
nevm 23 0.0072019243240356
nevm 24 0.0067453527450562
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment