PHP Benchmark tests

Moderatori: agvozden, Blagota

PHP Benchmark tests

Postod agvozden na 21 Maj 2008 08:29

NOTE You must keep in mind to refresh this page a few times to "catch" the right result. The numbers change sometimes drastically during each refresh. I assume that this is because of PHP's memory garbage collector that drops in randomly and also other processes that run on this machine have an influence.
Test:
READ LOOP: foreach() vs. while(list()=each())
What is the best way to loop a hash array?
Given is a Hash array with 100 elements, 24byte key and 10k data per entry
I've chosen the large data amount to try out what happens if I reference the data with the &-ref-operator (to avoid copying). But to my surprise the loops are never faster! In tests 5 and 6 are even 10x - 30x slower !! The larger the data entrys are the slower the tests 5 and 6 get! Copying seams always faster then using the &-ref-operator.
Way ???
Let me know at bs_php@users.sourceforge.net
+ 100 % 1: foreach($aHash as $val); Total time: 0[ms]
+ 1236 % 2: while(list(,$val) = each($aHash)); Total time: 2[ms]
+ 3979 % 3: foreach($aHash as $key=>$val); Total time: 5[ms]
+ 4793 % 4: while(list($key,$val)= each($aHash)); Total time: 6[ms]
+ 3942 % 5: foreach($aHash as $key=>$val) $tmp[] = &$aHash[$key]; Total time: 5[ms]
+ 4346 % 6: while(list($key) = each($aHash)) $tmp[]=&$aHash[$key]; Total time: 6[ms]
+ 695 % 7: Get key-/ value-array: foreach($aHash as $key[]=>$val[]); Total time: 1[ms]
+ 681 % 8: Get key-/ value-array: array_keys() / array_values() Total time: 1[ms]
+ 717 % 9: STRANGE: This is the fasetest code when using the the &-ref-operator (to avoid copying)
$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++) $tmp[] = &$aHash[$key[$i]]; Total time: 1[ms]
Conclusion:
It must have something to do with PHP4 variable ref-count So you can safely use foreach and only use the &-ref-operator when realy needed OR (according to the link above) when passing objects to functions. (Thanx to Wayne for his help)

Test:
MODIFY LOOP: foreach() vs. while(list()=each())
While the above test only reads and copies the data the question arised what would happen if I modify each value of the hash above.
Again I an unexpected result. Even if I reduce the data size to 100 byte p. e. it ends up that Nr.3 is 1.5 - 2x faster.
+ 549 % 1: foreach($aHash as $key=>$val) $aHash[$key] .= "a"; Total time: 5[ms]
+ 154 % 2: while(list($key) = each($aHash)) $aHash[$key] .= "a"; Total time: 1[ms]
+ 100 % 3: STRANGE: This is the fasetest code :
$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++) $aHash[$key[$i]] .= "a"; Total time: 1[ms]
Conclusion:
Use foreach unless the hash is lage AND has lage data elements. In that case use variation Nr.3 .

Test:
For-loop test
Is it worth the effort to calculate the length of the loop in advance?
E.g. "for ($i=0; $i<$size; $i++)" instead of "for ($i=0; $i<sizeOf($x); $i++)"
+ 100 % 1: With pre calc Total time: 1[ms]
+ 2106 % 2: Without pre calc Total time: 23[ms]
Conclusion:
The test above speeks for it self. Always calculate the length of the loop in advance!

Test:
Using the &-ref-operator as so called "alias"
Is a good idea to use the &-ref-operator to substitute (or alias) a complex mutidim-array? . Call 1'000x
E.g. $person = &$aHach["country"]["zip"]["streat"]["number"]["name"]
+ 110 % 1: NO Aliasing. Using: $aSingleDimArray[$i] Total time: 1[ms]
+ 100 % 2: Aliasing. Using: $alias = &$aSingleDimArray[$i] Total time: 1[ms]
+ 176 % 3: NO Aliasing. Using: $aMultiDimArray[$i]["aaaaa"]["aaaaaaaaaa"] Total time: 2[ms]
+ 121 % 4: Aliasing. Using: $alias = &$aMultiDimArray[$i]["aaaaa"]["aaaaaaaaaa"] Total time: 1[ms]
+ 287 % 5: NO Aliasing. Using: veryMultiDimArray[$i]["a"]["aa"]["aaa"]["aaaa"]["aaaaa"] Total time: 3[ms]
+ 148 % 6: Aliasing. Using: $alias = &$veryMultiDimArray[$i]["a"]["aa"]["aaa"]["aaaa"]["aaaaa"] Total time: 1[ms]
Conclusion:
It seams to be ok to use aliases. It also makes the code more readabel. But I was expecting to get a lager performance gain; especially with very multdimetional arrays.

Test:
$obj = new SomeClass() vs. $obj =& new SomeClass() using the =&-ref-operator
Is a good idea to use the =&-ref-operator when creating a new object? Call 1'000x
+ 101 % 1: $obj = new SomeClass() Total time: 1[ms]
+ 100 % 2: $obj =& new SomeClass() Total time: 1[ms]
+ 1214 % 3: $obj =& $someClass->f(); Total time: 17[ms]
+ 135 % 4: $obj = $someClass->f(); Total time: 2[ms]
Conclusion:
There seams to be no difference in performance.

Test:
double (") vs. single (') quotes
Is a there a difference in using double (") and single (') quotes for strings. Call 1'000x
+ 103 % 1: single (') quotes. Just an empty string: $tmp[] = ''; Total time: 0[ms]
+ 100 % 2: double (") quotes. Just an empty string: $tmp[] = ""; Total time: 0[ms]
+ 101 % 3: single (') quotes. 20 bytes Text : $tmp[] = 'aaaaaaaaaaaaaaaaaaaa'; Total time: 0[ms]
+ 100 % 4: double (") quotes. 20 bytes Text : $tmp[] = "aaaaaaaaaaaaaaaaaaaa"; Total time: 0[ms]
+ 102 % 5: single (') quotes. 20 bytes Text and 3x a $ : $tmp[] = 'aa $ aaaa $ aaaa $ a'; Total time: 0[ms]
+ 456 % 6: double (") quotes. 20 bytes Text and 3x a $ : $tmp[] = "aa $ aaaa $ aaaa $ a"; Total time: 2[ms]
+ 103 % 7: double (") quotes. 20 bytes Text and 3x a \$ : $tmp[] = "aa \$ aaaa \$ aaaa \$ a"; Total time: 0[ms]
Conclusion:
Single and double quoted strings behave almost the same with one exception: Don't use the a lonely ($) in double quoted string unless you want to reference a PHP-var; or use (\$).

Test:
isSet() vs. empty() vs. is_array()
What is the performance of isSet() and empty(). Call 2'000x
+ 138 % 1: isSet() with var that was set Total time: 1[ms]
+ 122 % 2: empty() with var that was set Total time: 1[ms]
+ 110 % 3: isSet() with var that was *not* set Total time: 0[ms]
+ 113 % 4: empty() with var that was *not* set Total time: 0[ms]
+ 108 % 5: isSet() with array-var that was set Total time: 0[ms]
+ 106 % 6: empty() with array-var that was set Total time: 0[ms]
+ 101 % 7: isSet() with array-var that was *not* set Total time: 0[ms]
+ 100 % 8: empty() with array-var that was *not* set Total time: 0[ms]
+ 221 % 9: is_array() of an array Total time: 1[ms]
+ 209 % 10: is_array() of a string Total time: 1[ms]
+ 5953 % 11: is_array() of a non set value Total time: 25[ms]
+ 122 % 12: isSet() AND is_array() of a non set value Total time: 1[ms]
Conclusion:
isSet() and empty() are identical. Interesting that a is_array() on a unset val is 3x slower. So alway check if val is set at all befor using type-checking. E.g. if (isSet($foo) AND is_array($foo))

Test:
switch/case vs. if/elseif
Is a there a difference between switch and if elseif. Call 1'000x
+ 132 % 1: if and elseif (using ==) Total time: 1[ms]
+ 100 % 2: if and elseif (using ===) Total time: 0[ms]
+ 143 % 3: case Total time: 1[ms]
Conclusion:
Using a switch/case or if/elseif is almost the same. Note that the test is unsing === and is slitly faster then using ==.

http://www.blueshoes.org/en/developer/php_bench/
agvozden

 
Postovi: 37
Pridružio se: 16 Dec 2007 13:00

SQL Query Caching

Postod bgsvetionik na 12 Jan 2009 16:56

Intended Audience
This tutorial is intended for the PHP programmer interested in caching SQL queries to reduce the overhead of a database connection and query, and to improve script performance.
Overview
Many sites use a database backend as a data store for the site. Whether the database contains product information, category structure, articles or a guest book, some of the data is likely to be quite static and will greatly benefit from a caching system.

Such a system would cache the results of an SQL query into a file stored on the system and hence improve the response time by avoiding the need to make a database connection, forming the query, executing it and retrieving the results.

On systems where the database does not reside on the same machine as the web server and requires a remote connection (TCP or similar), or where large amounts of data are retrieved from the database, you stand to gain even more in terms of response times and resources used.
Prerequisites
This tutorial will use MySQL as the database. You will need MySQL installed (available from www.mysql.com) and PHP MySQL extension enabled (it is enabled by default).

You will need to know the basics of the SQL (Structured Query Language) in order to query the database.
Caching SQL Query Results
Why cache query results?
Caching query results can dramatically improve script execution time and resource requirements.

Caching SQL results also allows you to carry out post processing on the data. This may not be possible if you use file caching to cache the outputs of the entire script (HTML output caching).

When you execute an SQL query, the typical process undertaken is:

* Connect to the database
* Prepare SQL query
* Send query to database
* Retrieve the results
* Close the database connection

The above is quite resource intensive and can adversely affect the script performance. This can be further compounded by factors such as amount of data retrieved and location of database server.

Although persistent connections may improve the overhead of connecting to the database, they are more memory intensive and the overall time saved will be very little if a large amount of data is retrieved.
Creating an SQL Query
SQL (Structured Query Language) queries are used as an interface to manipulate a database and its contents. SQL can be used to define and edit the table structure, insert data into the tables, update and delete information from the tables.

SQL is the language used to communicate with the database and in most PHP database extensions (MySQL, ODBC, Oracle etc), the extension manages the process of passing the SQL query to the database.

In this tutorial, only the select statement is used to retrieve data from the database. This data is cached and later used as the data source.
Deciding when to update the cache
Caching can take a few forms according to the program's needs. The 3 most common approaches are:

* Time triggered caching (expiry timestamp).
* Information change triggered caching (sensing data has changed and updating the cache accordingly).
* Manual triggered caching (manually letting the system know information is outdated and forcing a new cache generation).

Your caching requirements may be one or a combination of the mechanisms above. In this tutorial, the time-triggered approach is discussed. However, a combination of all 3 approaches can be used as an overall caching policy.
Caching the results
The basics to caching is using the serialize() and unserialize() PHP functions.

The serialize() function can be used to store PHP values without losing their types and structure. In fact, the PHP session extension uses the serialized representation of the variables in a file to store the contents of the session variable ($_SESSION).

The unserialize() function reverses the operation and turns the serialized string back into its original structure and data contents.

In this example, an e-commerce store is used. The store has 2 basic tables, categories and products. While product information may change daily, categories remain fairly static.

For product display, you can use an output caching script to store the resultant HTML output in a file to be called up. However, categories may need some post processing. For example, all categories are displayed and according to the category_id variable that is passed to the script ($_REQUEST['category_id']) you may wish to highlight the current category selected.

The categories table has the following format:

+----------------------+------------------+-----+----------------+
| Field | Type | Key | Extra |
+----------------------+------------------+-----+----------------+
| category_id | int(10) unsigned | PRI | auto_increment |
| category_name | varchar(255) | | |
| category_description | text | | |
+----------------------+------------------+-----+----------------+


In this example, the time limited caching technique is used where the cached SQL output is considered outdated after a set amount of time. In this particular example, 24 hours are used.

Serialize example:

* Connect to database
* Execute query
* Get all results into an array so you can access them later.
* Serialize array
* Save serialized array to file

Kod: Obeleži sve
$file = 'sql_cache.txt';
$link = mysql_connect('localhost','username','password')
    or die (mysql_error());
mysql_select_db('shop')
    or die (mysql_error());
/* form SQL query */
$query = "SELECT * FROM categories";
$result = mysql_query($query)
    or die (mysql_error());
while ($record = mysql_fetch_array($result) ) {
    $records[] = $record;
}
$OUTPUT = serialize($records);
$fp = fopen($file,"w"); // open file with Write permission
fputs($fp, $OUTPUT);
fclose($fp);

Looking at the sql_cache.txt file, it may look something like this:

a:1:{i:0;a:6:{i:0;s:1:"1";s:11:"category_id";s:1:"1";i:1;s:9:"Computers";s:13:"category_name";s:9:
"Computers" ;i:2;s:25:"Description for computers";s:20:"category_description"
;s:25:"Description for computers";}}


This output is the internal representation of the variables and their types. In this case you are using mysql_fetch_array() that returns both numeric indexed array and an associative array (which is why the data seems to occur twice – once with the numeric index and once with the string index).
Using the Cache
In order to use the cache, you will need to unserialize() the information back into the original format.

You can read the contents of the sql_cache.txt file into a variable using the file_get_contents() function.

Please note: This function is available in PHP version 4.3.0 and above only. If you are using an older version of PHP, a simple workaround is using the file() function (reads an entire file into an array, each new line becomes an array entry). The implode() function is used to join the array elements into one string to unserialize().

// file_get_contents() workaround for PHP < 4.3.0
$file = 'sql_cache.txt';
$records = unserialize(implode('',file($file)));
You are now able to go through the $records array and get the data from the original query:

foreach ($records as $id=>$row) {
print $row['category_name']."<br>";
}
Note that the $records array is an array of arrays (a numeric indexed array containing the query results – each row being a numeric and string indexed array... what a mouthful).
Putting it all together
The decision whether to cache is time based in this instance. If the file modification timestamp is greater than the current time less the expiration time set, the cache is used, else the cache is updated.

* Check file exists AND timestamp is less than expiry time set.
* Get the records stored in the cache file or update the cache file.

Kod: Obeleži sve
$file = 'sql_cache.txt';
$expire = 86400; // 24 hours (in seconds)
if (file_exists($file) &&
    filemtime($file) > (time() - $expire)) {
    // Get the records stored in cache
    $records = unserialize(file_get_contents($file));
} else {
    // Create the cache using serialize() function
}
Possible Additions

* Storing cache results in shared memory for faster retrieval.
* Adding a function that runs the SQL query randomly and checks if output is the same as cached output, if not, the cache is updated (this function can be given the probability of running once in every 100 script executions). Using a hashing algorithm (such as MD5()) can assist in determining if the contents of a string or file have changed.
* Adding an administrative function that manually deletes the file, hence forcing the cache to update (as the file_exists() function above would return false). You can use the unlink() function to delete the file.

The Script

Kod: Obeleži sve
$file = 'sql_cache.txt';
$expire = 86400; // 24 hours
if (file_exists($file) &&
    filemtime($file) > (time() - $expire)) {
    $records = unserialize(file_get_contents($file));
} else {
    $link = mysql_connect('localhost','username','password')
        or die (mysql_error());
    mysql_select_db('shop')
        or die (mysql_error());
    /* form SQL query */
    $query = "SELECT * FROM categories";
    $result = mysql_query($query)
        or die (mysql_error());
    while ($record = mysql_fetch_array($result) ) {
        $records[] = $record;
    }
    $OUTPUT = serialize($records);
    $fp = fopen($file,"w");
    fputs($fp, $OUTPUT);
    fclose($fp);
} // end else

// Query results are in $records array
foreach ($records as $id=>$row) {
    if ($row['category_id'] == $_REQUEST['category_id']) {
        // category selected - print bold
        print '<B>'.$row['category_name'].'</B><br>';
    } else {
        // other category - print regular
        print $row['category_name'].'<br>';
    }
} // end foreach


About the Author
Ori Staub is a senior systems analyst, developer and consultant specializing in web-based solutions. He can be contacted at os@zucker-staub.com


http://devzone.zend.com/node/view/id/1258
bgsvetionik
Site Admin
 
Postovi: 46
Pridružio se: 16 Dec 2007 12:15


Povratak na PHP

Ko je OnLine

Korisnici koji su trenutno na forumu: Nema registrovanih korisnika i 0 gostiju

cron