PHP中的基准内存使用
所以 细节 让我们假设我们有一些问题和至少两种解决方案。我们想要实现的是比较它们的有效性。如何做到这一点?显然,最好的答案是:做测试。我怀疑有没有更好的方法来解决特定于语言的问题(例如“什么对PHP来说更快:PHP中的基准内存使用,php,performance,memory,benchmarking,Php,Performance,Memory,Benchmarking,所以 细节 让我们假设我们有一些问题和至少两种解决方案。我们想要实现的是比较它们的有效性。如何做到这一点?显然,最好的答案是:做测试。我怀疑有没有更好的方法来解决特定于语言的问题(例如“什么对PHP来说更快:echo'foo',bar'或echo'foo.'bar')) 好的,现在我们假设如果我们想测试一些代码,就等于测试一些函数。为什么?因为我们可以将代码包装到函数中,并将其上下文(如果有)作为参数传递。因此,我们所需要的就是,例如,有一些基准函数,可以完成所有的工作。这里有一个非常简单的例子
echo'foo',bar'
或echo'foo.'bar')
)
好的,现在我们假设如果我们想测试一些代码,就等于测试一些函数。为什么?因为我们可以将代码包装到函数中,并将其上下文(如果有)作为参数传递。因此,我们所需要的就是,例如,有一些基准函数,可以完成所有的工作。这里有一个非常简单的例子:
function benchmark(callable $function, $args=null, $count=1)
{
$time = microtime(1);
for($i=0; $i<$count; $i++)
{
$result = is_array($args)?
call_user_func_array($function, $args):
call_user_func_array($function);
}
return [
'total_time' => microtime(1) - $time,
'average_time' => (microtime(1) - $time)/$count,
'count' => $count
];
}
-我们想测试baz,也就是说,它将使用多少内存。我所说的“多少”是指“在函数执行期间,最大内存使用量是多少”。很明显,我们不能像测量执行时间那样行事——因为我们对执行时间之外的函数一无所知——它是一个黑匣子。事实上,我们甚至不能确定该函数是否会成功执行(例如,想象一下,如果$x
和$y
内部baz
将被指定为1E6,会发生什么情况)。因此,将代码封装在函数中可能不是一个好主意。但如果代码本身包含其他函数/方法调用呢
我的方法
我目前的想法是以某种方式创建一个函数,它将在每个输入代码行之后测量内存。这意味着:让我们有代码
$x = foo();
echo($x);
$y = bar();
-完成某些操作后,测量功能将执行以下操作:
$memory = memory_get_usage();
$max = 0;
$x = foo();//line 1 of code
$memory = memory_get_usage()-$memory;
$max = $memory>$max:$memory:$max;
$memory = memory_get_usage();
echo($x);//second line of code
$memory = memory_get_usage()-$memory;
$max = $memory>$max:$memory:$max;
$memory = memory_get_usage();
$y = bar();//third line of code
$memory = memory_get_usage()-$memory;
$max = $memory>$max:$memory:$max;
$memory = memory_get_usage();
//our result is $max
-但这看起来很奇怪,也没有回答一个问题——如何衡量函数内存的使用
用例
用例:在大多数情况下,复杂性理论至少可以为某些代码提供big-O
估计。但是:
- 首先,代码可能非常庞大——我希望尽可能长时间地避免手动分析。这就是为什么我目前的想法不好的原因:它可以被应用,是的,但它仍然可以手工处理代码。而且,为了更深入地了解代码的结构,我需要递归地应用它:例如,在将它应用于顶级之后,我发现一些
函数占用了太多内存。我会怎么做?是,转到此foo()
函数,然后。。重复我的分析。等等foo()
- 第二,正如我所提到的,有些特定于语言的问题只能通过测试来解决。这就是为什么我的目标是拥有一些自动的方式,比如时间测量
基准测试
时间测量功能) 您可以使用和,它提供内存使用信息
如果这是不可能的,您可以始终使用我认为比内存使用更合适的工具。这可能不是您想要的,但您可能会使用它 这应该已经说明了我将要说的一切 使用勾号处理程序,在每次执行时将内存使用情况打印到一个文件,文件行带有:
function tick_handler() {
$mem = memory_get_usage();
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[0];
fwrite($file, $bt["file"].":".$bt["line"]."\t".$mem."\n");
}
register_tick_function('tick_handler'); // or in class: ([$this, 'tick_handler']);
然后查看文件,逐行查看内存在时间上的变化
您还可以稍后通过单独的程序解析该文件,以分析峰值等
(为了通过调用内部函数来查看可能的峰值,您需要将结果存储到一个变量中,否则在tick处理程序测量内存之前,它已经被释放)在提出了使用ticks的好主意之后,我做了一些研究。现在我对这门课有了答案:
class Benchmark
{
private static $max, $memory;
public static function memoryTick()
{
self::$memory = memory_get_usage() - self::$memory;
self::$max = self::$memory>self::$max?self::$memory:self::$max;
self::$memory = memory_get_usage();
}
public static function benchmarkMemory(callable $function, $args=null)
{
declare(ticks=1);
self::$memory = memory_get_usage();
self::$max = 0;
register_tick_function('call_user_func_array', ['Benchmark', 'memoryTick'], []);
$result = is_array($args)?
call_user_func_array($function, $args):
call_user_func($function);
unregister_tick_function('call_user_func_array');
return [
'memory' => self::$max
];
}
}
//var_dump(Benchmark::benchmarkMemory('str_repeat', ['test',1E4]));
//var_dump(Benchmark::benchmarkMemory('str_repeat', ['test',1E3]));
-所以它正是我想要的:
- 这是一个黑匣子
- 它测量传递函数的最大使用内存
- 它独立于上下文
\uu toString()
方法)。为什么会这样?这不是别的,只是一个简单的问题。我希望很快就能修好
还有什么其他选择?我想到的最简单的选择是使用变量。但它们很奇怪,而且这也是我想要避免的副作用。我不想影响上下文。但是,实际上,我们可以将所有需要的东西封装在某个类中,然后通过调用tick函数。而call\u user\u func\u array
仅仅是字符串,因此我们可以克服这种错误的PHP行为,并成功地完成整个工作
更新:我已经实现了这个。我在这里添加了时间度量和自定义回调定义的度量。请随意使用它
更新:这个答案中提到的Bug现在已经修复,所以不需要使用注册为tick函数的call\u user\u func()
。现在可以直接创建和使用闭包
更新:由于功能要求,我添加了此测量工具。刚刚偶然发现
虽然他们没有提供基准测试的详细信息,但是他们分别实施了一些措施——不要认为很多人在办公桌下的虚拟机上并行运行了100多个PHP版本;) 正如您所说,这在很大程度上取决于内存管理,如果垃圾收集
function tick_handler() {
$mem = memory_get_usage();
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[0];
fwrite($file, $bt["file"].":".$bt["line"]."\t".$mem."\n");
}
register_tick_function('tick_handler'); // or in class: ([$this, 'tick_handler']);
class Benchmark
{
private static $max, $memory;
public static function memoryTick()
{
self::$memory = memory_get_usage() - self::$memory;
self::$max = self::$memory>self::$max?self::$memory:self::$max;
self::$memory = memory_get_usage();
}
public static function benchmarkMemory(callable $function, $args=null)
{
declare(ticks=1);
self::$memory = memory_get_usage();
self::$max = 0;
register_tick_function('call_user_func_array', ['Benchmark', 'memoryTick'], []);
$result = is_array($args)?
call_user_func_array($function, $args):
call_user_func($function);
unregister_tick_function('call_user_func_array');
return [
'memory' => self::$max
];
}
}
//var_dump(Benchmark::benchmarkMemory('str_repeat', ['test',1E4]));
//var_dump(Benchmark::benchmarkMemory('str_repeat', ['test',1E3]));