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 |
---|---|---|---|---|
ComposerAutoloadincludeFile@1 | 105 | 130952 | 43371.22 KB | 137090 |
array_change_key_case | 184 | 22635 | 3291.38 KB | 0 |
ComposerAutoloadincludeFile | 114 | 18888 | 23357.67 KB | 19252 |
ComposerAutoloadincludeFile@3 | 14 | 11328 | 4831.90 KB | 12025 |
each | 2686 | 9052 | 901.87 KB | 0 |
Smarty::fetch@1 | 26 | 9033 | 5560.42 KB | 9032 |
PDO::query | 101 | 8963 | 2786.97 KB | 1107 |
ComposerAutoloadincludeFile@2 | 19 | 8186 | 4940.82 KB | 8799 |
ComposerAutoloadincludeFile@5 | 3 | 5557 | 1724.64 KB | 5935 |
explode | 624 | 4643 | 367.98 KB | 55 |
is_readable | 427 | 4393 | 146.58 KB | 4408 |
Smarty::_smarty_include@1 | 5 | 3920 | 2578.56 KB | 3927 |
OxidEsalesEshopCommunityCoreModelBaseModel::_getFieldLongName | 1722 | 3468 | 152.72 KB | 2876 |
OxidEsalesEshopCommunityCoreModelBaseModel::_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 |
OxidEsalesEshopCommunityCoreField::__construct | 1440 | 2942 | 486.25 KB | 56 |
file_exists | 374 | 2637 | 140.97 KB | 2621 |
main() | 1 | 2446 | 1611.23 KB | 2569 |
OxidEsalesEshopCommunityCoreStrMb::preg_replace | 658 | 1970 | 91.58 KB | 2108 |
trim | 2431 | 1936 | 65.45 KB | 0 |
OxidEsalesEshopCommunityApplicationModelCategory::_setFieldData | 641 | 1924 | 90.27 KB | 1968 |
strtolower | 3056 | 1802 | 76.48 KB | 0 |
Smarty::_smarty_include | 5 | 1715 | 969.62 KB | 2253 |
OxidEsalesEshopCommunityCoreUtils::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]