Захват (извне) потребления памяти данного Обратного вызова

Проблема

Допустим, у меня есть эта функция:

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() с обоими экспериментами выше, чтобы увидеть, что-то изменилось – это не так.

Solutions Collecting From Web of "Захват (извне) потребления памяти данного Обратного вызова"

Я копал в руководстве по 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 .


Тест № 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 / 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.

Надеюсь, это поможет вам немного! 🙂