问题
假设我有这样一个函数:
function hog($i = 1) // uses $i * 0.5 MiB, returns $i * 0.25 MiB
{
$s = str_repeat('a', $i * 1024 * 512); return substr($s, $i * 1024 * 256);
}
我想调用它,并能够检查它使用的最大内存量。
也就是 memory_get_function_peak_usage($callback);
。这可能吗?
What I Have try
我使用以下值作为hog()
的非单调递增的$i
参数:
$iterations = array_merge(range(0, 50, 10), range(50, 0, 5));
$iterations = array_fill_keys($iterations, 0);
本质上是:
(
[0] => 0
[10] => 0
[20] => 0
[30] => 0
[40] => 0
[50] => 0
[45] => 0
[35] => 0
[25] => 0
[15] => 0
[5] => 0
)
用memory_get_usage()
封闭foreach ($iterations as $key => $value)
{
$alpha = memory_get_usage(); hog($key);
$iterations[$key] = memory_get_usage() - $alpha;
}
print_r($iterations);
输出:(
[0] => 96
[10] => 0
[20] => 0
[30] => 0
[40] => 0
[50] => 0
[45] => 0
[35] => 0
[25] => 0
[15] => 0
[5] => 0
)
如果我存储hog()
的返回值,结果开始看起来更真实:
foreach ($iterations as $key => $value)
{
$alpha = memory_get_usage(); $s = hog($key);
$iterations[$key] = memory_get_usage() - $alpha; unset($s);
}
print_r($iterations);
输出:(
[0] => 176
[10] => 2621536
[20] => 5242976
[30] => 7864416
[40] => 10485856
[50] => 13107296
[45] => 11796576
[35] => 9175136
[25] => 6553696
[15] => 3932256
[5] => 1310816
)
和预期的一样,现在它显示了返回的内存量,但是我需要使用的总内存量
使用register_tick_function()
:
我不知道,但事实证明,当你知道的时候:
declare (ticks=1)
{
$a = hog(1);
}
它不会为hog()
函数内的每一行,语句或代码块打勾,只会为declare
块内的代码打勾——所以,除非在其中定义了函数,否则这个选项是不允许的。
与gc_*
函数混合使用:
我尝试(没有太多的希望我必须说)使用gc_disable()
, gc_enable()
和gc_collect_cycles()
的组合与上面的两个实验,看看是否有什么改变-它没有。
我在PHP手册中挖掘,我发现memtrack
扩展,不完美,但它的东西。
EDIT:我听说过它,但从未真正尝试过。原来XHProf是我所需要的:
$flags = array
(
XHPROF_FLAGS_CPU,
XHPROF_FLAGS_MEMORY,
XHPROF_FLAGS_NO_BUILTINS,
);
$options = array
(
'ignored_functions' => array
(
'call_user_func',
'call_user_func_array',
'xhprof_disable',
),
);
function hog($i = 1) // uses $i * 0.5 MiB, returns $i * 0.25 MiB
{
$s = str_repeat('a', $i * 1024 * 512); return substr($s, $i * 1024 * 256);
}
测试# 1:
xhprof_enable(array_sum($flags), $options);
hog(4);
$profile = xhprof_disable();
print_r($profile);
输出: [main()==>hog] => Array
(
[ct] => 1
[wt] => 54784
[mu] => 384
[pmu] => 3142356
)
[main()] => Array
(
[ct] => 1
[wt] => 55075
[mu] => 832
[pmu] => 3142356
)
mu
为内存占用率,pmu
为内存占用率峰值,3142356 / 1024 / 1024 / 0.5 = 4 = $i
.
Test #2(不含XHPROF_FLAGS_NO_BUILTINS
):
[hog==>str_repeat] => Array
(
[ct] => 1
[wt] => 21890
[cpu] => 4000
[mu] => 2097612
[pmu] => 2094200
)
[hog==>substr] => Array
(
[ct] => 1
[wt] => 17202
[cpu] => 4000
[mu] => 1048992
[pmu] => 1048932
)
[main()==>hog] => Array
(
[ct] => 1
[wt] => 45978
[cpu] => 8000
[mu] => 1588
[pmu] => 3143448
)
[main()] => Array
(
[ct] => 1
[wt] => 46284
[cpu] => 8000
[mu] => 2132
[pmu] => 3143448
)
Whoohoo !由于Facebook !
从XHProf文档:
值得澄清的是,XHProf并不严格跟踪每一个分配/自由操作。相反,它使用了一个更简单的方案。它跟踪分配给PHP的内存量的增加/减少在每个函数的入口和出口之间。它还跟踪增加/减少分配给PHP的峰值内存的数量
这与变量作用域有关。函数结束后,函数内的所有内容都将被清除。因此,如果您需要知道总共使用了多少内存,则需要在函数作用域之外声明它们。我的投票将去一个单一的变量数组,以方便,如果你需要多个变量。如果你只需要一个,那么显然不需要数组。
<?php
$outervar = array();
function hog($i = 1) // uses $i * 0.5 MiB, returns $i * 0.25 MiB
{
global $outervar;
$outervar['s'] = str_repeat('a', $i * 1024 * 512); return substr($outervar['s'], $i * 1024 * 256);
}
foreach ($iterations as $key => $value)
{
$alpha = memory_get_usage();
hog($key);
$iterations[$key] = memory_get_usage() - $alpha;
$outervar = array();
}
print_r($iterations);
由于我们不存储hog的结果,因此将使用0.5mb*$i
。如果您还需要返回值,即使它没有存储,首先将其保存到$outervar['result']
或其他东西,然后返回它。但是,如果您保存它,它将被双倍计数。
&$memusage
给出第二个形参,并在函数中使用memory_get_usage()
部分,并将结果存储在byref变量
我发现:github.com/kampaw/profiler
似乎使用了"tick/register/declare -concept",这不是你的选择。我还读到,register_tick_function将在PHP 6中删除。(但这可能只是谣言)
我完全理解你在函数内部检查内存的意思。
我已经测试了你的代码基于只是调用函数,然后返回内存使用之后使用该函数。我做了一个checkMemoryFunction(),只是为了使它更通用(当然checkMemoryFunction会占用一点内存,但如果有必要,可以减去)。我想你想的是对的,但是我发现了另一件很奇怪的事情……
…使用下面的代码:
<?php
function hog($i)
{
$s = str_repeat('a', $i * 1024 * 512);
return substr($s, $i * 1024 * 256);
}
function checkMemoryFunction($functionName, $i) {
$startMemory = memory_get_usage();
call_user_func($functionName, $i);
return memory_get_usage() - $startMemory;
}
$iterations = array_merge(range(0, 50, 10), range(50, 0, 5));
$iterations = array_fill_keys($iterations, 0);
foreach ($iterations as $key => $value)
{
$mem = checkMemoryFunction('hog', $key);
$iterations[$key] = $mem;
}
$peak = max($iterations);
echo '<hr />Iteratins array:';
print_r($iterations);
echo 'memory peak=' . $peak;
?>
我得到了和你一样的结果:(第一个元素集,但不是其余的)
输出迭代数组:
Array ( [0] => 312 [10] => 0 [20] => 0 [30] => 0 [40] => 0 [50] => 0 [45] => 0 [35] => 0 [25] => 0 [15] => 0 [5] => 0 ) memory peak=312
然而,当我添加行设置每个键值为0(或任何值)…
$iterations = array_merge(range(0, 50, 10), range(50, 0, 5));
$iterations = array_fill_keys($iterations, 0);
// set each value to 0 in array
foreach ($iterations as $key => &$value)
{
$value = 0;
}
foreach ($iterations as $key => $value)
{
$mem = checkMemoryFunction('hog', $key);
$iterations[$key] = $mem;
}
…我得到这些值(所有函数调用的一些内存使用):
Array ( [0] => 312 [10] => 24 [20] => 24 [30] => 24 [40] => 24 [50] => 24 [45] => 24 [35] => 24 [25] => 24 [15] => 24 [5] => 24 ) memory peak=312
所以似乎问题在于对array_fill_keys()
的调用。(似乎数组元素没有以某种奇怪的方式初始化)
仔细查看合并数组后代码中的$iterations-array,它看起来是这样的:(重复值)
Array ( [0] => 0 [1] => 10 [2] => 20 [3] => 30 [4] => 40 [5] => 50 [6] => 50 [7] => 45 [8] => 40 [9] => 35 [10] => 30 [11] => 25 [12] => 20 [13] => 15 [14] => 10 [15] => 5 [16] => 0` )
但我认为你真正想要的是这样的东西:
Array ( [0] => 0 [1] => 10 [2] => 20 [3] => 30 [4] => 40 [5] => 50 [6] => 45 [8] => 35 [10] => 25 [12] => 15 [14] => 5)
我的怀疑是重复使array_fill_keys()以奇怪的方式行事,所以我尝试:
$iterations = array(0, 10, 20, 30, 40, 50, 45, 35, 25, 15);
$iterations = array_fill_keys($iterations, 0);
foreach ($iterations as $key => $value)
{
$mem = checkMemoryFunction('hog', $key);
$iterations[$key] = $mem;
}
但它仍然没有像预期的那样工作:
Array ( [0] => 312 [10] => 0 [20] => 0 [30] => 0 [40] => 0 [50] => 0 [45] => 0 [35] => 0 [25] => 0 [15] => 0 ) memory peak=312
添加
foreach ($iterations as $key => &$value)
{
$value = 0;
}
还是像预期的那样:
Array ( [0] => 312 [10] => 0 [20] => 24 [30] => 24 [40] => 24 [50] => 24 [45] => 32 [35] => 48 [25] => 24 [15] => 24 [5] => 24 ) memory peak=312
我认为这很奇怪,因为array_fill_keys($iterations, 0);
应该和上面的foreach做同样的事情。我不明白为什么它不像预期的那样工作。也许这是一个bug,但也可能是一些我没有想到的"愚蠢"的东西。
另一种方法是将php源文件中的"内容存储在函数中",然后将其保存为profile/hog.php,然后执行hog.php代码。
我希望这能帮助你一点!: -)