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 .
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 %}

twig [] syntax (34.3 ms)
{% for i in 1..10000 %}
{% set testvar = a[b][c][d][e] %}
{% endfor %}

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'];
}));

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

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.
@stof here you can find the profiles
Twig . syntax
https://blackfire.io/profiles/c6eb236f-5a77-45b0-a6df-f1db7a4a7738/graph
Twig [] syntax
https://blackfire.io/profiles/0178abb7-535d-43d4-a703-8d9918fe4135/graph
Custom function
https://blackfire.io/profiles/1d3ba50b-557d-4ca3-9a58-1cc0164228bf/graph
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
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 :)).