48 comments

[ 3.2 ms ] story [ 97.7 ms ] thread
this is interesting, I'd like to see a comparison with foreach vs. for too - just out of curiousity :) Also the $var == "" kind of suprised me, didn't know it was that bad, although I guess this does rarely happen since most probably use empty anyway.
The $var == "" issue is more understandable when you realize that PHP attempts to do type coercion to test comparison for all data types, while the equality operator (triple equals, ===) does not attempt to do type coercion.

Also, foreach(range(1, 1000 * 1000) as $i) will execute at roughly the same speed as for($i = 0, $count = 1000 * 1000; $i < $count; $i++), so the difference is negligible.

What am I supposed to learn from these numbers? They are interesting to look at, but I don't have enough information. Exactly what version of php is running, and how is it configured? What does the "Index" field signify? Where can I find the code?

While fun to look at and calculate, it also doesn't mean a lot for most applications. I would argue that data fetching from DBs or caches will be the majority of execution time.

>I would argue that data fetching from DBs or caches will be the majority of execution time.

People keep saying that, and I would like to see some numbers for that also.

The DB dataset could be already cached in memory, for example.

Database access latency will always trump any language design issues, except in extreme cases. This article does make some of those cases obvious, especially since they are easily-overlooked programming mistakes.
Database access latency will always be an important factor of _latency_ .

That doesn't prevent anyone from coding stuff that requires 20 times less application servers while using the same amount of database (+caching) servers.

The very idea that because the DB call takes a while to complete, we can waste even more CPU cycles is a complete misunderstanding of real world use cases.

But then, the very use of PHP means that you don't give a shit about execution time or server costs at all.

Large web apps do often spend a lot of time manipulating strings and arrays in memory. It's absolutely true and I've seen it myself several times -- a performance penalty that is measurable and noticeable to users.

But that pales in comparison to the overhead of a Database call, or even a call to Redis or Memcached.

I've read (from sources I trust) that a database call on a modern stack like .Net or JVM is 4 orders of magnitude slower than a local method call. Think about that. So if the time you spend brushing your teeth is a local method call, the entire next day is a database query.

I can't remember the source here but the information is available with a few minutes of coding or googling.

And to the caching point -- having data cached locally in memory is great, but you can't trust that.

> And to the caching point -- having data cached locally in memory is great, but you can't trust that.

You can if you design around handling a possibly-stale cache.

Not really. There is no system that ever has had an always-fresh cache.

Are you disputing the premise or just disagreeing for the sake of it?

I'm saying that having a stale cache won't be a problem for an application that is designed around the fact that you will occasionally have stale data.
I'd love to see a benchmark of:

    for ($i = 0; $i < count($users); $i++)
vs

    foreach($users as $user)
There are some similar benchmark here: http://www.phpbench.com/

Intuitively the for loop should be a lot fast as it isn't assigning anything each loop. Although in your example it may be slower because the count($users) would be ran on each iteration of the loop.

It only assigns $user in a foreach when the $user is modified, or if you are trying to outsmart PHP-core and use & to pass it as a reference.
I get what you mean, but it might be helpful to translate it for everybody else:

Foreach is doing an assignment in each iteration, robryan is right. But PHP variables are copy-on-write so $user is, until modified, just an entry in the symbol table which is very fast.

And that for() loop also has an assignment -- the incrementer.

I just coded up a benchmark and posted the ProTip on coderwall.

http://coderwall.com/p/il1tog

You're re-calculating the count() on each iteration. Of course it's slower.

Fix that:

  for($i = 0; $count = count($elements); $i < $count; $i++) { }
Yeah I know, done purposefully to see how MUCH slower this is. This is how the majority of code is written in the wild, rarely do you see the `count()` pulled outside of the `for()`.
This is simply unfair, should the language maintainers be held accountable for user behaviour? A more interesting and fairer topic is whether PHPs count() is considerably slower than python's len()
This chart is also useful to find different ways to do the same thing, so you can choose to use what is more readable or functions you didn't know about.

    My conclusion: Never use the ereg…() functions.
Well, duh. It's deprecated.
It wasn't in 5.2 which these tests are based on. Basically someone linked benchmarks done several years ago.
I got excited at first because I thought it was a standalone script that benchmarked a server. Does anybody know of a script that will score a server based on cpu/io/memory/etc? Would be a great way to see if a server is properly configured or what kind of performance to expect from a cloud solution.
The difference between php-5.2 (the version used), php-5.3, and php-5.4 is quite vast for each version, especially since php-5.4 basically cut memory usage/execution time by a third at worst (or directly by half at best) when compared to php-5.3.

This is purely anecdotal but I use PHP for large scripts doing lots of inserts/deletes (>100,000) from databases and relatively simple logic.

Nice, I'll have to give 5.4 a try. Currently using 5.3, and finding it memory hogging when doing a lot of database operations in a row (300k+).
Depending on the ORM (or otherwise) it may have some level of caching, so the best performance would be using the PHP functions/classes directly (eg: PDO/MongoCollection/Redis).

In addition to the above, PHP extensions would be extremely preferable to pure-PHP libraries for database interaction simply because the extensions will be written in C and lack the overhead of the language.

Sadly, PHP 5.4 stills fails badly with APC in several cases, so I do not consider it ready for production yet
Do you have any links to the bugs? We use 5.4 in production and haven't had any issues with APC, so I'm curious if we're missing anything relevant to our use case.
> Sadly, PHP 5.4 stills fails badly with APC in several cases, so I do not consider it ready for production yet

You're using outdated APC code. Update to the latest, it resolves issues with the 5.4 line. We use it in production, zero issues.

"PHP version 5.2.13 is running on this server" :/
This article is 2 years old.
(comment deleted)
The bad performance of count() and the lack of difference in performance between single and double quotes came as a surprise!
If I were writing a PHP parser, then I would convert double-quoted strings of the form "foo $bar baz" to 'foo '.$bar.' baz' when the script is first parsed. So executing the string concatenation in a loop would have no performance difference, because the string is only parsed the first time.

That's just a guess, though, having not actually looked at the code myself.

Does "ms" mean milliseconds or microseconds? And are these times for one iteration of the code fragment?

2 milliseconds to compare two strings seems awfully slow.

Definitely microseconds, most of my scripts with database interaction and all fully complete in around 50 milliseconds which would be impossible if string comparison took 2 milliseconds.
When you do a benchmark you usually run the code more than once. It's milliseconds. The real question is how many time the code loop.

But anyway the interesting number is how many times it's slower compared to an equivalent code.

I got this site bookmarked from a long time ago so I do not know what is the PHP version tested. Adding it because it seems to contain quite alot of different tests.

http://net-beta.net/ubench/

According to the request headers it's using php-5.3.13 (released May 2012).
for ($i = 0; $i < count($users); $i++) is moronic

person who wrote this should be fired

Two things caught me by surprise:

    for ($i = 0; $i < count($array); $i++)
I thought PHP would be smart enough to optimise this.

    'contains no dollar signs'
I practised the single vs. double quotes thing religiosity. I'm kind of embarrassed it doesn't actually make a difference.
In general you can't hoist the length check out of the loop because of the possibility that the length might change during the loop. Proving that the length doesn't change during the loop is quite difficult; in general this requires alias analysis.
"I thought PHP would be smart enough to optimise this."

How? This is not a trivial optimization for a compiler.

You can't trust benchmarks, unless you've either seen the code or trust the person posting the benchmark enough that he benched correctly. It's easy to benchmark something other than what you think you are benchmarking.
Two wrong conclusions: === is infinitely better than empty() due to less glitchy behavior. don't use empty, ever.

Numeric arrays in PHP are AS SLOW as associative array, that's pure fail and one of the reasons PHP::fannkuch is so slow.

They're implemented using the same data structure and given the language semantics (keys can be any scalar type intermingled horribly), how would you improve that?

   My conclusion: count() is horribly slow. Always precalculate it, if possible.
https://github.com/php/php-src/blob/master/ext/standard/arra...

In PHP they actually check the length of an array at every invocation of count(). As far as I can see the numeric value for length isn't stored anywhere.

In default, non recursive mode, it's only returning the value of zval->nNumOfElements (with a few indirections).

It's not count() that's benchmarked here, its the difference between "$a < $b" and "$a < function_call($b)".