Форум dkLab и Denwer
Здесь общаются Web-разработчики.
Генеральный спонсор:
Хостинг «Джино»

Выделение памяти под параметры функции (NectarIn, оценка: 3)
Author Message
NectarIn
Участник форума



Joined: 10 Sep 2007
Posts: 21
Карма: 2
   поощрить/наказать

Location: Санкт-Петербург

PostPosted: Fri Dec 24, 2010 6:08 pm (написано за 9 минут 7 секунд)
   Post subject: Выделение памяти под параметры функции
Reply with quote

Вечер добрый.
Сегодня при экспериментировании с отложенным копированием и передачей по ссылке обнаружил довольно любопытное поведение.
Тестовый код выглядит так:
Code (php): скопировать код в буфер обмена
function main() {
    // INIT.
    $start = 0;
    echo (www.php.net/echo) '<b>MEMORY TESTS</b><br /><pre>';
    $start = memory_get_usage (www.php.net/memory_get_usage)();
   
    // START.
    $var = str_repeat (www.php.net/str_repeat)('A', 9952);
    echo (www.php.net/echo) 'BEFORE CALL:   ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    same($start, $var, $var, $var, $var, $var);
    echo (www.php.net/echo) 'AFTER RETURN:  ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
}

main();

function same($start, $var1, $var2, $var3, $var4, & $var5) {
    echo (www.php.net/echo) 'AFTER CALL:    ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    // Ничего не делаем...
    echo (www.php.net/echo) 'BEFORE RETURN: ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
}
В данном тесте я передаю одну и ту же переменную в функцию несколько раз. При этом, если всегда передавать её "по значению", ничего странного не происходит, срабатывает отложенное копирование, и память не выделяется. Если всегда передавать по ссылке - аналогично, память не дублируется. А вот если совместить оба способа (с одной переменной) - начинает плодиться память, причём количество дублирующих блоков зависит от порядка аргументов функции.
В частности, если ссылка идёт последней (как в примере), выделяется лишних 1x$var памяти. Если ссылка идёт предпоследней, уже 2x$var. Если ссылка идёт первой, выделяется лишних 4x$var памяти! Экспериментируя с количеством и положением ссылок в списке аргументов функции, я вывел следующую формулу:
Quote:
Количество лишних блоков памяти размером с $var определяется по формуле:
Одна не-ссылка до первой ссылки (если она есть) плюс количество не-ссылок после первой ссылки.
Количество добавочных ссылок по понятным причинам на увеличение памяти не влияет.

Я читал Шлосснейгла, я читал мануал, но видимо, этого недостаточно. К сожалению, ставить VLD сейчас нет времени, а знаний C недостаточно, чтобы отлаживать живой код. Поэтому буду очень признателен, если кто-то подскажет, почему начинает плодиться память. Тестировал на версии 5.3.1.
Back to top
View user's profile Send private message
bæv
Модератор «Дзена»



Joined: 27 Aug 2003
Posts: 7275
Карма: 9995
   поощрить/наказать


PostPosted: Fri Dec 24, 2010 7:06 pm (спустя 57 минут; написано за 43 секунды)
   Post subject:
Reply with quote

NectarIn wrote:
 Тестировал на версии 5.3.1.
— Вы самое главное не указали: какая у Вас операционная система?
Back to top
View user's profile Send private message
NectarIn
Участник форума



Joined: 10 Sep 2007
Posts: 21
Карма: 2
   поощрить/наказать

Location: Санкт-Петербург

PostPosted: Fri Dec 24, 2010 9:08 pm (спустя 2 часа 2 минуты; написано за 2 минуты 9 секунд)
   Post subject:
Reply with quote

Quote:
Вы самое главное не указали: какая у Вас операционная система?
Честно говоря, не думал, что это может играть роль. Тестировал на Windows Server 2003, только что на FreeBSD на хостинге... Ещё днём скидывал друзьям, они тестировали на 5.2.16, скорее всего на Убунте.
Точные числа на Windows и FreeBSD различаются, но эффект в целом тот же.
К слову: а как могла повлиять операционная система?..
Back to top
View user's profile Send private message
Юрий Насретдинов
Модератор



Joined: 13 Mar 2003
Posts: 8642
Карма: 194
   поощрить/наказать

Location: 007 495

PostPosted: Fri Dec 24, 2010 9:56 pm (спустя 47 минут; написано за )
   Post subject:
Reply with quote

В большинстве случаев Ваш вопрос не будет актуален. Где Вы используете подобный код и зачем?

А вообще, это может иметь некоторую логику — ведь если передавать и значение и ссылку в функцию, то для использования копирования-при-записи нужно быть аккуратным с тем фактом, что копировать значение придется и при изменении исходной переменной (мы ведь передали не только значение, но и ссылку на исходную переменную). Видимо, PHP в таком случае считает, что лучше скопировать переменные и не париться, чем париться со ссылками.
Back to top
View user's profile Send private message Send e-mail
NectarIn
Участник форума



Joined: 10 Sep 2007
Posts: 21
Карма: 2
   поощрить/наказать

Location: Санкт-Петербург

PostPosted: Fri Dec 24, 2010 10:41 pm (спустя 45 минут; написано за 8 минут 15 секунд)
   Post subject:
Reply with quote

Quote:
В большинстве случаев Ваш вопрос не будет актуален. Где Вы используете подобный код и зачем?
Согласен. Нигде. Ситуация возникла в ходе экспериментов.
Quote:
А вообще, это может иметь некоторую логику — ведь если передавать и значение и ссылку в функцию, то для использования копирования-при-записи нужно быть аккуратным с тем фактом, что копировать значение придется и при изменении исходной переменной (мы ведь передали не только значение, но и ссылку на исходную переменную). Видимо, PHP в таком случае считает, что лучше скопировать переменные и не париться, чем париться со ссылками.
Не могу согласиться. PHP нормально обрабатывает подобные ситуации, когда речь не идёт о вызове функции. Если передача по значению эквивалентна "$param = $var", а передача по ссылке эквивалентна "$param =& $var", то всё должно работать и так, для ZE это не должно составлять проблемы (как мне кажется). Тем более непонятен тот факт, что в одном случае выделяется 1 блок памяти, а в другом несколько - если бы разработчики PHP заранее предусмотрели подобную ситуацию, скорее всего, они бы нашли способ обойтись без захламления памяти.
Back to top
View user's profile Send private message
Юрий Насретдинов
Модератор



Joined: 13 Mar 2003
Posts: 8642
Карма: 194
   поощрить/наказать

Location: 007 495

PostPosted: Sat Dec 25, 2010 12:23 am (спустя 1 час 42 минуты; написано за )
   Post subject:
Reply with quote

Ну, кстати говоря, не факт, что PHP не применяет такую же стратегию при создании ссылок на переменные, которые были уже кому-то присвоены. То есть, если будет примерно такой код:
Code (php): скопировать код в буфер обмена
$var = 'Tralala';
$another = $var;
$link = &$var;
то память не будет скопирована для переменной $another, это тоже было бы неплохо потестировать. А вообще, вызов функции в пхп это не просто присвоение переменных, тут еще важно, что внутри функции другой контекст, да еще он может быть многократно вложенным. В таких случаях управление памятью становится весьма нетривиальной задачей.
Back to top
View user's profile Send private message Send e-mail
NectarIn
Участник форума



Joined: 10 Sep 2007
Posts: 21
Карма: 2
   поощрить/наказать

Location: Санкт-Петербург

PostPosted: Sun Dec 26, 2010 4:19 pm (спустя 1 день 15 часов 56 минут; написано за 2 минуты 21 секунду)
   Post subject:
Reply with quote

Да, действительно PHP и здесь удивил. Работает так же, как и при вызове функций (так что не в вызове дело).
Три простых теста:
Code (php): скопировать код в буфер обмена
function main() {
    // INIT.
    $start = 0;
    echo (www.php.net/echo) '<b>MEMORY TESTS</b><br /><pre>';
    $start = memory_get_usage (www.php.net/memory_get_usage)();
   
    // START.
    $var = str_repeat (www.php.net/str_repeat)('A', 100000);
    echo (www.php.net/echo) 'BEFORE COPY:   ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    $var1 = $var;
    $var2 = $var;
    $var3 = $var;
    $var4 = $var;
    $var5 = $var;
    echo (www.php.net/echo) 'BEFORE LINK:  ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    $ref1 =& $var;
    $ref2 =& $var;
    $ref3 =& $var;
    echo (www.php.net/echo) 'AFTER LINK:   ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
}

main();

// Выделяется 1 лишний блок.
 
Code (php): скопировать код в буфер обмена
function main() {
    // INIT.
    $start = 0;
    echo (www.php.net/echo) '<b>MEMORY TESTS</b><br /><pre>';
    $start = memory_get_usage (www.php.net/memory_get_usage)();
   
    // START.
    $var = str_repeat (www.php.net/str_repeat)('A', 100000);
    echo (www.php.net/echo) 'BEFORE LINK:   ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    $ref1 =& $var;
    $ref2 =& $var;
    $ref3 =& $var;
    echo (www.php.net/echo) 'BEFORE COPY:  ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    $var1 = $var;
    $var2 = $var;
    $var3 = $var;
    $var4 = $var;
    $var5 = $var;
    echo (www.php.net/echo) 'AFTER COPY:   ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
}

main();

//
 
Code (php): скопировать код в буфер обмена
function main() {
    // INIT.
    $start = 0;
    echo (www.php.net/echo) '<b>MEMORY TESTS</b><br /><pre>';
    $start = memory_get_usage (www.php.net/memory_get_usage)();
   
    // START.
    $var = str_repeat (www.php.net/str_repeat)('A', 100000);
    echo (www.php.net/echo) 'BEFORE MIX:   ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
    $var1 = $var;
    $var2 = $var;
    $var3 = $var;
    $ref =& $var;
    $var4 = $var;
    $var5 = $var;
    echo (www.php.net/echo) 'AFTER MIX:    ', memory_get_usage (www.php.net/memory_get_usage)() - $start, "\n";
}

main();

// Выделяется 3 лишних блока.
 
Единственное, что для меня всё равно остаётся непонятным - как это всё внутри работает. Шлосснейгл как раз не написал, как работают переменные-ссылки, а зря...
Back to top
View user's profile Send private message
NectarIn
Участник форума



Joined: 10 Sep 2007
Posts: 21
Карма: 2
   поощрить/наказать

Location: Санкт-Петербург

PostPosted: Tue Jan 11, 2011 2:01 pm (спустя 15 дней 21 час 41 минуту; написано за 1 минуту 36 секунд)
   Post subject:
Reply with quote

Удалось понять всё в деталях. Оформил выводы в виде небольшой статьи: nectarin.livejournal.com/100637.html
Спасибо Юрию за наводящие мысли и дельные советы.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic All times are GMT + 3 Hours
Page 1 of 1    Email to a Friend.
You cannot post new topics in this forum. You cannot reply to topics in this forum. You cannot edit your posts in this forum. You cannot delete your posts in this forum. You cannot vote in polls in this forum. You cannot attach files in this forum. You can download files in this forum.
XML