SO,
Особенности
Предположим, что у нас есть некоторая проблема и по крайней мере два решения для нее. И то, что мы хотим достичь - это сравнить эффективность для них. Как это сделать? Очевидно, лучший ответ: делать тесты. И я сомневаюсь, что есть лучший способ, когда речь заходит о специфических для языка вопросах (например, "что быстрее для PHP: 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
];
}
- это будет соответствовать нашей проблеме и может использоваться для сравнения сравнительных тестов. Под сравнительным я имею в виду, что мы можем использовать функцию выше для кода X
, а затем для кода Y
, и после этого можно сказать, что код X
равен Z%
быстрее/медленнее, чем код Y
.
Проблема
Хорошо, поэтому мы можем легко измерить время. Но как насчет памяти? Наше предыдущее предположение "если мы хотим протестировать некоторый код, равный проверке некоторой функции", похоже, здесь неверно. Зачем? Потому что - это верно из формальной точки, но если мы будем скрывать код внутри функции, мы никогда не сможем измерить память после этого. Пример:
function foo($x, $y)
{
$bar = array_fill(0, $y, str_repeat('bar', $x));
//do stuff
}
function baz($n)
{
//do stuff, resulting in $x, $y
$bee = foo($x, $y);
//do other stuff
}
- и мы хотим протестировать 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()
и повторите мой анализ внутри нее. И так далее. - Во-вторых - как я уже упоминал, существуют некоторые специфические для языка вещи, которые могут быть решены только путем проведения тестов. Вот почему для меня важно автоматическое определение времени - это моя цель.
Кроме того, включена сборка мусора. Я использую PHP 5.5 (я считаю, что это имеет значение)
Вопрос
Как мы можем эффективно измерять использование памяти определенной функцией? Возможно ли это в PHP? Возможно ли это с помощью некоторого простого кода (например, функция benchmark
для измерения времени выше)?