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

Эта проблема

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

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 block - поэтому, если функция не определена внутри него, эта опция не нужна.

Смешивание сgc_* функции:

Я пытался (без особой надежды, я должен сказать), используя комбинации,gc_disable()gc_enable() а такжеgc_collect_cycles() с обоими экспериментами выше, чтобы увидеть, если что-то изменилось - это нет.

 Damien Overeem12 мая 2013 г., 22:19
Понятия не имею, почему кто-то сделал понижающее голосование. Ненавижу, когда люди понижают голос без комментариев. Я'Я просто буду поддерживать это ...
 bestprogrammerintheworld14 мая 2013 г., 12:48
@AlixAxel - Ага, хорошо. Я постараюсь предоставить решение в течение нескольких дней. Я несейчас нет времени, но оноЭто очень сложный и интересный вопрос :-)
 Alix Axel12 мая 2013 г., 22:49
@OneTrickPony: Снаружи я могу, внутри я могуt (представьте, что я хочу профилировать использование памяти ...preg_match_all() - не могне делай этого).
 Alix Axel14 мая 2013 г., 13:36
@bestprogrammerintheworld: я проверил код и пример этого проекта, я подозреваю, чтокажется пойматьвыход потреблениеarray_push а такжеarray_pop потому что он'на самом деле хранит это массив. Я бы хотел этонажмите во внутреннее / частное выполнение функции и возвращают пик памяти самой функции.
 Hugo Delsing14 мая 2013 г., 15:53
Да, это не будет самым динамичным решением. Вероятно, проще изменить некоторые исходные файлы PHP и запустить профилировщик на отдельном сборочном PHP-сборке.
 Alix Axel14 мая 2013 г., 15:33
@HugoDelsing: Этоочень умная идея! Тем не менее, это 'Это очень непрактично и требует много времени. знак равно
 Damien Overeem12 мая 2013 г., 22:29
Некоторые люди @ стека немного ... странно .. Понижение рейтинга без объяснения причин вряд ли кому-нибудь поможет. Люди должны принять это во внимание.все в порядке :)
 Alix Axel14 мая 2013 г., 13:29
@bestprogrammerintheworld: Это выглядит очень похоже на то, что я делал, за исключением того, что я оборачивал свой код вdeclare(1) {...} блок иhog() определение функции было за пределами этого блока. Может быть, этопричина, по которой это нет работа. Тем не менее, я'Я попробую это позже, когда у меня будут изменения, а пока опубликую это в качестве ответа, чтобы я мог проголосовать. =) Проект конечно приятный.
 Alix Axel14 мая 2013 г., 13:43
@bestprogrammerintheworld: в основном яя пишу профилировщик, который во времяs секунд вызывает функцию / метод,,abc то же самое (переменное) число раз (циклически изменяя время ЦП для большей согласованности) и собирает их абсолютное (и относительное) время выполнения, напримерprofile('crc32|md5|sha1', $seconds = 15, 'variadic args here');, Это удобный способ быстро выбрать один над другим. Но представьте себе, что один обратный вызов на 5% быстрее другого, однако он потребляет на 300% больше оперативной памяти. Вы видите, почему в этом случае было бы полезно / необходимо использовать функции использования?
 bestprogrammerintheworld14 мая 2013 г., 12:52
Может быть, это может быть что-то?github.com/kampaw/profiler
 Alix Axel13 мая 2013 г., 19:05
@bestprogrammerintheworld: Этоглобальный для всего сценария, как только он достигнет максимального выигрышане сможет выяснить оставшиеся блоки.
 Alix Axel12 мая 2013 г., 22:25
@damienovereem: яя довольно пессимистичен в поиске решения, но я нене знаю ... Отсюда и вопрос. Я предполагаю, что отрицательный результат заключается в том, что это, очевидно, не имеет решения
 bestprogrammerintheworld13 мая 2013 г., 19:03
memory_get_peak_usage ()? (Если PHP>= 5.2.0)php.net/manual/en/function.memory-get-peak-usage.php
 Hugo Delsing14 мая 2013 г., 14:59
Я думаю, что ваша лучшая ставка была бы методом проб и ошибок. Позвольте отдельному сценарию запустить функцию, и непосредственно перед ее вызовом заполняется памятьstr_repeat например, оставить только 100Kb. Затем вызовите этот скрипт с помощью curl или чего-то еще, и если он вернет статус 200, вы запустите его снова, оставив всего 50 КБ. Если его статус 500, вы получите сообщение, как «.. пытаясь выделить хххх байт и затем снова запустите скрипт с байтами 100Kb - xxxx. Очевидно, это будет работать только тогда, когда у вас есть результаты, которые вы можете кэшировать, иметь небольшие изменения во входных данных и не требовать точного количества байтов.

Ответы на вопрос(3)

Это все, что связано с переменной области. Все внутри функции будет очищено после завершения функции. Поэтому, если вам нужно знать, сколько памяти в целом используется, вам нужно объявить их вне области действия функции. Мой голос будет идти для одного массива переменных для простоты, если вам нужно несколько переменных. Если вам нужен только один, вам, очевидно, не нужен массив.

 $value) 
{ 
  $alpha = memory_get_usage(); 
  hog($key); 
  $iterations[$key] = memory_get_usage() - $alpha;
  $outervar = array(); 
 }

print_r($iterations);

Так как мы не храним результат борова, это будет использовать0.5mb*$i, Если вам также нужно возвращаемое значение, даже если оно не сохранено, сначала сохраните его в$outervar['result'] или что-то, а затем вернуть его. Но тогда он будет засчитан дважды, если вы сохраните его.

Вторым вариантом будет дать второй параметр по ссылке&$memusage и использоватьmemory_get_usage() часть внутри функции и сохранить результат в переменной byref

 Alix Axel12 мая 2013 г., 22:55
Я все еще должен разместить$outervar['s'] = ... на каждом логическом блоке, чтобы получить пиковое использование функции, что крайне непрактично. Плюс, еслиhog() использует любую другую функцию, такую какpreg_replace() на большом массиве я все еще выигралне сможет отчитаться заpreg_replace() использование памяти.
Решение Вопроса

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

Whoohoo! СпасибоFacebook!

Из документов XHProf:

Стоит уточнить, что XHProf неСтрого отслеживать каждое распределение / свободную операцию. Скорее он использует более упрощенную схему. Он отслеживает увеличение / уменьшение объема памяти, выделяемой PHP между каждой функцией.Вход и выход. Он также отслеживает увеличение / уменьшение количествапик память, выделенная PHP для каждой функции.

 bestprogrammerintheworld22 мая 2013 г., 14:32
Здорово! Я'мы никогда не слышали об этом. Спасибо, что поделился! +1

Я нашел это:github.com/kampaw/profiler которые, кажется, используюттик / регистрация / декар-концепцию»что не быловариант для вас. Я'Мы также читали, что register_tick_functionality будет удален в PHP 6. (Но это может быть просто слух)

Я полностью понимаю, что вы имеете в виду, проверяя память ВНУТРИ функции.

мы проверили ваш код, основываясь на простом вызове функции и последующем возврате памяти после использования этой функции. Я сделал checkMemoryFunction (), просто сделав его более общим (конечно, checkMemoryFunction занял бы немного памяти, но при необходимости это можно было бы вычесть). Я думаю, что вы правильно подумали о том, как привыкнуть к памяти, НО я нашел еще одну очень странную вещь ...

... с этим кодом:

 $value)
{
    $mem = checkMemoryFunction('hog', $key);
    $iterations[$key] = $mem;
}

$peak = max($iterations);
echo '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 ( [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 = ar,ray_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.

Я надеюсь, что это может помочь вам немного! :-)

 bestprogrammerintheworld22 мая 2013 г., 14:39
Спасибо! Я не'Это действительно нужно сейчас, но, кажется, очень полезно знать, как этого добиться в будущем! :-)
 Alix Axel22 мая 2013 г., 05:15
Вы, похоже, тоже интересовались решением, просто сообщая, что я нашел одно - проверьте мой ответ. знак равно

Ваш ответ на вопрос