Benjamin Benjamin 07.06.2018

Testing a new approach to Memory Profiling in PHP with XHProf

Memory profiling in PHP has traditionally been hard. Most memory profilers compare the memory or peak memory before and after a function call to find out how much memory usage increased or decreased. This can be achieved by calling the equivalent of memory_get_usage() and memory_get_peak_usage() functions from within the profiling extension.

But this approach has one major blind spot, when a function allocates a lot of memory but also frees it again.

With PHP 7 there is a new hook into the Zend memory allocator that allows us to count the number of memory allocations, memory frees and the size of all memory allocations. We originally saw a similar approach to this in Danack's MemTrigger extension and adopted it to fit the XHProf datastructure.

We have added experimental (!) support for this new approach to memory profiling into our open-source XHProf extension and are looking for feedback from you, if this collected memory data provides a more useful way of finding memory problems.

To use this feature with one of the existing XHProf UIs, you can call the Profiler with:

<?php

tideways_xhprof_enable(
    TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU |
    TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC |
    TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU
);

The second flag will enable the allocation based profiler and store the data in the "mu" key that is already processed by all the existing UIs. This way no additional changes are necessary to your stack to get first feedback.

If you use the TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC instead, then three additional keys are part of the XHProf payload for each parent+child function pair:

  • mem.na The sum of the number of all allocations in this function.
  • mem.nf The sum of the number of all frees in this function.
  • mem.aa The amount of allocated memory.

With our CLI tool for analying XHProf data (sorry, no binary releases yet) we can then render this data much like Latency or Memory to get a result similar to this example:

FUNCTION COUNT NUM ALLOC ALLOC AMOUNT NUM FREES
Composer\Autoload\[email protected] 105 130952 43371.22 KB 137090
array_change_key_case 184 22635 3291.38 KB 0
Composer\Autoload\includeFile 114 18888 23357.67 KB 19252
Composer\Autoload\[email protected] 14 11328 4831.90 KB 12025
each 2686 9052 901.87 KB 0
Smarty::[email protected] 26 9033 5560.42 KB 9032
PDO::query 101 8963 2786.97 KB 1107
Composer\Autoload\[email protected] 19 8186 4940.82 KB 8799
Composer\Autoload\[email protected] 3 5557 1724.64 KB 5935
explode 624 4643 367.98 KB 55
is_readable 427 4393 146.58 KB 4408
Smarty::[email protected] 5 3920 2578.56 KB 3927
OxidEsales\EshopCommunity\Core\Model\BaseModel::_getFieldLongName 1722 3468 152.72 KB 2876
OxidEsales\EshopCommunity\Core\Model\BaseModel::_setFieldData 1397 3271 183.32 KB 3207
smarty_core_load_plugins 41 3054 3904.06 KB 3055
preg_match 580 2954 234.51 KB 1227
OxidEsales\EshopCommunity\Core\Field::__construct 1440 2942 486.25 KB 56
file_exists 374 2637 140.97 KB 2621
main() 1 2446 1611.23 KB 2569
OxidEsales\EshopCommunity\Core\StrMb::preg_replace 658 1970 91.58 KB 2108
trim 2431 1936 65.45 KB 0
OxidEsales\EshopCommunity\Application\Model\Category::_setFieldData 641 1924 90.27 KB 1968
strtolower 3056 1802 76.48 KB 0
Smarty::_smarty_include 5 1715 969.62 KB 2253
OxidEsales\EshopCommunity\Core\Utils::getLangCache 1 1710 351.24 KB 1712
func_get_args 713 1431 223.44 KB 0

Please write your feedback to the Issue Tracker of the XHProf extension or directly to [email protected]