In theory, this article was supposed to be published in the “Linux for All” blog. However, this problem did not manifest itself in similar tests on python and perl, and therefore specifically dependent on PHP and the bitness of the platform. Since PHP is primarily associated with websites, then the blog is appropriate - hosting.Attention ! The following text contains
scenes of violence source code snippets and can inflict irreparable damage to your brain and destroy the faith in
humanity linux php.
It all started with a complaint that, under the 64-bit architecture, the following PHP script works disastrously slow:
<?php
$cnt=0;
while ($cnt++<5) {
echo '----- TEST #'.$cnt.'-----<br>';
echo 'start FOR & WHILE testing<br>';
$start = microtime(1);
for($i=0;$i<3000000;$i++) {}
$end = microtime(1);
$time = $end - $start;
echo $time.' for($i=0;$i<3000000;$i++) {}<br>';
$start = microtime(1);
$i=0; while($i++<3000001) {}
$end = microtime(1);
$time = $end - $start;
echo $time.' while($i++<3000001) {}<br>';
$start = microtime(1);
$i=0; while($i<3000000) {$i++;}
$end = microtime(1);
$time = $end - $start;
echo $time.' while($i<3000000) {$i++;}<br>';
$start = microtime(1);
$i = 0;
while ($i<3000000): $i++; endwhile;
$end = microtime(1);
$time = $end - $start;
echo $time.' while ($i<3000000): $i++; endwhile;<br>';
echo '<br><br>start a.=b & a=ab testing<br>';
$a = NULL;
$b = ' , 40';
$start = microtime(1);
for($i=0;$i<3000000;$i++) {$a = $a.$b;}
$end = microtime(1);
$time = $end - $start;
echo $time.' for($i=0;$i<3000000;$i++) {$a = $a.$b;}<br>';
$a = NULL;
$b = ' , 40';
$start = microtime(1);
for($i=0;$i<3000000;$i++) $a = $a.$b;
$end = microtime(1);
$time = $end - $start;
echo $time.' for($i=0;$i<3000000;$i++) $a = $a.$b;<br>';
$a = NULL;
$b = ' , 40';
$start = microtime(1);
for($i=0;$i<3000000;$i++) {$a .= $b;}
$end = microtime(1);
$time = $end - $start;
echo $time.' for($i=0;$i<3000000;$i++) {$a.=$b;}<br>';
$a = NULL;
$b = ' , 40';
$start = microtime(1);
for($i=0;$i<3000000;$i++) $a .= $b;
$end = microtime(1);
$time = $end - $start;
echo $time.' for($i=0;$i<3000000;$i++) $a .= $b;<br>';
$a = NULL;
$b = ' , 40';
$start = microtime(1);
$i=0; while($i++<3000001) {$a.= $b;}
$end = microtime(1);
$time = $end - $start;
echo $time.' while($i++<3000001) {$a.= $b;}<br>';
$a = NULL;
$b = ' , 40';
$start = microtime(1);
$i=0; while($i++<3000001) $a.= $b;
$end = microtime(1);
$time = $end - $start;
echo $time.' $i=0; while($i++<3000001) $a.= $b;<br>';
}
?>
')
Indeed, this problem is present in php5.2, which was tested on various distributions. Therefore - the problem was really related to the digit capacity. However, having experienced an analogue of similar code on other interpreters, it was found out that there is no such problem.
Respectively - it was exclusively a problem of PHP itself, which showed itself on 64 bits. But why so much a thousand devils?
It was launched from strace, which on i386, PAE and x86_64 produced different values:
- i386 mmap
- PAE mremap (which is logical, this is mmap with flags)
- x86_64 brk and mmap (but why brk?)
What is this brk, why does it affect the speed of php and how to avoid such situations?
First of all, I note that the picture to the topic is clickable and leads to a fundamental article that reveals all the details of working with memory. However, it is very complex and in English. In short, brk allows you to change the size of the allocated memory instead of biting a piece through mmap, which reduces its fragmentation.
But as you can see, this operation is very costly, and a reduction in the memory used causes such a large overhead projector that it is better to neglect it. Especially when it comes to hayload - the speed of response is much more important than saving memory.
So why on i386 we only use mmap, while brk gets on x86_64? To answer this question, we refer to the original source -
malloc :
Normally, malloc() allocates memory from the heap, and adjusts the size of the
heap as required, using sbrk(2). When allocating blocks of memory larger than
MMAP_THRESHOLD bytes, the glibc malloc() implementation allocates the memory
as a private anonymous mapping using mmap(2). MMAP_THRESHOLD is 128 kB by
default, but is adjustable using mallopt(3).
Now everything fell into place - for x86_64 MMAP_THRESHOLD was increased, which led to such an interesting result, apparently related to a very clever garbage collector in PHP, which in this case outwitted itself.
And in order to return the call to malloc as in i386, you need to return the old values. You can change this globally by collecting glibc, which is undesirable or selectively for the process, specifying the old value in the kernel variables (if of course glibc is built with the support of this option):
export MALLOC_MMAP_THRESHOLD_ = 131072If you insert this magic line in the apache init script, you can get a boost in speed and a higher memory consumption. Of course, this is relevant not only for php, but possibly for many other programs, when developers calling malloc meant mmap.
PS Test is better to run from the console.
strace php -f qq.php will give one picture
MALLOC_MMAP_THRESHOLD_ = 131072 strace php -f qq.php will give a completely different, of course, if you have 64 bits.