Здравствуйте, BulatZiganshin, Вы писали:
F>>> 1. параллельное выполнение множества задач
S>>Шедулеры ОС прекрасно справляются с этой штукой и труда в доведения шедулеров ОС до state-of-the-art тратиться в разы больше, чем для шедулера Erlang-а.
BZ>ты всё же определись — справляется ли шедулер винды с 100К потоков так же легко как эрланг или нет, а то ты уже несколько раз свою точку зрения в этом треде менял
Мной нигде не утверждалось, что шедулеры традиционных ОС так же хорошо справляются с сотнями тысяч потоков, как VM Erlang-а. Речь шла о том, что многозадачные ОС уже давно имеют инструменты для параллельного выполнения множества задач. Но при этом не нужно с потоками ОС обращаться так же, как с легковесными потоками Erlang. Т.к. проблему M:N никто не отменял.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>так ты говорил как раз про потоки ОС. а здесь тестируешь нити своего SO5, т.е. как раз аналог легковесных потоков эрланга
В SO5 нет легковесных потоков, там нити ОС и только нити ОС используются.
Аналогичная картина и в CAF.
Легковесные короутины использует Synca (а унутре у ней Boost.Coroutine, если правильно помню).
Здравствуйте, BulatZiganshin, Вы писали:
BZ>соответственно, самый удобный для программистов вариант — именно короутины
Не так все просто, если из короутин приходится дергать функции из сторонних библиотек. Если там где-то TLS используются, то могут быть серьезные проблемы.
Здравствуйте, so5team, Вы писали:
BZ>>так ты говорил как раз про потоки ОС. а здесь тестируешь нити своего SO5, т.е. как раз аналог легковесных потоков эрланга S>В SO5 нет легковесных потоков, там нити ОС и только нити ОС используются.
BZ>>соответственно, самый удобный для программистов вариант — именно короутины S>Не так все просто, если из короутин приходится дергать функции из сторонних библиотек. Если там где-то TLS используются, то могут быть серьезные проблемы.
какой выигрыш даёт использование нитей ОС по сравнению с самоделкой из буста? минусы, как я понимаю — затраты 70 кб озу на кажду нить, непереносимость, и вроде создание/переключение медленней
Здравствуйте, so5team, Вы писали:
S>Мной нигде не утверждалось, что шедулеры традиционных ОС так же хорошо справляются с сотнями тысяч потоков, как VM Erlang-а. Речь шла о том, что многозадачные ОС уже давно имеют инструменты для параллельного выполнения множества задач. Но при этом не нужно с потоками ОС обращаться так же, как с легковесными потоками Erlang. Т.к. проблему M:N никто не отменял.
просто не надо называть нити потоками. и в чём разница между нитями ОС, короутинами буста и потоками эрланга? в частности, причём проблема m:n (речь ведь о том что потоков CPU гораздо меньше чем потоков выполнения программы?) ?
Здравствуйте, so5team, Вы писали:
S>>>У меня нет такой идеи. Даже в первом своем сообщении в этой теме в качестве примера фреймворка для C++ был приведен фреймворк Synca, который как раз сопрограммы и использует. BZ>>среди штук 5 акторных бибилотек. при этом ты ни словом не упомянул наиболее известные tbb/ppl/boost.coroutine S>Boost.Coroutine -- это как раз низкоуровневая либа, при работе с которой нужно, как и при работе с threads вручную заниматься стартом/стопом и синхронизацией. Это не интересно. Интересно то, что над Coroutine надстраивают, та же Synca, тому пример.
Над Boost.Coroutine есть и другая надстройка (от того же автора) — Boost.Fiber (ещё не принятая в Boost):
boost.fiber provides a framework for micro-/userland-threads (fibers) scheduled cooperativly. The API contains classes and functions to manage and synchronize fibers similiar to boost.thread.
S>tbb/ppl, как и недавно ставшая известной, hpx -- это инструменты для parallel computing. А так как, на мой взгляд, Erlang вообще не является инструментом для parallel computing, то и смысла упоминать tbb/ppl/hpx в разговоре про Erlang нет. Зато есть смысл говорить про инструменты для concurrent и distributed computing.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>просто не надо называть нити потоками. и в чём разница между нитями ОС, короутинами буста и потоками эрланга? в частности, причём проблема m:n (речь ведь о том что потоков CPU гораздо меньше чем потоков выполнения программы?) ?
У термина thread два устоявшихся перевода на русский язык: нить и поток. Эти термины зачастую используются в одном и том же контексте как взаимозаменяемые, дабы не повторять по 100 раз одно и то же слово.
Нить(поток) в современных ОС является единицей диспетчеризации в ОС. Чем больше нитей, тем сложнее для ОС. Но зато ОС имеет возможность вытеснять нити при исчерпании кванта времени или при переходе нити в режим ожидания (на объекте синхронизации, на I/O и т.д.).
Короутины (зеленые нити, легковесные потоки) -- это сущности, которые существуют внутри приложения и о которых ОС может не знать вообще (тут зависит от реализации, в Windows есть fibers, поверх которых могут работать короутины). За шедулинг короутин отвечает сам разработчик. Соответственно задачей разработчика является маппинг M короутин на N нитей ОС. Соответственно, если где-то программист накосячил и напрямую вызвал блокирующую операцию внутри короутины, то заблокируется одна из выделенных под обслуживание короутин нитей. Из-за этого работа с короутинами требует большего внимания, т.к., в отличии от вытесняющей многозадачности, которую может обеспечить нитям ОС, с короутинами приходится работать в кооперативном режиме. (Вообще-то неправильно отождествлять короутины с легковесными потоками, но в контексте данного разговора сойдет).
Процессы в Erlang-е -- это более продвинутая разновидность легковесных потоков. ОС про них ничего не знает, представление о них имеет только VM Erlang-а. Но, т.к. Erlang-овская VM сама выделяет кванты времени своим процессам, она имеет возможность вытеснять процесс после определенного количества редукций (т.е. выполненых инструкций VM). Кроме того, стандартная библиотека Erlang-а позволяет Erlang-овскому шедулеру снимать процесс при обращении к блокирующим операциям, передавать саму операцию на соответствующий рабочий поток, а затем поднимать задачу при получении ответа. Т.е. получается своя мини-ОС, которая работает хорошо только, если Erlang-овый код не использует обращения к NIF-ам (т.е. функциям, написанным на C).
Проблема M:N возникает, когда у нас много однотипных задач, которые, преимущественно, требуют только процессора. Например, выполняют вычисления. Это означает, что каждая задача, получив свой рабочий квант, будет использовать его полностью, не давая шедулеру снять задачу и отдать процессор кому-то еще. Тут чем быстрее работает задача, чем большие кванты времени ей выдаются, тем лучше. Проблема Erlang-а по сравнению с нативным кодом здесь в том, что Erlang медленный.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>какой выигрыш даёт использование нитей ОС по сравнению с самоделкой из буста? минусы, как я понимаю — затраты 70 кб озу на кажду нить, непереносимость, и вроде создание/переключение медленней
Речь идет не о выигрыше в производительности. А о возможности выделять акторам тот контекст, который им нужен. Например, для синхронного взаимодействия с БД (на каждый конекшен к БД выделяется своя рабочая нить). Для общения с подключеным к компьютеру оборудованием (HSM, CardReades, I/O Controllers...). Для выполнения тяжелых вычислительных операций. И т.д.
При этом счет рабочих нитей идет разве что на сотни. Но никак не на тысячи и, уж тем более, не на десятки тысяч.
Здравствуйте, so5team, Вы писали:
S>Boost.Coroutine -- это как раз низкоуровневая либа, при работе с которой нужно, как и при работе с threads вручную заниматься стартом/стопом и синхронизацией. Это не интересно. Интересно то, что над Coroutine надстраивают, та же Synca, тому пример.
при этом короутины, как и потоки — наиболее удобное средство программирования многозадачности. и в эрланге из коробки тоже такой подход. и поверх него хоть в C++, хоть в эрланге можно прикрутить акторы или что-то ещё. поэтому твой вывод что заменой потоков в эрланге являются акторы в С++ — я не понимаю. если кому-то удобно с потоками в эрланге — значит ему будет удобней с потоками и в С++. если кому-то больше подходят акторы — он будет использовать их и в эрланге
S>tbb/ppl, как и недавно ставшая известной, hpx -- это инструменты для parallel computing. А так как, на мой взгляд, Erlang вообще не является инструментом для parallel computing, то и смысла упоминать tbb/ppl/hpx в разговоре про Erlang нет. Зато есть смысл говорить про инструменты для concurrent и distributed computing.
про hpx не знал. из её доки:
— It enables programmers to write fully asynchronous code using hundreds of millions of threads.
— HPX makes concurrency manageable with dataflow and future based synchronization.
плюс там ещё distributed programming, который был в SO4 и есть в эрланг
в tbb аналогично, есть средства и для параллельности, и для конкурентности. и в отличии от всех акторных иблиотек, это широко известная и поддерживаемая монстром вещь
BZ>> скаал что подход с потоками принципиально неприменим в C++. S>Где я такое сказал?
в первом сообщении в этом треде. и многократно повторил позже, что в С++ надо использовать иные подходы, чем в эрланге
Здравствуйте, vdimas, Вы писали:
EP>>Это понятно, тут со stateless хорошо то что они требуют ровно столько памяти, сколько требуется. Но вопрос-то был про другое. EP>>Тем не менее, я без проблем запускал 400k stackful корутин на ноутбуке
, причём это далеко не предел. V>Я уже читал твои отчеты (познавательно, кста) и не согласен с тем, чтобы уменьшать стек под корутину. Ведь не знаешь же, сколько реально стека потребуется для каких-нить промежуточных вычислений.
Да, это одна из основных проблем stackful корутин. Не размер стэка когда корутина спит, а именно то что между yield'ами может понадобится нырнуть поглубже в стэк. Это конечно далеко не всегда будет проблемой, но учитывать обязательно.
Тут вижу несколько вариантов:
1. Split Stacks — то есть поддержка "разорванных" стэков компилятором+рантаймом.
2. На x64 резервировать требуемый объём адресного пространства под стэк, делать commit на страницу, при росте добавлять, а при уменьшении возвращать.
3. Тяжёлые вычисления между yield'ами делать в отдельной корутине с большим стэком (в том же потоке), то есть:
coroutine_with_small_stack([&]
{
Data x;
yield();
auto result = coroutine_with_large_stack_do_now([&]
{
return heavy_computation_without_yield(x);
});
yield();
});
Здравствуйте, BulatZiganshin, Вы писали:
BZ>>> скаал что подход с потоками принципиально неприменим в C++. S>>Где я такое сказал?
BZ>в первом сообщении в этом треде. и многократно повторил позже, что в С++ надо использовать иные подходы, чем в эрланге
Речь шла о нативных потоках в C++ (так же, как и в Java, и в Ada, и в Eiffel, и в C#) против процессов в Erlang-e. Поскольку единицей диспетчеризации является либо нить в обычной ОС, либо Эрланговский процесс в Erlang VM.
При этом я сразу говорил о возможности использования короутин:
Существует и другой подход, который принято называть SEDA-way, при котором отдельный процесс/нить заводится не под всю активность, а под определенную операцию (stage) в рамках активности. Сами же активности могут оформляться либо в виде легковесных короутин, либо в виде еще более легковесных объектов (конечных автоматов).
Т.е. рабочие нити (коих N) + короутины (или конечные автоматы, коих M). Где N << M.
Эрланг — это не только возможность создавать миллион потоков.
а я тебе предлагаю перечитать ответ, который я тебе дал на то сообщение. в эрланге есть много чего, что мне лично не нужно. поэтому я предлагаю сосредоточиться именно на миллионе потоков, включая сюда распространение исключений (вещь жизненно необходимая), но не возможность продолжать работать при поломках оборудования, realtime и прочую коммутаторную специфику
Здравствуйте, so5team, Вы писали:
BZ>>просто не надо называть нити потоками. и в чём разница между нитями ОС, короутинами буста и потоками эрланга? в частности, причём проблема m:n (речь ведь о том что потоков CPU гораздо меньше чем потоков выполнения программы?) ?
S>У термина thread два устоявшихся перевода на русский язык: нить и поток. Эти термины зачастую используются в одном и том же контексте как взаимозаменяемые, дабы не повторять по 100 раз одно и то же слово.
я думал что нить — это fiber, но fiber оказывается — волокно. ок, с этим ясно. так твой пример который 4 секунды выполнялся — он создавал 100 тыщ полноценных потоков ОС?
S>Короутины (зеленые нити, легковесные потоки)
зелёными потоками принято называть короутины, которые шедулятся рантаймом. можешь зхаглянуть в википедию, там говорится о шедулинге в ВМ, но в ghc например VM нет, так что моё определение более полное. а короутины, соответственно — когда тебе выдали стёк и крутись как хочешь
S>Процессы в Erlang-е -- это более продвинутая разновидность легковесных потоков. ОС про них ничего не знает, представление о них имеет только VM Erlang-а. Но, т.к. Erlang-овская VM сама выделяет кванты времени своим процессам, она имеет возможность вытеснять процесс после определенного количества редукций (т.е. выполненых инструкций VM). Кроме того, стандартная библиотека Erlang-а позволяет Erlang-овскому шедулеру снимать процесс при обращении к блокирующим операциям, передавать саму операцию на соответствующий рабочий поток, а затем поднимать задачу при получении ответа. Т.е. получается своя мини-ОС, которая работает хорошо только, если Erlang-овый код не использует обращения к NIF-ам (т.е. функциям, написанным на C).
собственно, для этого достаточно все обращения к ОС заменять на обёртки, которые умеют кооперироваться с ВМ/рантаймом. преркасно реализуется что в C++ (как раз Asynca это и делает), что в ghc из коробки
S>Проблема M:N возникает, когда у нас много однотипных задач, которые, преимущественно, требуют только процессора. Например, выполняют вычисления. Это означает, что каждая задача, получив свой рабочий квант, будет использовать его полностью, не давая шедулеру снять задачу и отдать процессор кому-то еще. Тут чем быстрее работает задача, чем большие кванты времени ей выдаются, тем лучше.
ты похоже видишь единственный вариант решения этой проблемы — разбить работу на мноджество небольших задач. а можно сделать горажо проще — исользовать короутины, вставить yield в тех же самых местах и при этом сохранить состояние стека. т.е. в эрланге/ghc у нас зелёные треды, а в c++ — короутины, которые отличаются только тем, что надо вручную вставлять yield. всё остальное, включая асинхронный I/O, выглядит точно так же (опять же см. Asynca). поэтому я несогласен с твоей идеей что в С++ надо использовать иную парадигму программирования, неджели в эрланге. всё что нужно сделать — разбавить вычисления yield и код из просто многопоточного станет выполняемым асинхронно в пуле потоков
Здравствуйте, BulatZiganshin, Вы писали:
BZ>ты похоже видишь единственный вариант решения этой проблемы — разбить работу на мноджество небольших задач. а можно сделать горажо проще — исользовать короутины, вставить yield в тех же самых местах и при этом сохранить состояние стека. т.е. в эрланге/ghc у нас зелёные треды, а в c++ — короутины, которые отличаются только тем, что надо вручную вставлять yield. всё остальное, включая асинхронный I/O, выглядит точно так же (опять же см. Asynca). поэтому я несогласен с твоей идеей что в С++ надо использовать иную парадигму программирования, неджели в эрланге. всё что нужно сделать — разбавить вычисления yield и код из просто многопоточного станет выполняемым асинхронно в пуле потоков
При этом даже необязательно везде ставить явный yield — это может происходить внутри функций библиотеки.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>так твой пример который 4 секунды выполнялся — он создавал 100 тыщ полноценных потоков ОС?
Да, но там времена чуть другие 4s на создание 100K настоящих потоков ОС, 4s на саму работу и более 60s на очистку всех ресурсов
BZ>собственно, для этого достаточно все обращения к ОС заменять на обёртки, которые умеют кооперироваться с ВМ/рантаймом. преркасно реализуется что в C++ (как раз Asynca это и делает),
Не так все просто, если речь идет об использовании кучи чужого кода в своем проекте.
BZ> что в ghc из коробки
Можно порадоваться за ghc
BZ>ты похоже видишь единственный вариант решения этой проблемы — разбить работу на мноджество небольших задач. а можно сделать горажо проще — исользовать короутины, вставить yield в тех же самых местах и при этом сохранить состояние стека. т.е. в эрланге/ghc у нас зелёные треды, а в c++ — короутины, которые отличаются только тем, что надо вручную вставлять yield. всё остальное, включая асинхронный I/O, выглядит точно так же (опять же см. Asynca). поэтому я несогласен с твоей идеей что в С++ надо использовать иную парадигму программирования, неджели в эрланге. всё что нужно сделать — разбавить вычисления yield и код из просто многопоточного станет выполняемым асинхронно в пуле потоков
Не совсем так. Речь идет о том, что в C++ нужно определиться с теми операциями, которые должны выполняться на полноценных нитях ОС (например, операции с СУБД, с внешними устройствами, с сетью). А прикладную логику конкретной операции (например, обслуживания клиента, обработки архива) оформлять либо в виде конечного автомата, либо в виде короутины с вручную расставлеными yield-ами.
Выбор в большей степени определяется личными пристрастиями и опасениями нарваться на какие-нибудь проблемы при переходе с платформы на платформу или при задействовании чужих библиотек.
Здравствуйте, so5team, Вы писали:
BZ>>ты похоже видишь единственный вариант решения этой проблемы — разбить работу на мноджество небольших задач. а можно сделать горажо проще — исользовать короутины, вставить yield в тех же самых местах и при этом сохранить состояние стека. т.е. в эрланге/ghc у нас зелёные треды, а в c++ — короутины, которые отличаются только тем, что надо вручную вставлять yield. всё остальное, включая асинхронный I/O, выглядит точно так же (опять же см. Asynca). поэтому я несогласен с твоей идеей что в С++ надо использовать иную парадигму программирования, неджели в эрланге. всё что нужно сделать — разбавить вычисления yield и код из просто многопоточного станет выполняемым асинхронно в пуле потоков S>Не совсем так. Речь идет о том, что в C++ нужно определиться с теми операциями, которые должны выполняться на полноценных нитях ОС (например, операции с СУБД, с внешними устройствами, с сетью).
А в Erlang'е разве не нужно делать выбор между NIF'ами / Port Driver'ами?
Здравствуйте, so5team, Вы писали:
S>>>Не совсем так. Речь идет о том, что в C++ нужно определиться с теми операциями, которые должны выполняться на полноценных нитях ОС (например, операции с СУБД, с внешними устройствами, с сетью). EP>>А в Erlang'е разве не нужно делать выбор между NIF'ами / Port Driver'ами? S>AFAIK, там прибегают к NIF-ам/драйверам когда производительности pure-Erlang кода не хватает.
Например есть набор блокирующих функций в сторонней библиотеке (например connection к какой-нибудь модной СУБД) — что в Erlang'е, что в C++, придётся определятся где именно их запускать.
Здравствуйте, Sharov, Вы писали:
S>И да, раз уж Эрланг так крут, чего же гугл стал изобретать свой велосипед Go, а не форкнул Эрланг. S>Эрланг это язык, конкретно заточенный под телеком, со своими минусами и плюсами. Зачем его всюду пихать? S>Это исключительно нишевый язык.