Possible events performance optimizations

5 years ago

Jan Tvrdík
Nette guru | 2553
+
+2
-

Events in Nette are not slow, but they could be faster. The most expensive part of event invocation (assuming ac853ac will get merged) is ObjectMixin::hasProperty (more than 50 % in the worst case scenario, which is also the most-common scenario in Nette itself).

The problem is that even though the caller (e.g. Nette\Application\Application) knows that he's calling event stored in public property, ObjectMixin does not know that and therefore has to use reflection to verify that.

There are two possible optimization that crossed my mind:

1) Optimize the worst case / most-common scenario

Instead of $this->onClick(...) use $this->onClick && $this->onClick(...).

2) Add new method for fast event call

public static function fireEvent($event, $args)
{
    if (is_array($event) || $event instanceof \Traversable) {
        foreach ($event as $handler) {
            Callback::invokeArgs($handler, $args);
        }
    }
}

And use

WhatEver::fireEvent($this->onClick, ...);

Does this make sense to you or do you consider current event implementation fast enough?

Last edited by Jan Tvrdík (2014-11-10 17:04)

5 years ago

bazo
Member | 625
+
+1
-

nette is kinda slow in some areas, so everything that makes it faster is welcome.

i would completely disable any checks for callback methods existence and just fire them. if they're not callable it would crash either way

5 years ago

newPOPE
Member | 659
+
0
-

I agree with improvement of events. I think that events is “the right way” :).

5 years ago

stekycz
Member | 161
+
0
-

It make sense to me. However what is real speedup of both possibilities?

I like the second possibility because the property with event is written only once and does not need some magic around calling missing method with the same name as array property.

5 years ago

Jan Tvrdík
Nette guru | 2553
+
0
-

Currently (assuming ac853ac will get merged) 100 worst-case (unique) event calls takes about 1 ms.

Speedup #1 – check for empty is about 10-times faster in worst-case scenario and makes no difference in base-case scenario.

Speedup #2 – fast event call (WhatEver::fireEvent) is about 10-times faster in worst-case scenario and about 5-times faster in best-case scenario.

Last edited by Jan Tvrdík (2014-11-10 18:19)

5 years ago

David Grudl
Nette Core | 6849
+
0
-

Invoking and checking callbacks can be changed (with (nearly) no BC break) this way

from:

public static function invokeArgs($callable, array $args = array())
{
    self::check($callable);
    return call_user_func_array($callable, $args);
}

to:

public static function invokeArgs($callable, array $args = array())
{
    if (($res = call_user_func_array($callable, $args)) === FALSE) {
        self::check($callable);
    }
    return $res;
}

But without measurement I can not guess that the latter will be faster.

5 years ago

Jan Tvrdík
Nette guru | 2553
+
0
-

@DavidGrudl Yes, that is faster. Assuming one listener per event it makes every event call about 10 % faster in best-case scenario and about 5 % faster in worst-case scenario*.

With fast-event call is difference even bigger.

*Therefore it is much less significant than not using preg_match =)

Last edited by Jan Tvrdík (2014-11-10 19:20)