Допустим, у меня есть эта функция:
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);
с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); }
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
.
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 / decare-concept», это не вариант для вас. Я также прочитал, что функция register_tick_functionality будет удалена в PHP 6. (Но это может быть просто слухи)
Я полностью понимаю, что вы имеете в виду, проверяя память INSIDE функции.
Я протестировал ваш код на основе просто вызова функции, а затем возвращал использование памяти после использования этой функции. Я сделал 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()
с вызовом 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, а затем сохранение его в виде профиля / hog.php и после этого выполнить код hog.php.
Надеюсь, это поможет вам немного! 🙂