📜 ⬆️ ⬇️

How much memory does objects consume in PHP and is it worth using the 64-bit version?



This post was inspired by a study of memory consumption for my current big project at ZendFramework. As usual, according to the research, I was shocked by our programmer arrogance, which is often present when we write something big in PHP. And, probably, not only in PHP.

But first things first.
')
This article is a logical continuation of the following articles:


How are we going to measure


To begin with, we will define how we will measure “weight”. Here is the template:
$startMemory = 0; $startMemory = memory_get_usage(); //  echo (memory_get_usage() - $startMemory) . ' bytes' . PHP_EOL; 

This pattern is suitable for measuring the new allocated memory, that is, memory for variables. But to measure how many definitions eat, that is, descriptions of functions and classes, such an approach is impossible, since they are stored in memory before the script starts. To measure definitions, use the following template:
 $startMemory = 0; $startMemory = memory_get_usage(); //  include $testfile; echo (memory_get_usage() - $startMemory - $include_overhead) . ' bytes' . PHP_EOL; 

Where $ include_overhead is how much is the include statement to fit its internal needs. In this article we will not study how we can measure $ include_overhead. I will note only that the size of the devoured memory depends on 3 things:

If anyone is interested in understanding this more deeply, then you can examine the file run.include-test.php , it very well illustrates the unevenness of devouring memory with include. Also note that in all tests below we measure $ include_overhead approximately, because we need not exact values, but a trend and differences between the 32-bit and 64-bit versions.

How much do "objects" weigh


So TestSuite was written to automatically run a large number of tests. All tests were run in VirtualBox for Ubuntu 12.04.1 LTS i386 and Ubuntu 12.04.1 LTS amd64 . PHP version - 5.3.10, ZendFramework - 1.11.11. The command to run in the console:
 php run.testsuite-without-accelerator.php 
Additionally, I did a test on my machine with Gentoo amd64 for control. PHP accelerators do not work when started from the console. Here are the results:
Test nameDescriptionUbuntu x86
PHP 5.3.10,
ZF 1.11.11
Ubuntu x86-64
PHP 5.3.10,
ZF 1.11.11
Gentoo x86-64
PHP 5.3.15,
ZF 1.11.4
a.mention_variableVariable reference448048
a.new_null_variableCreating a new variable with a null value108208144
a.unset_null_variableDeleting variable-108-208-144
stdClass.newObject creation120232168
stdClass.tovar1Creating an object and linking $ a to it264512352
stdClass.tovar2_unset_and_thesameRemoving link $ a and recreating link $ a000
stdClass.tovar3_unset_and_anotherRemove link $ a and create link $ b000
stdClass.tovar4_anotherCreating an object and linking $ c to it264512352
stdClass.tovar5_addlinkCreating the link $ a to the same object as $ b6412896
stdClass.z.free_memoryRemoving links $ a, $ b and $ c-592-1152-800
myclass.a.emptyClass A Description70013441128
myclass.aa.interfaceInterface Description A70013441128
myclass.ab.finalAB Final Class Description70013441128
myclass.ac.abstractDescription of the abstract class AC70013441128
myclass.b.extended.emptyDescription of Class B Extending A70013441128
myclass.c.empty.namespaceDescription of blank namespace C000
myclass.d.constructDescription of class D with constructor110422881920
myclass.dd.methodDD class description with method108822801912
myclass.ddd.private.varDDD class description with private variable96018401472
myclass.dddd.public.varDDDD class description with public variable96018401472
myclass.ddddd.static.varDDDDD class description with static variable96018401472
myclass.e.extended.destructClass E Description with a Destructor Extending Class D134427042272
myclass.e.instance.abCreating an AB object and $ e linking to it264512352
myclass.e.instance.dddddCreating the DDDDD object and the $ e link to it000
myclass.e.instance.eCreating an object E and linking $ e to it000
myclass.f.instance.dddddCreating a DDDDD object and linking $ f to it264512352
myclass.z.free_memoryRemoving links $ e, $ f-484-944-656
zend.a.init.autoloadAutoload initialization for ZendFramework127 444276 288249,232
zend.a.init.modelInitializing the default adapter for the base1,018,3882,081,6001 871 256
zend.extended.controller1Definition of the controller from Zend_Controller_Action. Along the way, standard Zend classes are loaded.378,296809 384712,816
zend.extended.controller2Definition of the controller. The Zend classes are already loaded, see how much our class weighs.11 32819,60816 008
zend.extended.model1Model definition from Zend_Db_Table. Along the way, standard Zend classes are loaded.27,93648 54440,224
zend.extended.model2Model definition. The Zend classes are already loaded, see how much our class weighs.27,93648 53640 208
zend.use.model1.e.instance1Creating a Model1 object and $ e references to it249246483432
zend.use.model1.f.instance2Creating a Model1 object and $ f references to it176432562488
zend.use.model1.g.instance3Creating a Model1 object and linking $ g to it176432562488
zend.use.model2.e.instance1Creating a Model2 object and $ e references to it7401400944
zend.use.model2.f.instance2Creating a Model2 object and $ f references to it000


You may notice that the assembly of Gentoo consumes 10-20% less memory, and in rare cases, the savings reach 50%. Apparently, the size of the internal structures depends on the optimizations for the processor. For the experiment, I rebuilt php with different variants of CFLAGS, but he didn’t consume more of it. Apparently the difference is not due to rebuilding PHP itself, but from rebuilding standard Sish libraries.


As noted above, it is difficult to accurately measure $ include_overhead, so if you run these tests, then you can make it so that memory consumption jumps to 4, 8, 12, 16 bytes, even in tests that should consume the same. Do not focus on this attention. I ran the tests in a different order and more or less found true memory consumption.

Let's talk about tests related to ZendFramework. Loading class definitions of Zend into memory erases significant resources, whereas object references already consume not so much. Controller2 is needed to check how much a similar controller will eat, if all intermediate classes are already in memory. Model2 is designed for the same purpose.
Potentially, the use of a PHP accelerator will save us memory on all definitions, because they will already be stored in memory. Let's check this statement.

Accelerator Testing


APC was taken for testing, and tests were run via the web using a script:
 php run.testsuite-with-accelerator.php 

The results are shown only tests, where the accelerator has an effect:
Test nameDescriptionUbuntu x86
PHP 5.3.10,
ZF 1.11.11,
Empty cache
Ubuntu x86
PHP 5.3.10,
ZF 1.11.11,
Refresh
Ubuntu x86-64
PHP 5.3.10,
ZF 1.11.11,
Empty cache
Ubuntu x86-64
PHP 5.3.10,
ZF 1.11.11,
Refresh
myclass.a.emptyClass A Description84067214801256
myclass.aa.interfaceInterface Description A85667615121264
myclass.ab.finalAB Final Class Description84467214881256
myclass.ac.abstractDescription of the abstract class AC85268015041264
myclass.b.extended.emptyDescription of Class B Extending A91270015121264
myclass.c.empty.namespaceDescription of blank namespace C176-sixteen184-72
myclass.d.constructDescription of class D with constructor125696024481736
myclass.dd.methodDD class description with method126896824321728
myclass.ddd.private.varDDD class description with private variable114096420001760
myclass.dddd.public.varDDDD class description with public variable113295220001760
myclass.ddddd.static.varDDDDD class description with static variable112495220001760
myclass.e.extended.destructClass E Description with a Destructor Extending Class D1528122828882160
myclass.z.free_memoryRemoving links $ e, $ f-332-548-784-1024
zend.a.init.autoloadAutoload initialization for ZendFramework127 59616 196276,44028,992
zend.a.init.modelInitializing the default adapter for the base1,018,564251,8402,081,696479 280
zend.extended.controller1Definition of the controller from Zend_Controller_Action. Along the way, standard Zend classes are loaded.378 46466,804809 608120 864
zend.extended.controller2Definition of the controller. The Zend classes are already loaded, see how much our class weighs11,47611,14019,79219,056
zend.extended.model1Model definition from Zend_Db_Table. Along the way, standard Zend classes are loaded.28,08025,67648,70442 944
zend.extended.model2Model definition. The Zend classes are already loaded, see how much our class weighs.28,08025,70448 67242 960


I also did some tests with xcache and noticed 2 differences from APC. Firstly: xcache loses (almost always) by 10-15% in terms of memory savings. And secondly: xcache immediately gives the files from the cache, while the APC - only after repeated treatment. Although useless, but an advantage.


Immediately, I note that there is much more scatter in the results than when testing without an accelerator, since the files were not renamed and $ include_overhead was calculated with a big error.

As we can see, the accelerator saves us memory for definitions, but not completely, since PHP apparently transfers some pieces from the cache to the current session.
Now we will pass from abstract tests to quite real.

Testing a small application on ZendFramework


For testing, a test task of one of our programmers ( Simple-blog ) was taken: a collective blog service with functions: registration, authorization, reading the list of posts, opening a post and commenting on it. At the end of index.php it was written:
 echo memory_get_peak_usage(); 
to check how much memory the script devoured during page generation. Results:
Page typeUbuntu x86
PHP 5.3.10,
ZF 1.11.11,
Empty cache
Ubuntu x86
PHP 5.3.10,
ZF 1.11.11,
Refresh
Ubuntu x86-64
PHP 5.3.10,
ZF 1.11.11,
Empty cache
Ubuntu x86-64
PHP 5.3.10,
ZF 1.11.11,
Refresh
List of posts5 328 6481 792 96810,938,1603,306,720
Post and his comments5,372,3561,831,45211,015,3203,373,528
Login form6,781,6562,277,16413 982 1044,187,600
Registration form6,796,4962,291,56814 009 3844,211,432

Additionally, the Gentoo build was tested, it was 25% more efficient in all tests.

findings



Coder



UPD


AntonShevchuk added results for tests for PHP 5.4 . PHP 5.4 looks much more economical compared to 5.3. Official documentation also confirms this.

Source: https://habr.com/ru/post/161629/


All Articles