2

问题

可以说我有这个功能:

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);. 这可能吗?


我试过的

我使用以下值作为我的非单调递增$i参数hog()

$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()通过上面的两个实验来查看是否有任何改变 - 它没有。

4

3 回答 3

1

我正在挖掘 PHP 手册,我发现了memtrack扩展,虽然不完美,但确实是这样。


编辑:我听说过它,但以前从未真正尝试过。原来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


测试#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
        )

哇哦!谢谢脸书


来自 XHProf 文档:

值得澄清的是,XHProf 并没有严格跟踪每个分配/释放操作。相反,它使用更简单的方案。它跟踪在每个函数的进入和退出之间分配给 PHP 的内存量的增加/减少。它还跟踪为每个函数分配给 PHP的峰值内存量的增加/减少。

于 2013-05-22T02:29:34.323 回答
0

这与变量范围有关。一旦函数结束,函数内的所有内容都将被清除。因此,如果您需要知道总共使用了多少内存,则需要在函数范围之外声明它们。如果您需要多个变量,我的投票将转到单个变量数组。如果你只需要一个,你显然不需要数组。

<?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 变量中

于 2013-05-12T20:37:04.523 回答
0

我发现了这个:github.com/kampaw/profiler 这似乎使用了“tick/register/decare-concept”,这不是你的选择。我还读到了 register_tick_functionality 将在 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 做同样的事情。我不知道为什么它不能按预期工作。也许这是一个错误,但可能是我没有想到的“愚蠢”。

另一种方法可能是从 PHP 源文件中存储“函数内的内容”,然后将其保存为 profile/hog.php,然后执行 hog.php 代码。

我希望这可以帮助你一点!:-)

于 2013-05-16T07:58:51.757 回答