php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #40809 Poor perfomance of ".="
Submitted: 2007-03-14 23:51 UTC Modified: 2007-03-20 06:48 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: thuejk at gmail dot com Assigned: dmitry
Status: Closed Package: Performance problem
PHP Version: 5.2.1 OS: Linux
Private report: No CVE-ID:
 [2007-03-14 23:51 UTC] thuejk at gmail dot com
Description:
------------
In some cases, the ".=" operator can have poor performance.

I have a production PHP program which a problem which I think is caused by this. Originally a task took 700 seconds to run. Some debug output found that a simple ".=" on a very long string took far too long. Tweaking the memory allocator to round up allocations to the nearest power of 2 decreased the run time to 60 seconds.

My tweak was to insert
    uint pow_2 = 4;
    while (pow_2 < size) {
        pow_2 *= 2;
    }
    size = pow_2;
into the top of _zend_mm_alloc_int() and _zend_mm_realloc_int()

(I think) ".=" uses the Zend/zend_operators.c:concat_function() function. This function will reallocate the string with the minimum length needed for each concatenation. My tweaked memory allocator will allocate extra space in advance, which means realloc() returns immediately, which seems to make the difference.

I have attached some code which reproduces the performance problem. I am actually not sure exactly what happens. The part for creating MM holes is taken from http://bugs.php.net/bug.php?id=40261 , so this may be the same bug, but I am not sure.

Run time of attached test case with and without my tweak:
t@t2 ~> time /usr/local/php-5.2.1/bin/php evil.php
/usr/local/php-5.2.1/bin/php evil.php  1,04s user 0,01s system 99% cpu 1,047 total
t@t2 ~> time /usr/local/php-5.2.1_clean/bin/php evil.php
/usr/local/php-5.2.1_clean/bin/php evil.php  12,30s user 0,02s system 99% cpu 12,323 total


Reproduce code:
---------------
<?php
error_reporting(E_ALL|E_STRICT);

$num_increments = 100;
$num_repeats = 1000;
$increment = 50;

/* Create some more holes to give the memory allocator something to
 * work with. */
$num = 5000;
$a = Array();
for ($i=0; $i<$num; $i++) {
  $a[$i] = Array(1);
}
for ($i=0; $i<$num; $i++) {
  $b[$i] = $a[$i][0];
}
unset($a);

for ($i=0;$i<$num_repeats;$i++) {
  $evil = "";
  for ($j=0;$j<$num_increments;$j++) {
    $evil .= str_repeat("a", $increment);
  }
  unset($evil);
}

?>



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-03-20 06:48 UTC] dmitry@php.net
Fixed together with #40261 in CVS HEAD and PHP_5_2.
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Fri Apr 18 13:02:15 2014 UTC