Comments 67
Это просто леген… Подожди-подожди!
Рождество в стиле PHP.
А где можно найти весь список «легендарных коммитов» — гугление не помогает
это победа.
Ура, товарисчи!
Руки сами тянутся добавить gc_disable в контроллер ;)
Надо будет попробовать эту новую «супер-фичу»
Эх, если бы php был «standalone» для web-приложений из коробки…
Походу сейчас гитхаб висит от наплыва читателей…
Надо будет попробовать эту новую «супер-фичу»
Эх, если бы php был «standalone» для web-приложений из коробки…
Походу сейчас гитхаб висит от наплыва читателей…
Скорее от этого он у вас висит habrahabr.ru/post/244813/
Вы правы — это. Хотя это совершенно несправедливо. За что???
github — террористический ресурс, рассылающий детскую порнографию. Вы что, не знали?
ага, и вот на гиктаймсе есть подтверждение: geektimes.ru/post/242306/#comment_8175536
Я бы рекомендовал перед этим сделать ini_set('memory_limit',0)
Не травите душу про github
Проверил. Ощутимого прироста нет. Хинт весьма специфичный.
Читаю комментарии на гитхабе через аномайзер… Ощущаю какие то странные эмоции… Спасибо Роскомнадзор! Как же хорошо что я перешёл на битбакет.
Это не решение. Добровольный проброс https-каналов через третьих лиц лишь усугубляет ситуацию — ненамеренно можно открыть доступ к приватным данным или аккаунтам.
Извиняюсь за оффтопик конечно.
Извиняюсь за оффтопик конечно.
Я вынужденно перешёл с тарифа за $5 в DigitalOcean на тариф за $10 — именно из-за того, что composer на 512 мегабайтах не фурычил.
Судя по бенчмарку в конце поста, помимо ускорения работы, отключение GC ещё и уменьшает потребление памяти!
Может, попробовать откатиться назад на $5?..
Судя по бенчмарку в конце поста, помимо ускорения работы, отключение GC ещё и уменьшает потребление памяти!
Может, попробовать откатиться назад на $5?..
На крайний случай всегда есть очень хардкорный вариант, «по старинке» пушить папочку вендоров прямо из локалки по sftp\ftp ;)
Ага, у меня весь проект занимает 12 МБ, public занимает ещё 20 МБ. А vendor — 160 МБ!
Кстати, пошёл мерить папки, обнаружил, что var/cache/dev/profiler весит 480 МБ! Пойду прибью его…
Кстати, пошёл мерить папки, обнаружил, что var/cache/dev/profiler весит 480 МБ! Пойду прибью его…
Хм, у меня были проблемы с GitLab на тарифе за $10 (git'у иногда не хватало оперативки при загрузку больших diff'ов), но проблему я решил не переходом на более дорогой тариф, а просто включением свопа.
Зачем платить больше?
cat updatedeps.sh
#!/bin/bash
# Enable swap, run composer update, disable swap. That's it.
if [ ! -f /swapfile ]; then
sudo dd if=/dev/zero of=/swapfile bs=1024 count=1024m
sudo mkswap /swapfile
fi
sudo swapon /swapfile
composer update
sudo swapoff /swapfile
хм… Интересно! Спасибо!
А зачем дизейблить своп? У DO SSD, это быстро + лучше уж своп, чем не работает совсем.
Видимо, в большинстве случаев из за использования свопа система начинает работать настолько медленно, что лучше уж дать прибить composer, чем, считай, повесить весь сервер.
И судя по всему это связано с ограничением IOPS в вм.
Есть такой параметр swappiness, который можно несколько понизить. Тогда совсем фейла когда память скушается не будет, но и память будет предпочитать свопу.
1. Не на SSD.
2. Если свопа нет и память кончилась, сервер гарантировано повиснет. Опробовано, по крайней мере, на Ubuntu Server.
2. Если свопа нет и память кончилась, сервер гарантировано повиснет. Опробовано, по крайней мере, на Ubuntu Server.
Вам даже на «composer install» с имеющимся composer.lock памяти не хватает? Насколько я знаю, это очень щадящий режим, который не производит разрешения зависимостей, на что и уходит так много памяти.
Честно говоря, я не пробовал install делать. Я делаю update — пока сайт в разработке, пусть всегда будут свежие версии пакетов.
Сейчас вы подсказали, нужно будет попробовать делать update у себя на dev-машине, composer.lock добавить в git, и делать install на test-сервере.
Я уже решил проблему включением swap, как подсказал Xobb, только я делал своп не его скриптом (не заработал), а по оф.инструкции DO.
Сейчас вы подсказали, нужно будет попробовать делать update у себя на dev-машине, composer.lock добавить в git, и делать install на test-сервере.
Я уже решил проблему включением swap, как подсказал Xobb, только я делал своп не его скриптом (не заработал), а по оф.инструкции DO.
Да, вы думаете в правильном направлении.
Я правильно понимаю что вы еще не сталкивались с «веселыми приключениями с разными версиями пакетов на разных машинах»?
Сильно сократить объём используемой памяти композером при обновлении можно если прописать более поздние версии пакетов в composer. Т.е. если у вас, например, в зависимостях стоит symfony: dev-master, то это несколько десятков версий для symfony и по нескольку десятков зависимостей для каждого компонента symfony — а это по отдельному Package на версию.
Если же прописать версию как ~2.6 (где 2.6 — самая свежая версия), то при вычислении зависимостей не будут анализироваться версии меньше 2.6 (если только у других пакетов нет таких зависимостей или если с последними версиями не получится удовлетворить зависимости и придётся перебирать более старые версии). Ну и minimum-stability: stable тоже помогает.
Можно почитать stof об этом.
Если же прописать версию как ~2.6 (где 2.6 — самая свежая версия), то при вычислении зависимостей не будут анализироваться версии меньше 2.6 (если только у других пакетов нет таких зависимостей или если с последними версиями не получится удовлетворить зависимости и придётся перебирать более старые версии). Ну и minimum-stability: stable тоже помогает.
Можно почитать stof об этом.
Рискую быть оплеванным ликующей толпой, но не могу не сказать. Да, выполнение скрипта ускорилось в разы, но что действительно это значит?
1) Отключать GC — плохо. «Но расход памяти не увеличивается, значит циклических ссылок нет, так какая разница?» На самом деле они есть до этого места. И даже если сейчас после выключения GC их нет, не факт, что они не появятся в будущем. Теперь каждый комит надо проверять на наличие циклических ссылок.
2) Composer — пакетный менеджер. Что он делает, что ему в памяти постоянно нужно держать сотни мегабайт и создавать огромное количество объектов? Отключение сборщика не устранило проблему, а лишь отложила её решение.
3) Как так получается, что отключение сборки мусора ускоряет выполнение скрипта в десять раз? Что не так со сборщиком мусора? В нем нет поколений объектов, и ему приходится перебирать все объекты при каждой сборке?
1) Отключать GC — плохо. «Но расход памяти не увеличивается, значит циклических ссылок нет, так какая разница?» На самом деле они есть до этого места. И даже если сейчас после выключения GC их нет, не факт, что они не появятся в будущем. Теперь каждый комит надо проверять на наличие циклических ссылок.
2) Composer — пакетный менеджер. Что он делает, что ему в памяти постоянно нужно держать сотни мегабайт и создавать огромное количество объектов? Отключение сборщика не устранило проблему, а лишь отложила её решение.
3) Как так получается, что отключение сборки мусора ускоряет выполнение скрипта в десять раз? Что не так со сборщиком мусора? В нем нет поколений объектов, и ему приходится перебирать все объекты при каждой сборке?
Ура! Наконец-то хоть кто-то начал задавать правильные вопросы. Я отвечу на второй, на мой взгляд он самый важный.
Композеру нужно столько памяти, потому что он туда(в память) закачивает скачанные пакеты (.tar.gz). Затем он там туда же(да да, в память) их распаковывает в поисках composer.json и на его основе скачивает зависимые пакеты(опять же в память).
Мы пытались сделать пакет размером в 700МБ. Мы падали по memory_limit в 2G.
Дело не а PHP и его GC. Дело в самом композере
Композеру нужно столько памяти, потому что он туда(в память) закачивает скачанные пакеты (.tar.gz). Затем он там туда же(да да, в память) их распаковывает в поисках composer.json и на его основе скачивает зависимые пакеты(опять же в память).
Мы пытались сделать пакет размером в 700МБ. Мы падали по memory_limit в 2G.
Дело не а PHP и его GC. Дело в самом композере
То есть коммит «Сделать быстро» для композера все-таки добавит подводных камней в его работе?
UFO just landed and posted this here
Странная реализация… Зачем всё скачивать и распаковывать в память, если есть /tmp? Я не сталкивался с Composer, но для примера apt-get не хавает память гигабайтами, а ворочает пакетами, размеры которых скорее всего больше чем у Composer. А если у пакета тысяча зависимостей — тушите свет? А если у меня memory_limit скажем 128Мб или меньше? Что то тут не так.
Я не знаю, может для совместимости в windows, в котором нет /tmp, tar, gz а есть php extension
В windows есть свой temp. Как там в bat, переменная %temp%? Я не считаю это проблемой, которая может как либо помешать в принципе. А на счет архивов я без понятия, но если оно сейчас распаковывается, значит всё есть и всё работает и остаётся сбросить на диск. Надо просто посмотреть код распаковки, хотя у меня сейчас совсем нет желания это делать.
Пакеты целиком закачиваются что ли? Не через буфер, записывая на диск периодически?
3) Как так получается, что отключение сборки мусора ускоряет выполнение скрипта в десять раз? Что не так со сборщиком мусора? В нем нет поколений объектов, и ему приходится перебирать все объекты при каждой сборке?Резкое падение эффективности сборки мусора в условиях нехватки памяти — это общая беда всех сборщиков мусора. Даже при наличии поколений объектов, десятая сборка мусора подряд в любом случае затащит все объекты в перманентное поколение.
Отсюда и ответ на пункт 1. Да, отключать GC — плохо. Но это единственный способ утилизировать 100% памяти без тормозов.
Даже при наличии поколений объектов, десятая сборка мусора подряд в любом случае затащит все объекты в перманентное поколение.Да, затащит. И они будут проверяться значительно реже, чем в других поколениях. В том и смысл.
Пример на Питоне
import time
import gc
gc_counters = [0, 0, 0]
def gc_cb(phase, info):
gc_counters[info['generation']] += 1
# gc.disable()
gc.callbacks.append(gc_cb)
class Node(object):
def __init__(self, children):
self.children = children
@classmethod
def tree(cls, depth, numchildren):
if depth == 0:
return []
return [
cls(cls.tree(depth-1, numchildren))
for _ in range(numchildren)
]
start = time.time()
ref = Node.tree(9, 5)
print('>>>', gc_counters, time.time() - start)
Результаты при включенном сборщике мусора:
>>> [19180, 1744, 34] 10.722499132156372
При выключенном:
>>> [0, 0, 0] 4.036884069442749
Запусков нулевого поколения — 19k, первого — 1,7k, второго — всего 34. Т.е разница в производительности всего в 2,5 раза, хотя это синтетический тест и только и делает, что создает объекты.
Такой же пример на PHP
<?php
class Node {
public function __construct($children) {
$this->children = $children;
}
public static function tree($depth, $numchildren) {
if ($depth == 0) {
return [];
}
$children = [];
for ($i=0; $i < $numchildren; $i++) {
$children[$i] = new self(self::tree($depth - 1, $numchildren));
}
return $children;
}
}
ini_set('memory_limit', '-1');
// gc_disable();
$start = microtime(true);
$ref = Node::tree(9, 5);
print '>>> '.(microtime(true) - $start)."\n";
В PHP нельзя посмотреть запуски сборщика без перекомпиляции, но результаты очень похожи:
>>> 7.6519370079041
>>> 3.9215548038483
Поэтому скорее всего там сборщик по поколениям, возможно с другими лимитами. И поэтому еще более непонятно, что такого делает Composer, что умудряется выдать результат хуже, чем синтетический тест.
Вы создаете всего лишь 60000 объектов, этого мало. Проблемы с GC начинаются именно при исчерпании памяти.
Ка вы это насчитали? На каждом уровне создается 5 в степени глубины нод. Т.е. всего:
5**9 + 5**8 + 5**7 + 5**6 + 5**5 + 5**4 + 5**3 + 5**2 + 5**1 = 2 441 405 нод
Плюс у каждой ноды есть массив детей, даже если он пустой. Т.е. всего 4 882 811 объектов.
В PHP это занимает 1,37 ГБ памяти, а Питоне 545 МБ памяти. Мало?
5**9 + 5**8 + 5**7 + 5**6 + 5**5 + 5**4 + 5**3 + 5**2 + 5**1 = 2 441 405 нод
Плюс у каждой ноды есть массив детей, даже если он пустой. Т.е. всего 4 882 811 объектов.
В PHP это занимает 1,37 ГБ памяти, а Питоне 545 МБ памяти. Мало?
Так ответы на эти вопросы есть в комментарии того чувака, который и предложил отключать gc (https://github.com/composer/composer/pull/3482#issuecomment-65199153):
@naderman, generally if you create many many objects, you pretty much want to always disable GC. This is because PHP has a hard-coded limit (compile-time) of root objects that it can track in its GC implementation (I believe it's set to 10000 by default).
If you get close to this limit, GC kicks in. If it cannot clean-up, it will still keep trying in frequent intervals. If you go above the limit, any new root objects are not tracked anymore, and cannot be cleaned-up whether GC is enabled, or not.
This might also be why the memory consumption for bigger projects does not vary. If GC is enabled, it's just not working anymore even if there is potentially something to clean-up. For smaller projects, you might see a memory difference.
+ вот еще
@naderman the GC in PHP is only used to be able to handle circular object graphs. For non-circular graphs, the refcount is enough to destroy values without needing the GC. So as long as you avoid circular object graphs in objects used a lot in Composer, you could indeed disable it.
@naderman, generally if you create many many objects, you pretty much want to always disable GC. This is because PHP has a hard-coded limit (compile-time) of root objects that it can track in its GC implementation (I believe it's set to 10000 by default).
If you get close to this limit, GC kicks in. If it cannot clean-up, it will still keep trying in frequent intervals. If you go above the limit, any new root objects are not tracked anymore, and cannot be cleaned-up whether GC is enabled, or not.
This might also be why the memory consumption for bigger projects does not vary. If GC is enabled, it's just not working anymore even if there is potentially something to clean-up. For smaller projects, you might see a memory difference.
+ вот еще
@naderman the GC in PHP is only used to be able to handle circular object graphs. For non-circular graphs, the refcount is enough to destroy values without needing the GC. So as long as you avoid circular object graphs in objects used a lot in Composer, you could indeed disable it.
Вот отличная статья по теме: blog.ircmaxell.com/2014/12/what-about-garbage.html
Sign up to leave a comment.
Как Composer на 70% ускорили