Twig: Suggestion: optimize use of getAttribute using additional syntax to use native code

Created on 21 Aug 2017  路  10Comments  路  Source: twigphp/Twig

Currently the use of . ends calling to getAttribute(), which is a function that's difficult to optimize.

There's an option when working with arrays to use [] that is more optimized as it ignores all the object calls, but it fails when working with more than one depth of array as a[b][c] calls recursively to getAttribute returning a lot of data, instead of using native php doing $context["a"]["b"]["c"];
I suggest to extend the syntax so it's possible to compile to native php using:

  • [] to access array variables
  • {} to access object properties (not sure about this one)
  • -> to access object methods.

This will result in a[b]{c}->getD compiling into $context["a"]["b"]->c->getD() instead of calling multiple times getAttribute

This should not suppose a problem of backward compatibility, as the use of [] is only for arrays, and you could keep calling with .

Most helpful comment

Optimizing this part of Twig is quite complex as Twig is very flexible.

I've done one optimization for array calls in #2618 though. I don't see any other obvious ones (but that's a start :)).

All 10 comments

Compiling to native PHP is hard, because it also looses the Twig error handling (and so breaks strict_variables for instance).

Note that the optimized path you describe is still handled by getAttribute even in the non nested case, but the type is set to Twig_Template::ARRAY_CALL.
Btw, for method calls you can also add braces after the name in the template, and it will mark the attribute access as Twig_Template::METHOD_CALL, which will skip the array access and the property checks, allowing only methods.

I understand what you mean, but it's a big problem of performance for production environments where you hardly are going to use strict_variables

I've done a performance test using . and [] syntax and there isn't a lot of difference.

Using an average api response array, i've test
Twig . syntax (41.8 ms)

{% for i in 1..10000 %}
    {% set testvar = a.b.c.d.e %}
{% endfor %}

using_dots
twig [] syntax (34.3 ms)

{% for i in 1..10000 %}
    {% set testvar = a[b][c][d][e] %}
{% endfor %}

using_array

twig custom function (1.58 ms + 2.18 ms in getFunction + 1.06 ms in getCallable)

{% for i in 1..10000 %}
    {% set testvar = get_array(a) %}
{% endfor %}
$this->getTwig()->addFunction(new \Twig_SimpleFunction('get_array', function ($array) {
    return $array['b']['c']['d']['e'];
}));

using_custom_function

empty twig template + php code in controller (209 碌s)
using_native_php_code

As you can see there is a big performance difference, it make me think that maybe having available strict_variables in production environments is not a good idea.

Anyway, maybe somebody has came with this problem before, is there any workaround to improve calls to getAttribute?

Thank you!

@inakivb can you share the profiles themselves (using the Blackfire sharing feature) rather than just giving screenshots ? It will allow me to see them more in details.

Hello @stof,
have you had time to take a look at this?
I've been working around with a custom function to solve the problem, focusing on performance. It works (it's about 25x faster than using twig array notation), but with the overhead of calling a user function, and with the readability problems. Anyway it still being much slower than calling the array in php.

Here is the custom function:

        $this->getTwig()->addFunction(new \Twig_SimpleFunction(
            'a',
            function($array,$a1='',$a2='',$a3=''){
                $arg_list = func_get_args();
                $numArgs = count($arg_list);
                switch($numArgs){
                    case 1:
                        return $array;
                        break;
                    case 2:
                        return $array[$a1];
                        break;
                    case 3:
                        return $array[$a1][$a2];
                        break;
                    default:
                        $array = $array[$a1][$a2][$a3];
                        for($i=4;$i<$numArgs;$i++){
                            $array=$array[$arg_list[$i]];
                        }
                        return $array;
                }
            }
        ));

Thank you!

Hello @stof .

We麓ve found the same issue. Do you have any update regarding this? Probably it can improve our performance a lot and the change seems obvious and @inakivb has provided several performance results.

Thanks in advance,

Optimizing this part of Twig is quite complex as Twig is very flexible.

I've done one optimization for array calls in #2618 though. I don't see any other obvious ones (but that's a start :)).

Closing for now as there is nothing more to do IMHO.

And just when I thought nothing could be more optimized, I've found something that optimizes the performance when calling a method by more than 20%. See #2635.

Of course there is more! #2636

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SDPrio picture SDPrio  路  3Comments

unique1984 picture unique1984  路  4Comments

yguedidi picture yguedidi  路  4Comments

xxfaxy picture xxfaxy  路  6Comments

rungta picture rungta  路  3Comments