Home > Development, Development projects, English, php > PHP’s call_user_func_array() is slow

PHP’s call_user_func_array() is slow

I just solved an issue that had me waking up in the middle of the night for weeks (just pushing it a bit) and I have to thank PHP’s community and in particular a guy named Brad Proctor for writing a very short but very important comment in the PHP manual, and I quote:

This function is relatively slow (as of PHP 5.3.3) and if you are calling a method with a known number of parameters it is much faster to call it this way:

$class->{$method}($param1, $param2);

He mentions it for PHP 5.3.3, but this is true for PHP 5.3.10 and probably for 5.4 (haven’t tested that one yet).

And indeed, it might be a bit difficult to catch this, but we developed a migration system from external databases to Chamilo LMS, and we wanted to make it very flexible, so what we did was build a configuration file (a sort of dictionary) for which content of which tables went into which other tables. In doing this, we also indicated a “converter function” for each type of data.

So the script being kind of generic and all, th easiest way to call the right function when desired was to simply use call_user_func_array().

Everything went well for the first tables. That’s when we came to one big table (6 million rows) and the migration started to take a very long time. It still took about 0.3s per row, but multiply this by 6 million and you get about 55 days of migration script running without a pause. That’s really difficult to deal with. More even when you hav to deliver within 28 days.

Now the problem is that finding it is a little bit tricky, because putting some pofiling mechanism in place will obviously slow down the operation, and because there is a considerable amount of “previous data” that needs to be inserted before we get to the real problem (thus making us wait for almost hours before seeing the right profiling results).

It is even more tricky when seeing that the real time loss is occurring between the end of the function’s execution and the handling over of the control to the calling script/function…

So the process would be something like this:

0.5000s  Calling call_user_func_array(‘function_a’,$params); from general context

0.5001s  Inside function_a()

0.5011s  Executed something relatively complex inside function_a()

0.5012s Ending function_a()

0.8315s Next line of call_user_func_array() call in general context

That’s right, 0.33s lost just to recover the general context (in my case). That means you’ll loose that amount of time each time you call call_user_func_array().

A good reason to avoid it, just by creating a switch() {…} on the function name, for example.

By calling the function directly (within a switch), the results have been speeded up to something like this:

0.5000s  Calling function_a($params); from general context

0.5001s  Inside function_a()

0.5011s  Executed something relatively complex inside function_a()

0.5012s Ending function_a()

0.5014s Next line of call_user_func_array() call in general context

So, in short, if you can avoid it, avoid using call_user_func_array() at all cost!

Update: thanks to @marvil07 for pointing me towards the same kind of discussion on call_user_func_array() in Drupal. Apparently, they came to the same conclusion that it should be avoided, if at all possible.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: