N>>Вопрос же, почему вообще VM падает вместо того, чтобы забыть сообщение, делится на две части: N>>1. Почему Mach-styled VM, лежащие в основе всех современных ОС, плохо справляются с переполнением памяти и почему многие проектировщики системных VM отказываются вообще что-то с этим делать. N>>2. Почему в Erlang не желают признавать возможность негарантированной посылки сообщения внутри одной VM. N>>Первое, если интересно, могу развернуть, второе — пусть ещё кто-то расскажет.
LD>Интересно конечно развернуть. А то на rsdn редко серьезные проблемы обсуждаются.
В юниксах применяется Mach-styled VM (VM — тут одновременно virtual machine + virtual memory). Её основные концепции и подходы:
1. RAM есть кэш диска. Диск (говоря в общем, плоский диск или файл) — в её терминах — backing store. В диск входит своп, если нет иного источника; но для многих страниц — есть, например, для бинарников, библиотек... При этом может быть двухслойное (иногда больше) устройство — изменённая версия для одного процесса или группы, и неизменённая — на диске. Например, это делается для библиотек с PIC (positional-independent code), которыми сейчас являются практически все SO (shared object) и DLL. Все стандартные механизмы стараются использовать отображение файлов в память: это относится как минимум к бинарникам и библиотекам. Результатом является то, что, например, система может освободить все неактивные страницы неизменённых данных (да и загружает изначально только те, что нужны), оставив только специфичные для процесса; кроме того, неизменённые страницы хранятся в RAM в одной копии, что резко сокращает затраты памяти. В некоторых случаях она может объединять и изменённые, если они идентичны, но это уже очень дорогая проверка.
Эта часть устройства общая и с Windows (с поправкой на отсутствие fork'а). В случае fork(), все страницы двух порождённых процессов общие, и делятся только в случае изменения в одном из. Последствия этого подхода см. ниже.
2. Допускается lazy commit, то есть формальное выделение памяти без фактического её обеспечения в backing store. Фактическое обеспечение возникает по факту первой записи в область, до того она пуста (и можно читать — чтение даёт нулевые байты).
Что роднит эти два пункта — это то, что фактические затраты памяти процессом могут быть выражены как несколько совершенно разных цифр:
1) общий объём виртуальной памяти, известной процессу
2) суммарный объём страниц, изменённых только в данном процессе и в случае сброса на диск уходящих в своп
3) суммарный объём страниц, изменённых в данном процессе или в группе процессов и в случае сброса на диск уходящих в своп
4) суммарный объём резидентных (т.е. находящихся в RAM) страниц
и ни одна из них не является точным отражением затрат памяти данным процессом. Всякие ps обычно показывают (1) как VSZ и (4) как RSS. Показатели (2) и (3) никак не отражаются напрямую, их надо сложно вычислять.
Память, занятая несколькими процессами, не больше суммы их VSZ, но обычно меньше. Насколько меньше — опять-таки надо считать.
Не очень простой, но показательный пример, как это происходит. Представим себе жизненный цикл процесса:
1. Родились. В память отображён бинарник и библиотеки. VSZ может быть
дофига. RSS – только то, что изменилось (таблицы импорта в библиотеках
плюс минимум данных и стека).
2. Замапили гигабайтный файл. VSZ вырос на 1G. RSS не поменялся.
3. Прочитали этот файл в памяти. VSZ не изменился. RSS – вырос на
гигабайт или чуть меньше (если не сильно тесно).
4. Подумали о жизни. В это время другим процессам потребовалась
память. Половину кэша файла в памяти продискарили, VSZ не изменился,
RSS упал на 500M.
5. Форкнули из себя 10 копий. Все копии получили одну и ту же память
кроме параметров в стеке (возвращаемый pid). Суммарный VSZ равен 10G,
цена этой информации – 0 целых хрен десятых Суммарный RSS стал 5G,
при этом реально в памяти занято только 500M.
6. Одна из копий аллоцировала 100M и записала их данными обработки.
Её VSZ и RSS выросли на 100M. Такой же рост у суммы.
7. Эта копия форкнулась. Суммарные VSZ и RSS выросли на 1.1G, реальных
затрат не поменялось.
8. Новый форк переделал все данные в 100M области. Суммарные VSZ и RSS
не поменялись, фактические затраты обоих выросли на 100M за счёт
хранения копии.
9. Ещё кому-то потребовалась память и система продискардила остаток
большого файла в памяти. Суммарный VSZ не изменился. Суммарный RSS
упал на 5.5G. Фактические затраты в памяти сократились на 500M.
Думаю, понятно, что в этой каше нихрена не ясно, если не заставить всех пересчитать. Какие из этого последствия? Последствия, например, те, что фактическое исчерпание системной виртуальной памяти (RAM+своп) совершенно не обязательно вызвано какими-то явными действиями процесса по получению памяти. Процесс может работать, меняя данные в своей памяти, и "внезапно" (tm), изменив страницу, до того общую с другими процессами, потребовать у системы место под собственную копию страницы... места нет, и процесс получает по голове. Так как пути просигнализировать процессу в этом случае нет (штатно), в дело вступает OOM killer (название из Linux) и убивает (SIGKILL, то есть вообще ничего не дают сделать) или этот процесс, или другой — по своим критериям (достаточно сложным).
Всё это я описываю ради одного вывода: VM в принципе позволяет, чтобы процесс был убит в произвольный непредсказуемый момент за действия, которые формально никак не связаны с запросом ресурса (память). К чему это приводит? Это приводит к тому, что конструкция VM резко усиливает аргументы в пользу того, что
1) не имеет смысла вообще предполагать нехватку памяти, потому что после этого уже ничего работать не будет
2) следует делать lazy commit, потому что так проще, а если реально не хватит памяти — см. (1)
причём эти пункты распространяются в том числе и на случаи, когда проблему выделения можно и нужно ловить (ограничение не VM в целом, а превышение явно известного процесса (см. setrlimit()) на процесс или на группу).
Фактически, все современные сложные системы под Unix пишутся таким образом — что отказ любого выделения памяти становится фатальным. И это одна из тех вещей, что мне очень не нравятся в этом (Unix) мире. Windows в этом плане более управляема. Некоторые флаворы имеют защиты против такого: например, HP-UX в случае исчерпания общей памяти конвертирует часть памяти процессов в файлы в /tmp. Но даже этот костыль, к сожалению, не является общим методом.
Говоря более общим образом, виртуальная память здесь является одним из критических ресурсов. Критическими я называю те ресурсы, исчерпание которых препятствует даже аккуратному "свёртыванию" работы, потому что работы по освобождению могут потребовать нового выделения ресурсов. К критическим относятся как минимум следующие:
— виртуальная память
— дескрипторы открытых файлов
— списки процессов и нитей
Правильно спроектированная система должна позволять процессу держать резерв такого ресурса, который далее может быть применён для операций свёртывания. Для памяти это означает автоматическое перемещение страниц из резерва в основной пул в случае невозможности дальнейшего выделения. К сожалению, нигде в известных мне ОС такое не реализовано.
Тут моё более раннее описание проблемы. Это ещё 2002-й, при том, что в первый раз столкнулся ещё в 99-м. Радикального улучшения по сути до сих пор нет нигде. Единственное, что видел — более грамотное управление lazy commit'ом (например, /proc/sys/vm/overcommit_* в Linux). С моей точки зрения это не более, чем костыли, хоть и помогает в работе программам, которые не пытаются сами управлять памятью.
Возвращаясь к Erlang — думаю, из описанного понятно, что ситуация нехватки памяти рассматривается наиболее топорно, а обеспечение надёжности перекладывается на внешнюю среду. Упала VM — чёрт с ней, запустим снова. И так по кругу. На одной из установок, пока мы не сделали грамотное расщепление нагрузки между VM'ами, некоторые падали раз в 5-10 минут.
LD> А также интересно, возможна ли реализация VM без кучи, а чисто на стэке например.
Erlang — нет, такого не позволит. Насчёт других не буду ничего говорить, слишком сложная тема.
K>>При интенсивном обмене сообщениями не надо копировать данные из между памятью процессорами, можно сослаться на один и тот же блок памяти. LD>Это достаточно оригинальное утверждение, которое, уверен, не все здесь поняли. Может поясните?
Здравствуйте, Mamut, Вы писали:
M>В текущей ветке Эрланга — оптимизированый selective receive, могущий работать даже с гигантскими очередями сообщений
Честно говоря, я бы предпочёл всё-таки видеть разные очереди и задавать явный предел глубине просмотра. По такому типу:
receive
ControlMessage from control ->
process_control_message(ControlMessage);
{control, ControlMessage} from default maxdepth 1000 ->
process_control_message(ControlMessage);
{data, Data} ->
process_data(Data);
_Other ->
%% эт че за шняга?
log_strange("got unknown: ~p~n", [_Other])
end
а эти костыли выглядят очень странно.
Но если OTP'шники уверены, что они таким образом потянут обработку толстых очередей и внеочередное выдёргивание нужного — флаг им в руки...
M>После некоторых грубых тестов gen_server:call() показывает O(1) даже при миллионе сообщений в клиентской очереди.
Это в клиентской очереди кого — вызывающего или вызываемого? Разница существенная.
Здравствуйте, LelicDsp, Вы писали:
LD>Это конечно неплохо, но лучше бы они подумали как решить главную проблему — что при переполнении очереди падает вся VM.
Против этого можешь перекомпилировать Erlang в другую модель памяти. Текущая по умолчанию — держит все данные процесса одним непрерывным куском и аллоцирует новый по необходимости (расширение или сжатие). Можно сделать одну кучу на всех.
LD>>Это конечно неплохо, но лучше бы они подумали как решить главную проблему — что при переполнении очереди падает вся VM.
N>Против этого можешь перекомпилировать Erlang в другую модель памяти. Текущая по умолчанию — держит все данные процесса одним непрерывным куском и аллоцирует новый по необходимости (расширение или сжатие). Можно сделать одну кучу на всех.
Непрерывным? Я думал, что кусочков там много, и они по необходимости добавляются. Причем у каждого потока — свой сегмент памяти.
ОК. В любом случае, как это объясняет падение VM?
Ну нехватает памяти для очередного сообщения. Что, сложно выделить еще один блок памяти и складывать новые сообщения туда?
И как это решается использованием одной кучи на все нити?
LD>>>Это конечно неплохо, но лучше бы они подумали как решить главную проблему — что при переполнении очереди падает вся VM.
N>>Против этого можешь перекомпилировать Erlang в другую модель памяти. Текущая по умолчанию — держит все данные процесса одним непрерывным куском и аллоцирует новый по необходимости (расширение или сжатие). Можно сделать одну кучу на всех.
RD>Непрерывным? Я думал, что кусочков там много, и они по необходимости добавляются. Причем у каждого потока — свой сегмент памяти.
Что такое "поток" и "сегмент"? Прошу не использовать двусмысленные термины. В Эрланге есть процессы, и у них, таки да, в модели памяти по умолчанию — у каждого процесса непрерывная область в общем пространстве. Является ли это "сегментом" для Вас — не знаю, мне этот термин не нравится.
RD>ОК. В любом случае, как это объясняет падение VM? RD>Ну нехватает памяти для очередного сообщения. Что, сложно выделить еще один блок памяти и складывать новые сообщения туда?
Представим себе, что некоторый процесс имел "сегмент" в 400MB. В нём не хватает места. VM пытается выделять следующий. Размер увеличивается в какое-то количество раз, для простоты скажем — удваивается. Он пытается выделить 800MB. Но на системный процесс Erlang VM в целом выделено 1GB, и куска в 800MB там не получить, тем более непрерывного. "Хи-хи чпок" — вся VM падает.
Мы это проходили неоднократно, пока не подобрали разумный баланс между порождением и очисткой. И то — при таких объёмах спасает только ручной тюнинг параметров.
RD>И как это решается использованием одной кучи на все нити?
Теперь ещё и "нити". Что Вы называете нитями? Процессы внутри VM? Да, может решить — за счёт того, что нет моментов, когда требуется тройной размер, причём цельными кусками.
Допустим в Эрланг по-умолчанию у каждого процесса есть своя, непрерывная, область памяти. Пусть также в случае нехватки памяти для процесса VM выделяет для процесса в два раза больший кусок памяти (а старый освобождает).
Тогда понятно, что если сильно разростается один из процессов, то в конце-концов память заканчивается и программа мрёт. Но это — не удивительно.
Непонятно, как при этом поможет альтернативный вариант, когда у всех процессов — общая куча. Если один из процессов решает выделить себе много памяти, то в конце-концов памяти все равно не остается. В чем же польза?
Здравствуйте, Rtveliashvili Denys, Вы писали:
RD>Непонятно, как при этом поможет альтернативный вариант, когда у всех процессов — общая куча. Если один из процессов решает выделить себе много памяти, то в конце-концов памяти все равно не остается. В чем же польза?
При интенсивном обмене сообщениями не надо копировать данные из между памятью процессорами, можно сослаться на один и тот же блок памяти.
K>При интенсивном обмене сообщениями не надо копировать данные из между памятью процессорами, можно сослаться на один и тот же блок памяти.
Это достаточно оригинальное утверждение, которое, уверен, не все здесь поняли. Может поясните?
RD>>Непонятно, как при этом поможет альтернативный вариант, когда у всех процессов — общая куча. Если один из процессов решает выделить себе много памяти, то в конце-концов памяти все равно не остается. В чем же польза?
K>При интенсивном обмене сообщениями не надо копировать данные из между памятью процессорами, можно сослаться на один и тот же блок памяти.
Ну это, допустим, очевидно. Непонятно почему это лечит описанную выше проблему ("при переполнении очереди падает VM"). Разве при таком подходе не может возникнуть переполнение очереди с аналогичными последствиями? Если не может, то почему?
Здравствуйте, Rtveliashvili Denys, Вы писали:
RD>Хорошо, попробую перефразировать.
RD>Допустим в Эрланг по-умолчанию у каждого процесса есть своя, непрерывная, область памяти. Пусть также в случае нехватки памяти для процесса VM выделяет для процесса в два раза больший кусок памяти (а старый освобождает).
RD>Тогда понятно, что если сильно разростается один из процессов, то в конце-концов память заканчивается и программа мрёт. Но это — не удивительно.
RD>Непонятно, как при этом поможет альтернативный вариант, когда у всех процессов — общая куча. Если один из процессов решает выделить себе много памяти, то в конце-концов памяти все равно не остается. В чем же польза?
Пусть предел памяти на системный процесс — 1GB, эрланговый — занял 400MB. Надо добавить одно сообщение. Если оно будет аллоцировано в общей куче, потратим, например, байт 100. Если в куче процесса — потребуется 800MB, которых не дадут. В первом случае процесс выживет, во втором — нет.
Здравствуйте, Rtveliashvili Denys, Вы писали:
K>>При интенсивном обмене сообщениями не надо копировать данные из между памятью процессорами, можно сослаться на один и тот же блок памяти.
RD>Ну это, допустим, очевидно. Непонятно почему это лечит описанную выше проблему ("при переполнении очереди падает VM"). Разве при таком подходе не может возникнуть переполнение очереди с аналогичными последствиями? Если не может, то почему?
Я, кажется, понял (по крайней мере частично), что Вам непонятно. Вы сделали ложное допущение на основании непрямого ответа и теперь не можете понять, почему у Вас складывается такая картина.
Когда я отвечал про реаллокацию кучи, я не говорил, что смена модели памяти поможет избежать падения. Я объяснил, как именно падает — и не более того. Дальше уже были Ваши и ничьи домыслы о том, что смена модели поможет этого избежать.
На самом деле, смена модели памяти даёт не избежание проблемы, но делает поведение системы более ровным. Например, если предел памяти 1GB, а занято 800MB, в модели по умолчанию нельзя сказать, что будет от поступления всего лишь одного сообщения: выживет VM в целом или нет. В модели с общей кучей на всех — гарантированно выживет. То есть, мы получаем предсказуемость работы — за счёт её удорожания: например, тотальный GC по всей VM значительно дороже, чем VM в одном Erlang-процессе и тем более по сравнению с тем, что если процесс завершился, его кучу можно грохнуть не разбирая.
Вопрос же, почему вообще VM падает вместо того, чтобы забыть сообщение, делится на две части:
1. Почему Mach-styled VM, лежащие в основе всех современных ОС, плохо справляются с переполнением памяти и почему многие проектировщики системных VM отказываются вообще что-то с этим делать.
2. Почему в Erlang не желают признавать возможность негарантированной посылки сообщения внутри одной VM.
Первое, если интересно, могу развернуть, второе — пусть ещё кто-то расскажет.
N>Я, кажется, понял (по крайней мере частично), что Вам непонятно. Вы сделали ложное допущение на основании непрямого ответа и теперь не можете понять, почему у Вас складывается такая картина.
N>Когда я отвечал про реаллокацию кучи, я не говорил, что смена модели памяти поможет избежать падения. Я объяснил, как именно падает — и не более того. Дальше уже были Ваши и ничьи домыслы о том, что смена модели поможет этого избежать.
N>На самом деле, смена модели памяти даёт не избежание проблемы, но делает поведение системы более ровным. Например, если предел памяти 1GB, а занято 800MB, в модели по умолчанию нельзя сказать, что будет от поступления всего лишь одного сообщения: выживет VM в целом или нет. В модели с общей кучей на всех — гарантированно выживет. То есть, мы получаем предсказуемость работы — за счёт её удорожания: например, тотальный GC по всей VM значительно дороже, чем VM в одном Erlang-процессе и тем более по сравнению с тем, что если процесс завершился, его кучу можно грохнуть не разбирая.
Наверное, я уже успел забыть родной язык.
Следующее:
Утверждение: Это конечно неплохо, но лучше бы они подумали как решить главную проблему — что при переполнении очереди падает вся VM.
Ответ: Против этого можешь перекомпилировать Erlang в другую модель памяти. Текущая по умолчанию — держит все данные процесса одним непрерывным куском и аллоцирует новый по необходимости (расширение или сжатие). Можно сделать одну кучу на всех.
В утверждении речь шла о решении проблемы "при переполнении очереди падает вся VM".
Ответ предлагал использовать модель Shared Heap как вариант борьбы с проблемой. Но, как оказывается, это не _решение_, а просто некритичное изменение поведения.
N>Вопрос же, почему вообще VM падает вместо того, чтобы забыть сообщение, делится на две части: N>1. Почему Mach-styled VM, лежащие в основе всех современных ОС, плохо справляются с переполнением памяти и почему многие проектировщики системных VM отказываются вообще что-то с этим делать.
Тут видимо много причин, и одна из них — сложность с выяснением того, какой процесс нужно разбудить, чтобы очереди разгребались а не росли.
N>2. Почему в Erlang не желают признавать возможность негарантированной посылки сообщения внутри одной VM. N>Первое, если интересно, могу развернуть, второе — пусть ещё кто-то расскажет.
Ну а негарантированная посылка сообщений с одной стороны усложняет логику работы (приходится постоянно проверять, послалось сообщение или нет), а с другой стороны все равно не решает проблему №1 в общем случае.
P.S. Эрлангов подход, когда при необходимости расширяют кучу процесса просто созданием еще одной и копированием в нее — не единственный возможный. В том же GHC куча не является линейным адресным пространством и состоит в основном из небольших блоков. В результате описанной проблемы с смертью при добавлении одного сообщения в переполенный 400M блок просто нет.
N>Вопрос же, почему вообще VM падает вместо того, чтобы забыть сообщение, делится на две части: N>1. Почему Mach-styled VM, лежащие в основе всех современных ОС, плохо справляются с переполнением памяти и почему многие проектировщики системных VM отказываются вообще что-то с этим делать. N>2. Почему в Erlang не желают признавать возможность негарантированной посылки сообщения внутри одной VM. N>Первое, если интересно, могу развернуть, второе — пусть ещё кто-то расскажет.
Интересно конечно развернуть. А то на rsdn редко серьезные проблемы обсуждаются. А также интересно, возможна ли реализация VM без кучи, а чисто на стэке например.
N>Извините, мне кажется, что Вы просто не хотите думать над совершенно элементарными вещами. N>Пусть предел памяти на системный процесс — 1GB, эрланговый — занял 400MB. Надо добавить одно сообщение. Если оно будет аллоцировано в общей куче, потратим, например, байт 100. Если в куче процесса — потребуется 800MB, которых не дадут. В первом случае процесс выживет, во втором — нет. N>Неужели это действительно непонятно? Мне казалось, что звание программиста подразумевает желание сделать элементарную мысленную симуляцию ситуации на один шаг вперёд. Я ошибаюсь и это теперь не обязательно?
Это не элементарные вещи. %) Это взаимное непонимание. Причем не обсуждаемых процессов, а высказываемых утверждений. И мысленные симуляции тут не причем.
K>>>При интенсивном обмене сообщениями не надо копировать данные из между памятью процессорами, можно сослаться на один и тот же блок памяти. LD>>Это достаточно оригинальное утверждение, которое, уверен, не все здесь поняли. Может поясните?
К>На эту тему есть старенькая работа Йеспера Вильхельмсона.
Статья интересная, но я боюсь не актуальная, с увеличением числа ядер глобальная куча становится все большим гемороем. Да и на одном ядре, я бы предпочел линейную скалабильность и выдерживание софт риалтайма пусть и с чуть меньшей производительностью.
Другое дело, что наверняка можно посылку сообщений оптимизировать, если бы компилятор был поумнее и понимал, например, что в посылающем сообщение потоке объект больше не используется. А это довольно часто случается.
Вы не понятно о чем спорите, говорите об одном и том же, только с разных сторон.
а) общая куча повышает "эластичность", позволяет отдельным процессам жрать больше памяти. ( хотя, если управлять отдельными кучами более гранулярно, мне кажется можно добиться того же эффекта.)
б) принципиально это ситуацию не меняет, если появился сильно жрущий процесс, рано или поздно все грохнется.
Здравствуйте, LelicDsp, Вы писали:
LD>Вы не понятно о чем спорите, говорите об одном и том же, только с разных сторон. LD>а) общая куча повышает "эластичность", позволяет отдельным процессам жрать больше памяти. ( хотя, если управлять отдельными кучами более гранулярно, мне кажется можно добиться того же эффекта.) LD>б) принципиально это ситуацию не меняет, если появился сильно жрущий процесс, рано или поздно все грохнется.
Хм. А есть VM которые в описанных вами условиях не грохнутся?
K>Хм. А есть VM которые в описанных вами условиях не грохнутся?
Я не специалист, могу сказать за джаву только — там скорее всего грохнется, но именно по причине общей кучи — куча процессов начнет получать OutOfMemory эксепшены, и большая часть их не сможет корректно отработать.
Касательно эрланга, наивное решение — просто убивать процесс который начинает жрать слишком много. Проблема была бы решена. Но это очень спорное решение с точки зрения идеологии.
С точки зрения прикладного приложения, в какой-то степени эту проблему можно решить используя полу-блокирующую посылку сообщений, как это сделано в disk_log. Посылающие сообщения процессы друг друга не блокируют, но один процесс не может послать сообщения, пока его предыдущее не отработалось. Как-то так мне помнится.
Надо уточнить про джаву — на моем опыте VM падала, но что бы из-за недостатка памяти — не помню. Потенциально если отработать все OutOfMemoryException, то программа может выжить
Здравствуйте, LelicDsp, Вы писали:
LD>Вы не понятно о чем спорите, говорите об одном и том же, только с разных сторон. LD>а) общая куча повышает "эластичность", позволяет отдельным процессам жрать больше памяти. ( хотя, если управлять отдельными кучами более гранулярно, мне кажется можно добиться того же эффекта.) LD>б) принципиально это ситуацию не меняет, если появился сильно жрущий процесс, рано или поздно все грохнется.
Я не знаю, насколько принципиально Ваше "принципиально", но, с моей точки зрения, процесс, который устойчиво потребляет уровень памяти менее лимита и у которого колебания этого уровня при работе не превышают единиц процентов, лучше, чем процесс, который систематически разбухает до высоких размеров и сдувается обратно. Второе хуже хотя бы тем, что приходится выставлять лимит не в 1.2-1.5 от суммарного потребления, как в случае устойчивых затрат памяти, а в 3-4 уровня, а это значит, что мы уже потеряли контроль над затратами памяти в процессе.
Здравствуйте, Rtveliashvili Denys, Вы писали:
N>>На самом деле, смена модели памяти даёт не избежание проблемы, но делает поведение системы более ровным. Например, если предел памяти 1GB, а занято 800MB, в модели по умолчанию нельзя сказать, что будет от поступления всего лишь одного сообщения: выживет VM в целом или нет. В модели с общей кучей на всех — гарантированно выживет. То есть, мы получаем предсказуемость работы — за счёт её удорожания: например, тотальный GC по всей VM значительно дороже, чем VM в одном Erlang-процессе и тем более по сравнению с тем, что если процесс завершился, его кучу можно грохнуть не разбирая. RD>Наверное, я уже успел забыть родной язык.
В данном частном случае согласен с этим выводом, см. ниже.
RD>Следующее:
RD>Утверждение: Это конечно неплохо, но лучше бы они подумали как решить главную проблему — что при переполнении очереди падает вся VM.
RD>Ответ: Против этого можешь перекомпилировать Erlang в другую модель памяти. Текущая по умолчанию — держит все данные процесса одним непрерывным куском и аллоцирует новый по необходимости (расширение или сжатие). Можно сделать одну кучу на всех.
RD>В утверждении речь шла о решении проблемы "при переполнении очереди падает вся VM". RD>Ответ предлагал использовать модель Shared Heap как вариант борьбы с проблемой. Но, как оказывается, это не _решение_, а просто некритичное изменение поведения.
Неверно. Да, это не решение для всех случаев, но это контрмера, эффективная для большинства случаев. Именно поэтому я сказал "Против<...>можешь", а не "это решается". Насчёт некритичности Вы сильно неправы — практика показывает, что стабильный уровень затрат памяти создаёт существенно более управляемую, контролируемую и диагностируемую среду исполнения.
N>>Вопрос же, почему вообще VM падает вместо того, чтобы забыть сообщение, делится на две части: N>>1. Почему Mach-styled VM, лежащие в основе всех современных ОС, плохо справляются с переполнением памяти и почему многие проектировщики системных VM отказываются вообще что-то с этим делать. RD>Тут видимо много причин, и одна из них — сложность с выяснением того, какой процесс нужно разбудить, чтобы очереди разгребались а не росли.
Вы опять не о том. Во-первых, управление системными процессами в mach-styled VM не имеет ничего общего с управлением виртуальными процессами в Erlang. Во-вторых, никакой сложности нет: надо повышать диспетчерский приоритет процессу, который имеет большую очередь. На сейчас Erlang VM это делает. Более того, она притормаживает процессы, которые передают в длинную очередь (что для моего случая оказалось таки диверсией и пришлось искать другие каналы регулировки).
N>>2. Почему в Erlang не желают признавать возможность негарантированной посылки сообщения внутри одной VM. N>>Первое, если интересно, могу развернуть, второе — пусть ещё кто-то расскажет. RD>Ну а негарантированная посылка сообщений с одной стороны усложняет логику работы (приходится постоянно проверять, послалось сообщение или нет), а с другой стороны все равно не решает проблему №1 в общем случае.
Что Вы имеете в виду под "приходится постоянно проверять"? Если есть функция негарантированной посылки, всего лишь достаточно в случае получения от неё чего-то вроде {error, target_queue_overflow} отработать это максимально для себя (например, увеличить счётчик недоставок).
Этот вопрос смыкается с вопросом о множественных очередях (больная тема Erlang'а для случая больших нагрузок). Если штатный mailbox процесса остаётся на старом поведении, всё равно можно грамотно управлять дополнительными очередями (включая лимит объёма).
RD>P.S. Эрлангов подход, когда при необходимости расширяют кучу процесса просто созданием еще одной и копированием в нее — не единственный возможный. В том же GHC куча не является линейным адресным пространством и состоит в основном из небольших блоков. В результате описанной проблемы с смертью при добавлении одного сообщения в переполенный 400M блок просто нет.
По-моему, тут уже много раз было явно сказано, что этот подход не единственный даже в Эрланге. Поэтому здесь Вы работаете не более чем Капитаном Очевидность, повторяя уже сказанное.
Здравствуйте, LelicDsp, Вы писали:
LD>Касательно эрланга, наивное решение — просто убивать процесс который начинает жрать слишком много. Проблема была бы решена. Но это очень спорное решение с точки зрения идеологии.
Отстрел процесса, который вдруг "начинает жрать слишком много" совсем не выход. Да и чревато это.
Здравствуйте, LelicDsp, Вы писали:
LD>Надо уточнить про джаву — на моем опыте VM падала, но что бы из-за недостатка памяти — не помню. Потенциально если отработать все OutOfMemoryException, то программа может выжить
На моей практике падение Java VM из-за недостатка памяти это одна из наиболее частых ситуаций, OutOfMemoryError в Java не обрабатывается (про .Net c OutOfMemoryException не скажу). Выжить программа, соответственно, не может. VM сдохнет с выбросом хипдампа, если ей это позволено сделать.
RD>>В утверждении речь шла о решении проблемы "при переполнении очереди падает вся VM". RD>>Ответ предлагал использовать модель Shared Heap как вариант борьбы с проблемой. Но, как оказывается, это не _решение_, а просто некритичное изменение поведения.
N>Неверно. Да, это не решение для всех случаев, но это контрмера, эффективная для большинства случаев. Именно поэтому я сказал "Против<...>можешь", а не "это решается". Насчёт некритичности Вы сильно неправы — практика показывает, что стабильный уровень затрат памяти создаёт существенно более управляемую, контролируемую и диагностируемую среду исполнения.
О да, стабильный уровень затрат это шоколадно. Вот только как ни крути, но если у одного из процессов бесконтрольно растет очередь, то это является проблемой само по себе. И никакими танцами с бубном вроде смены модели памяти это не решается. Зато переходом на Shared Heap легко и непринужденно получаем потерю хваленого soft realtime (точнее того, что под ним подразумевается в Erlang).
N>>>Вопрос же, почему вообще VM падает вместо того, чтобы забыть сообщение, делится на две части: N>>>1. Почему Mach-styled VM, лежащие в основе всех современных ОС, плохо справляются с переполнением памяти и почему многие проектировщики системных VM отказываются вообще что-то с этим делать. RD>>Тут видимо много причин, и одна из них — сложность с выяснением того, какой процесс нужно разбудить, чтобы очереди разгребались а не росли.
N>Вы опять не о том. Во-первых, управление системными процессами в mach-styled VM не имеет ничего общего с управлением виртуальными процессами в Erlang. Во-вторых, никакой сложности нет: надо повышать диспетчерский приоритет процессу, который имеет большую очередь. На сейчас Erlang VM это делает. Более того, она притормаживает процессы, которые передают в длинную очередь (что для моего случая оказалось таки диверсией и пришлось искать другие каналы регулировки).
Да неужели? Прямо таки вообще нет ничего общего? Вообще-вообще?
А во-вторых, сложность есть, и фундаментальная. Вам, как программисту, следовало бы знать. Если у процесса большой mailbox, то не всегда притормаживание передающих в mailbox процессов и повышение процесса, разгребающего большую очереднь, является решением.
Пример: процесс с большой очередью находится в состоянии, когда на каждое входящее сообщение он рассылает кучу новых, в том числе и себе. Из этого состояния его может вывести получение сообщения, посланного процессом №2. Но этот процесс приторможет, так как он, собака такая, хочет послать процессу с длинной очередью. Вот и разруливайте себе..
Пока у описанного вами кхе-кхе "решения" нет серьезной математической основы, все это — детские забавы для яростно верующих. И оно относится к _решению_ проблемы как действия африканского шамана-целителя относятся к действиям солидного нейрохирурга.
N>>>2. Почему в Erlang не желают признавать возможность негарантированной посылки сообщения внутри одной VM. N>>>Первое, если интересно, могу развернуть, второе — пусть ещё кто-то расскажет. RD>>Ну а негарантированная посылка сообщений с одной стороны усложняет логику работы (приходится постоянно проверять, послалось сообщение или нет), а с другой стороны все равно не решает проблему №1 в общем случае.
N>Что Вы имеете в виду под "приходится постоянно проверять"? Если есть функция негарантированной посылки, всего лишь достаточно в случае получения от неё чего-то вроде {error, target_queue_overflow} отработать это максимально для себя (например, увеличить счётчик недоставок).
Это и имею в виду. Нужно проверять возвращаемое значение этой функции и писать кучу кода. А это уже противоречит философии Эрланга "let it crash" и здоровому принципу "KISS".
N>Этот вопрос смыкается с вопросом о множественных очередях (больная тема Erlang'а для случая больших нагрузок). Если штатный mailbox процесса остаётся на старом поведении, всё равно можно грамотно управлять дополнительными очередями (включая лимит объёма).
RD>>P.S. Эрлангов подход, когда при необходимости расширяют кучу процесса просто созданием еще одной и копированием в нее — не единственный возможный. В том же GHC куча не является линейным адресным пространством и состоит в основном из небольших блоков. В результате описанной проблемы с смертью при добавлении одного сообщения в переполенный 400M блок просто нет.
N>По-моему, тут уже много раз было явно сказано, что этот подход не единственный даже в Эрланге. Поэтому здесь Вы работаете не более чем Капитаном Очевидность, повторяя уже сказанное.
Видимо у К.О. таки до сих пор есть работа. Вот он как бы намекает, что вместо Private Heap и Shared Heap есть еще Private Chunked Heap (хотя в Эрланг его может и не быть). Впрочем, как я уже сказал, все это — бессмысленные забавы. Пока нет алгоритма гарантированного и оптимального разгребания очередей, все равно софтина может умереть от переедания.
K>Отстрел процесса, который вдруг "начинает жрать слишком много" совсем не выход. Да и чревато это.
ИМНО лучше убить его, пусть перестартует, чем убивать все приложение.
Здравствуйте, cadet354, Вы писали:
C>Здравствуйте, kpy3, Вы писали:
K>>Отстрел процесса, который вдруг "начинает жрать слишком много" совсем не выход. Да и чревато это. C>ИМНО лучше убить его, пусть перестартует, чем убивать все приложение.
Процесс считал деньги, пожрал память, был убит, база неконсистентна. Что дальше?
Здравствуйте, cadet354, Вы писали:
C>Здравствуйте, kpy3, Вы писали:
K>>Процесс считал деньги, пожрал память, был убит, база неконсистентна. Что дальше?
C>Аналогично: C>Процесс считал деньги, пожрал память, было убито все приложение, база неконсистентна. Что дальше?
В моём случае люлей схватил программист за наэффективное решение задачи. Кто будет отвечать в вашем случае? "Плохая" VM которая рубит прожорливые процессы?
Здравствуйте, Rtveliashvili Denys, Вы писали:
RD>О да, стабильный уровень затрат это шоколадно. Вот только как ни крути, но если у одного из процессов бесконтрольно растет очередь, то это является проблемой само по себе. И никакими танцами с бубном вроде смены модели памяти это не решается. Зато переходом на Shared Heap легко и непринужденно получаем потерю хваленого soft realtime (точнее того, что под ним подразумевается в Erlang).
Значит, или soft realtime, или большие очереди. Вообще, использование Erlang под большой нагрузкой с большими очередями — это, мягко говоря, не тот профиль, который у него был в начале пути.
N>>Вы опять не о том. Во-первых, управление системными процессами в mach-styled VM не имеет ничего общего с управлением виртуальными процессами в Erlang. Во-вторых, никакой сложности нет: надо повышать диспетчерский приоритет процессу, который имеет большую очередь. На сейчас Erlang VM это делает. Более того, она притормаживает процессы, которые передают в длинную очередь (что для моего случая оказалось таки диверсией и пришлось искать другие каналы регулировки). RD>Да неужели? Прямо таки вообще нет ничего общего? Вообще-вообще?
Ну конечно, можно найти общее. Начиная от задействованных атомов и транзисторов. А ещё есть общее у сверлящего взгляда и акустических характеристик слова "бетон".
RD>А во-вторых, сложность есть, и фундаментальная. Вам, как программисту, следовало бы знать. Если у процесса большой mailbox, то не всегда притормаживание передающих в mailbox процессов и повышение процесса, разгребающего большую очереднь, является решением.
Спасибо, я это знаю безо всяких Ваших "следовало бы". Опять под К.О. работаете не дочитав реплики собеседника?
RD>Пример: процесс с большой очередью находится в состоянии, когда на каждое входящее сообщение он рассылает кучу новых, в том числе и себе. Из этого состояния его может вывести получение сообщения, посланного процессом №2. Но этот процесс приторможет, так как он, собака такая, хочет послать процессу с длинной очередью. Вот и разруливайте себе..
К чему все эти страшилки? Хотите показать, что есть случаи, когда тот метод неэффективен? К.О. случай #3.
RD>Пока у описанного вами кхе-кхе "решения" нет серьезной математической основы, все это — детские забавы для яростно верующих. И оно относится к _решению_ проблемы как действия африканского шамана-целителя относятся к действиям солидного нейрохирурга.
Что именно Вы тут назвали "решением"? И что Вы собрались считать?
N>>Что Вы имеете в виду под "приходится постоянно проверять"? Если есть функция негарантированной посылки, всего лишь достаточно в случае получения от неё чего-то вроде {error, target_queue_overflow} отработать это максимально для себя (например, увеличить счётчик недоставок).
RD>Это и имею в виду. Нужно проверять возвращаемое значение этой функции и писать кучу кода. А это уже противоречит философии Эрланга "let it crash" и здоровому принципу "KISS".
С первым ещё можно как-то согласиться, но со вторым — нет. Никакого проблемного усложнения в немедленном оповещении о недоставке, если оно легко реализуется, нет.
RD>Видимо у К.О. таки до сих пор есть работа. Вот он как бы намекает, что вместо Private Heap и Shared Heap есть еще Private Chunked Heap (хотя в Эрланг его может и не быть). Впрочем, как я уже сказал, все это — бессмысленные забавы. Пока нет алгоритма гарантированного и оптимального разгребания очередей, все равно софтина может умереть от переедания.
K>В моём случае люлей схватил программист за наэффективное решение задачи. Кто будет отвечать в вашем случае? "Плохая" VM которая рубит прожорливые процессы?
что-то я не понял этого, в любом случае кто-то "схватит", но почему из-за того, что например, процесс логирования отвалился, должно все приложение умереть? Я стартую процесс, и если мне надо чтоб при его смерти умерла вся программа, я это могу реализовать, а как обратную задачу сделать?
RD>>А во-вторых, сложность есть, и фундаментальная. Вам, как программисту, следовало бы знать. Если у процесса большой mailbox, то не всегда притормаживание передающих в mailbox процессов и повышение процесса, разгребающего большую очереднь, является решением.
N>Спасибо, я это знаю безо всяких Ваших "следовало бы". Опять под К.О. работаете не дочитав реплики собеседника?
RD>>Пример: процесс с большой очередью находится в состоянии, когда на каждое входящее сообщение он рассылает кучу новых, в том числе и себе. Из этого состояния его может вывести получение сообщения, посланного процессом №2. Но этот процесс приторможет, так как он, собака такая, хочет послать процессу с длинной очередью. Вот и разруливайте себе..
N>К чему все эти страшилки? Хотите показать, что есть случаи, когда тот метод неэффективен? К.О. случай #3.
Дорогой товарищ, Вы говорите на каком-то особенном языке.
Вот ваша фраза:
"Во-вторых, никакой сложности нет: надо повышать диспетчерский приоритет процессу, который имеет большую очередь. На сейчас Erlang VM это делает. Более того, она притормаживает процессы, которые передают в длинную очередь (что для моего случая оказалось таки диверсией и пришлось искать другие каналы регулировки)."
Т.е. Вы утверждаете, что сложности нет. Я вам возразил. Вы согласились с моим возражением!? И назвали меня К.О.
Хорошо, я К.О., т.к. мои утверждения по крайней мере верны. Что нельзя сказать о ваших.
Здравствуйте, Rtveliashvili Denys, Вы писали:
RD>Дорогой товарищ, Вы говорите на каком-то особенном языке. :-)
Не понимаю, что в моём языке так смущает Вас.
RD>Вот ваша фраза:
RD>"Во-вторых, никакой сложности нет: надо повышать диспетчерский приоритет процессу, который имеет большую очередь. На сейчас Erlang VM это делает. Более того, она притормаживает процессы, которые передают в длинную очередь (что для моего случая оказалось таки диверсией и пришлось искать другие каналы регулировки)."
RD>Т.е. Вы утверждаете, что сложности нет. Я вам возразил. Вы согласились с моим возражением!? И назвали меня К.О.
Вы вообще различаете пожелания к работе VM и к работе целевого кода приложения? У них, мягко говоря, немного разные пределы и области ответственности. Для VM, к сожалению, подход с притормаживанием — разумный по умолчанию.
RD>Хорошо, я К.О., т.к. мои утверждения по крайней мере верны. Что нельзя сказать о ваших. RD>Короче, мне эта "содержательная" беседа надоела.
Да пожалуйста. Вы не желаете учитывать контекст — ну так не удивляйтесь результату.