LINUX.ORG.RU

Почему при разрешеных 128Мб в скрипте доступно только 900Кб?

 


0

5

Однако, я озадачен. Поливайте говном php, я готов, только растолкуйте, правильно ли я понял.

Вот тестовый скрипт:

<?php
	for ($i=0; $i < 900 * 1024; $i++)
	{
		global $a;
		
		$a[] = 'a';
	}
	
	echo "ok";
?>

Он отрабатывает хорошо. А вот если поменять 900 на 910, то вылетает со следующим сообщением:


Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /var/www/exp1.php on line 6

В php.ini memory_limit = 128M как и видно из сообщения об ошибке. Куда я просрал столько памяти? Почему не доступна?

★★★★★
Ответ на: комментарий от goingUp

что выдает memory_get_usage()?

219624

ничего не понимаю... а как тогда в эти 200 Кб помещается 900Кб массив? Или ему дополнительно выделяется памяти по ходу?

pihter ★★★★★
() автор топика
Ответ на: комментарий от zz

Новый код примера:

<?php

	echo memory_get_peak_usage()."<br>";

	for ($i=0; $i < 900 * 1024; $i++)
	{
		global $a;
		
		$a[] = 'a';
	}
	
	echo memory_get_peak_usage()."<br>";
	
	echo "ok";
?>

Выдает слудующее:

223448
133948664
ok

Теперь яснее. Это PHP настолько неэффективно использует память?

Олсо, очевидно что $a - это не указатель на кусок памяти, а 'a' не байт.

Я понимаю, кончено, что это не си и что в данном случае мой опыт слегка условен, но, блин не в сто же раз! То есть я хотел запихать в память 100 Мб буквами, а по факту не могу впихнуть даже 1Мб! То есть я нигде не ошибаюсь и просто PHP так жрет память, что одну букву в 100 байтах хранит?

pihter ★★★★★
() автор топика
Ответ на: комментарий от zz

Какая судьба уготована этому коду в будущем?

В дев нулл ему угатованно. Я просто столкнулся с нехваткой памяти на задаче, где совсем нет 100 Мб данных и вот написал тестовый пример чтоб понять куда девается память.

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Да. http://nikic.github.io/2011/12/12/How-big-are-PHP-arrays-really-Hint-BIG.html

Но можно сделать строку, она тоже индексируется, самый быстрый способ - с буфуризацией вывода:

<?php
	echo memory_get_peak_usage()."<br>";
	ob_start();
	for ($i=0; $i < 900 * 1024; $i++)
	{
		echo 'a';
	}
	$a = ob_get_contents();
	ob_end_clean();
	echo memory_get_peak_usage()."<br>";

	echo $a[900*1024-1];

zz ★★★★
()
Ответ на: комментарий от zz

Капец. Спасибо, конечно, но мне не 100Мб букв хранить в реальной задаче нужно, а большой иерархический массив. По-ходу нужно пересматривать концепцию и отказываться от такого большего массива, а думалось что оптимизация знатная выйдет :(

pihter ★★★★★
() автор топика
Ответ на: комментарий от zz

О. Годно, но мне не пригодится, я и так знаю куда утекла теперь. Просто не верилось в то, что реально 1 буква в 100 байтах хранится, думал я чего-то не учитываю, если так - то мне все ясно, спасибо большое.

pihter ★★★★★
() автор топика
Ответ на: комментарий от zz

:) а это только в PHP такая беда или во всяких руби/перлах/питонах высокоуровневых-нетипизированных прочих тоже, не знаешь?

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

В питоне получше, списки достаточно хороши, плюс там строгая типизация и число это число а не шняга которая умеет себя во все представления неявно конвертицо. Плюс на будущее всегда есть http://cython.org/ но до него еще дело не дошло. Олсо еще есть http://www.scipy.org/ но тебе наверное не очень подойдет.

На руби/перлах не пишу.

zz ★★★★
()
Ответ на: комментарий от x3al

Да, спасибо, я уже понял. Капец, какой толк от иерархических массивов, если в них ничего не влазит?

Кто в курсе, на рядовом хостинге сколько мемори лимит обычно ставят? Так и стоит по дефолту 128Мб?

pihter ★★★★★
() автор топика

Попробуй объявление global вынести за пределы цикла, например обернуть в функцию - картина может стать другая.

Вообще циклы самая тормознутая субстанция. С ними надо очень аккуратно аккуратно - как стакан на капоте.

Вообще globals это дурной тон.

Если приспичило - используй паттерн Registry или просто ссылку на объект (можно добавлять динамически свойство к экземпляру stdClass) или просто ArrayObject.

Позволь полюбопытствовать - зачем тебе такой тест?

swwwfactory ★★
()
Ответ на: комментарий от pihter

PHP ain’t C. That’s all this should tell us. You can’t expect that a super dynamic language like PHP has the same highly efficient memory usage that C has. You just can’t.

But if you do want to save memory you could consider using an SplFixedArray for large, static arrays.

Have a look a this modified script:

<?php $startMemory = memory_get_usage(); $array = new SplFixedArray(100000); for ($i = 0; $i < 100000; ++$i) { $array[$i] = $i; } echo memory_get_usage() - $startMemory, ' bytes';

It basically does the same thing, but if you run it, you’ll notice that it uses “only” 5600640 bytes.

x3al ★★★★★
()
Ответ на: комментарий от x3al

На типичных копеечных vps 512МБ, сколько поставишь — столько и будет.

Это я понимаю. А на хостингах, где олл инклюзив за 100 руб (ну типа домен в подарок при оплате за год, гиг места, ftp, 1 база MySQL, PHP5 ну и тд, все сталкивались) на таких сколько ставят? кто занет?

pihter ★★★★★
() автор топика
Ответ на: комментарий от swwwfactory

Попробуй объявление global вынести за пределы цикла, например обернуть в функцию - картина может стать другая.

Если даже и станет другой - то не кардинально, меня тут мордой натыкали в объяснение почему на 1 элемент уходит 144 байта (я знал, что PHP - не С, но такого представить не мог)

Вообще циклы самая тормознутая субстанция. С ними надо очень аккуратно аккуратно - как стакан на капоте.

Это еще почему?

Вообще globals это дурной тон.

Согласен, но плюс PHP как раз в простоте, в моем примере, кстати, global не нужен: это ж не рекурсивная функция, а цикл просто. Затупил я, бывает.

Позволь полюбопытствовать - зачем тебе такой тест?

Я пытался выяснить почему совсем небольшой массив не влазит в 128 Мб. Оказалось, что на один элемент уходит 144 байта, а я ожидал чего-то на пару порядков меньшего.

На моей задаче возникла вот такая вот ошибка по памяти и я решил разобраться... Хотел сразу при старте скрипта считывать из БД все дерево (ну реально оно не больше 4 мегабайт) в массив и дальше уже все вычисления, манипуляции, прогулки - вообще все, далать с этим массивом, а не спрашивать каждый раз БД, тем самым сократив количество заростов к БД в сотни раз.

Понятно теперь что такой малой кровью не обойдешься и нужно будет продумывать оптимальную структуру запросов/экономии памяти.

Вообще, конечно, я сам виноват, что кодю на PHP толком его не изучив, но ведь в том и прелесть (казалось) PHP, что там изучать нечего, а тут вылазит блин сюрприз за сюрпризом :)

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Это еще почему?

как минимум когда смотришь в профилировщике это первые источники тормозов. Циклы лучше избегать и тщательно оптимизировать - выносить все вычисления за пределы. В сабж на каждой итерации сохраняется некоторый контекст, расходуются ресурсы. С точки зрения php, да и любого языка практически - циклы ресурсоемки. Замечено, если тело цикла обернуть в отдельную функцию - потребление становится более компактным.

Используй SplFixedArray, SplTypes и если возможно сборщики мусора.

Память, если не нужен элемент освобождай явно.

$m = null;
unset($m);

На моей задаче возникла вот такая вот ошибка по памяти и я решил разобраться...

Проблемы с памятью - катализатор, что в архитектуре что-то не так. Для самоуспокоения смотри xdebug trace memory, но лучше улучшать алгоритм, найти более эффективный подход. Массивы могут потреблять много памяти и создавать ненужные копии при изменениях. Попробуй для дерева использовать объекты.

swwwfactory ★★
()

Allowed memory size of 134217728 bytes exhausted

134217728 / (900*1024) = 145.

Не так уж много, с учётом того, что это у нас ассоциативный массив (хэш), в котором хранятся индексы (вплоть до 6 знаков) в строковом виде (стоп-байт, указатели) и собственно строки.

Вот материалы для размышлений:

http://habrahabr.ru/post/134784/
http://habrahabr.ru/post/161629/
http://habrahabr.ru/post/162685/

KRoN73 ★★★★★
()
Ответ на: комментарий от pihter

мне не 100Мб букв хранить в реальной задаче нужно, а большой иерархический массив.

Это уже «числодробильная» функция. PHP тут не самый подходящий инструмент (как и Perl, Ruby...). Смотреть надо (если не хочется с Си связываться) в сторону Java или хотя бы Python.

KRoN73 ★★★★★
()
Ответ на: комментарий от pihter

Капец, какой толк от иерархических массивов, если в них ничего не влазит?

12 лет на PHP программирую, но задачи возни с сотнями мегов строк ни разу не возникало :D

Кто в курсе, на рядовом хостинге сколько мемори лимит обычно ставят?

И снова не тот инструмент. «Рядовой хостинг» и сотни мегабайт строк — это неверное сочетание.

Так и стоит по дефолту 128Мб?

Я на своих боевых проектах часто до 64Мб урезаю :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Смотреть надо (если не хочется с Си связываться) в сторону Java или хотя бы Python.

Собственно если присмотреться, то это задача эффективного стореджа на 100мб для букв.

swwwfactory ★★
()
Ответ на: комментарий от zz

Суть треда в том что у него 4 мегабайта букв, а не 100.

с 25-кратным запасом :)

swwwfactory ★★
()
Ответ на: комментарий от swwwfactory

Циклы лучше избегать и тщательно оптимизировать - выносить все вычисления за пределы.

Ну ты мне прям глаза открыл :) я уж решил что ты какую-то еще страшную тайну знаешь, которую я не знаю (в случае с PHP - не удивился бы)

Используй SplFixedArray, SplTypes

Не, оно, конечно, помогло бы, но не кардинально.

Память, если не нужен элемент освобождай явно.

Уже.

Проблемы с памятью - катализатор, что в архитектуре что-то не так.

Если ты не имел в виду слово «индикатор», то я ничего не понял :)

но лучше улучшать алгоритм, найти более эффективный подход

А чего тут улучшать? Делать более частые запросы к БД, меньше хранить в оперативке, терять в скорости.

Попробуй для дерева использовать объекты.

Хочешь сказать что один объект меньше памяти займет, чем одна запись в массиве?

pihter ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Не так уж много, с учётом того, что это у нас ассоциативный массив (хэш), в котором хранятся индексы (вплоть до 6 знаков) в строковом виде (стоп-байт, указатели) и собственно строки.

Да, объяснили уже.

Вот материалы для размышлений

За материалы спасибо, осилю как-нить на обеде.

pihter ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Смотреть надо (если не хочется с Си связываться) в сторону Java или хотя бы Python.

Да ну, для моих задач это уж слишком, просто надо отказаться от моей «гениальной оптимизации» - делать дамп БД сразу при запуске скрипта и, в дальнейшем, работать только с дампом. :)

pihter ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

12 лет на PHP программирую, но задачи возни с сотнями мегов строк ни разу не возникало :D

Да вот в том-то и дело что там сотнями и не пахнет! У этой БД в текстовом виде (дамп в SQL) размер никогда не превышал 4 Мб, я так прикинул, ну даже если предположить что она может быть 40 Мб, то все равно должно легко влазить - а вот фиг там! Я жестоко обломлен :)

И снова не тот инструмент. «Рядовой хостинг» и сотни мегабайт строк — это неверное сочетание.

Да я уже и подумал что мне проще эту проблему решить заказывая виртуальный хостинг...

Я на своих боевых проектах часто до 64Мб урезаю :)

Это еще зачем? Памяти на сервере жалко или для самоконтроля? :)

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Это еще зачем? Памяти на сервере жалко или для самоконтроля? :)

Когда PHP-процессу выдаёшь много памяти, он может сильно распухнуть. И на большом количестве обработчиков php-fcgi или php-fpm при активной работе сервера памяти может начать не хватать, хотя реально занято будет не так много. Поэтому предпочитаю ставить поменьше, сколько уверенно хватит на 99% запросов. А 1%, который начнёт сыпаться — это да, как раз, к самоконтролю. Значит, оптимизировать что-то надо :)

KRoN73 ★★★★★
()
Ответ на: комментарий от pihter

Ну ты мне прям глаза открыл :) я уж решил что ты какую-то еще страшную тайну знаешь, которую я не знаю (в случае с PHP - не удивился бы)

Порою в циклах лепят очевидные грабли типа вычислений констант 1000-раз и т.п...

Если знаешь, то почему в цикле делаешь объявление global? Смысл?

Объяви перед циклом один раз. Насчет тайн, возможно ничего там такого нет (лень поднимать архивы), но для foreach все далеко не тривиально. По инерции притянул туда и for, хотя от него так-же вероятно ожидать сюрпризы - лень лезть в тему php-internals и смотреть opcode. Когда foreach дает сюрпризы, то и for его дружок. Если есть возможность, то вместо цикла используй array_walk, array_walk_recursive, array_map и в качестве callback используй обычную функцию + (для array_walk* параметр для контекста в виде stdClass или свой кастомный объект)

Если ты не имел в виду слово «индикатор», то я ничего не понял :)

индикатор то-же, но и катализатор еще :) В данном случае, когда идет агрессивное потребление памяти таким способом процесс вскрытия косяков в архитектуре ускоряется в разы. Это как слегка нагреть реактивы или добавить немножко катализатора... Но это хорошо - чем раньше ты обнаружишь проблему...

А чего тут улучшать? Делать более частые запросы к БД, меньше хранить в оперативке, терять в скорости.

Частые запросы то-же не приветствуются, простительно если запросы идут через prepare или БД оптимизирована на чтение. Десяток запросов сделать ты всегда успеешь.

Хочешь сказать что один объект меньше памяти займет, чем одна запись в массиве?

Если вместо массива ты будешь передавать объект-контейнер, где живет массив, то ожидаемо снижение потребления или поэкспериментировать с ArrayObject. Избавься от globals - еще они источник тормозов.

swwwfactory ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.