I am facing more memory consumption in Php 7 compa

2019-03-12 07:17发布

问题:

When I was doing a benchmark, I found that PHP 7 was using more memory than PHP 5.6.

So, I did a test. I ran a script containing only:

  $a=10;

and below are the results for the memory used when I used PHP CLI without any modules (php -n)

php 5.6 = 222600 Bytes
php 7.0 = 350448 Bytes

* PHP 5.6.23 (cli) (built: Jun 22 2016 12:13:15)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies 

* PHP 7.0.9 (cli) (built: Jul 20 2016 10:47:41) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

Environment is

  • OS: window 10
  • Server : IIS (although I used the CLI, not the server), with fast cgi
  • machine : 64 bit
  • php-5.6.23-nts-Win32-VC11-x64
  • php-7.0.9-nts-Win32-VC14-x64

Can anyone explain why I got this result?


Additional Tests

Using this code, as suggested by @gordon,

$i=0;
while ($i++ < 100000) ;

php 5.6: 227408 bytes

php 7.0: 386640 bytes

I determined memory usage with this code:

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();

回答1:

To understand the answer to your question - you need understand how PHP5 and PHP7 allocs memory.

PHP5 allocating memory "By Request" assuming by it's Zend Engine structure.

In PHP7 it's some optimizations made at this side, so on memory allocating "by the chunks"

  • On start it allocates large chunk of memory
  • On in-app allocation it allocates small chunk to avoid fragmentation

This differences makes very good increasing of performance (because of engine don't need allocate memory on runtime every time you need it and save some time on fragmentation), but it increases memory consumption for "very small" programs, which size is below than "chunk size".

And yes, PHP7 saves memory very much on large programs.

You may view all this differences in pictures below:

Graphics builded with benchmark: 1.php

<?php

ini_set('memory_limit', '5G');
$a=range(1,$argv[1]);

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();
echo PHP_EOL;

bench.sh

// Small programs
(for i in $(seq 0 5 5000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m
(for i in $(seq 0 5 5000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m
//Large Programs
(for i in $(seq 0 50 100000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m    
(for i in $(seq 0 50 100000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m

octave drawer

php7;php7=ans;
php5;php5=ans;
plot(php5(:,5)',[php5(:,1:4)';php7(:,1:4)']');
legend("PHP5 mgu", "PHP5 rmu", "PHP5 rpmu", "PHP5 pmu","PHP7 mgu", "PHP7 rmu", "PHP7 rpmu", "PHP7 pmu");

Read more

  1. Official PHP7/PHP-NG presentation: https://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view
  2. Official PHP7/PHP-NG internal changes description: https://wiki.php.net/phpng-int
  3. Official extension migration guide: https://wiki.php.net/phpng-upgrading
  4. Good articles from @NikiC: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html and http://nikic.github.io/2015/06/19/Internal-value-representation-in-PHP-7-part-2
  5. PHP5 internal details: http://www.phpinternalsbook.com/
  6. Badoo PHP5->PHP7 success story with details: https://techblog.badoo.com/blog/2016/03/14/how-badoo-saved-one-million-dollars-switching-to-php7/


回答2:

Your tests show more memory usage in PHP 7.0 because the testing code is very simple.

PHP 7.0 is known to use less memory (and be faster) that PHP 5.6 due to a radical rewrite of the internal ZEND Engine (the interpreter core)

As Gordon commented most likely the new features and improvements in PHP 7.0 require a "bootstrap" that result in negative results when tested on small pieces of code.

Let's try it with something more complex: build an array of 10.000 integers then sort it using Quicksort algorythm.

Here is the result I get:

PHP 7.0

Memory Usage: 1432752
Real Memory Usage: 4194304
Real Peak Memory Usage: 4194304
Peak Memory Usage: 3152360


PHP 5.6

Memory Usage: 2756744
Real Memory Usage: 4980736
Real Peak Memory Usage: 6029312
Peak Memory Usage: 5710464

And still a simple 20 lines quicksort is way far from real world applications with thousands of lines of codes, many classes declarations, many instances...

I have run the test on http://phptester.net

Below is the code

<?php
function quick_sort($array)
{
    $length = count($array);
    $pivot = $array[0];
    $left = $right = array();
    for($i = 1; $i < count($array); $i++)
    {
        if($array[$i] < $pivot)
        {
            $left[] = $array[$i];
        }
        else
        {
            $right[] = $array[$i];
        }
    }
    return array_merge(quick_sort($left), array($pivot), quick_sort($right));
}

$unsorted = array();
for($i=0;$i<10000;$i++)
{
    $unsorted[] = rand(1,1000000);
}

$sorted = quick_sort($unsorted);

$lf = "<br/>";

echo $lf;
echo "Memory Usage: ".memory_get_usage();
echo $lf;
echo "Real Memory Usage: ".memory_get_usage(true);
echo $lf;
echo "Real Peak Memory Usage: ".memory_get_peak_usage(true);
echo $lf;
echo "Peak Memory Usage: ".memory_get_peak_usage();
echo $lf;

Credit for the quicksort algorythm in PHP: http://andrewbaxter.net/quicksort.php



回答3:

Upfront I want to say that if you see higher reported memory usage in PHP 7 on real code, the most likely cause is that PHP 7 will report memory usage of mysqlnd buffered queries as part of the memory usage. In PHP 5 this memory usage was not reported (but of course the memory was still used). For large queries this can make a very substantial difference.

Now to your actual case, which is basically the memory usage of PHP immediately after request startup. The answer by MobDev already explains why there is a discrepancy in "real" memory usage, which is the memory usage metric which reports how much memory PHP's allocator has requested from the system allocator of kernel. As MobDev points out PHP 7 will allocate memory in much larger chunks (2MB) and is also more aggressive about caching allocated chunks.

However this does not explain the discrepancy in "non-real" memory usage, which does not take these allocator details into account. It is easy to check whether exactly the memory goes by using a memory profiler, e.g. by running PHP through USE_ZEND_ALLOC=0 valgrind --tool=massif. The USE_ZEND_ALLOC=0 part instructs PHP to not use its own allocator.

First of all this will show you that the actual memory usage and the memory usage reported by PHP differ quite significantly. Massif will show 3.2MB usage for PHP 5.6 and 2.3MB for PHP 7. The reason is that PHP only reports memory that goes through it's own allocator (ZMM), while many structures that survive across multiple request are not allocated using it.

The largest allocations going through the system allocator (thus not reported in the memory usage) are:

                       | PHP 5.6 | PHP 7
interned string buffer | 1 MB    | 150 KB + strings
GC buffer              | 320 KB  | 320 KB
internal classes/funcs | >1.3 MB | >0.5 MB

The "internal classes/funcs" number is a crude lower bound, because there are many small allocations involved here which are hard to count. One main difference is visible, which is that PHP 7 does not use a fixed interned string buffer (the listed size is the for the hashtable buffer I'm seeing, which does not include the size of the strings themselves).

However, this still doesn't answer the question of the actually reported memory usage. In this case the largest allocations are:

             | PHP 5.6 | PHP 7
VM stack     | 130 KB  | 256 KB
Object store | 64 KB   | (8 KB)
CG arena     | ---     | 64 KB

There are a couple of differences here. The main one is that PHP 7 uses a larger VM page size (about twice as large). Additionally PHP 7 uses an arena to store certain structures (like user functions), which starts off with a default size of 64KB. On the other hand the size of the object store buffer is significantly smaller in PHP 7.

So essentially the TL;DR answer is that PHP 7 uses a larger VM stack page size.



回答4:

Php 5.6 requires less bytes as compared to Php 7.0.