Re[7]: Убийца C и C++ (и не только)
От: reversecode google
Дата: 27.01.22 11:46
Оценка: +1 -1
всегда можно найти какую то синтетическую конструкцию которая лучше реализована в том или другом языке
вон хаскель постоянно противоставляют по лаконичности и краткости
а толку ?

но код в расте дует очевидно его концепция
из за которой все обрастает боксами рефцелами и прочей мишурой на ровном месте
итог 20 строчек раста
заменяются двумя на С++
а огромный проект на расте
двумя файликами на С++

а когда довезут паттерн мачинг в С++
так вообще раст уйдет за D
Re[18]: Убийца C и C++ (и не только)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 27.01.22 11:50
Оценка:
Здравствуйте, so5team, Вы писали:


S>Не обязательно все. Передача управления может выполняться неявно в специальных функциях, предназначенных для использования в зеленых потоках (типа lock, release, send, receive, listen, accept и т.д.)


S>>А иначе на трёх-четырёх десятках тысяч зелёных потоков всё станет плохо.


S>С чего бы это? И почему этого не произойдет на 30-40K одновременно запущенных stackless coroutines?

Ну для асинхрошщины как раз используются WaitAsync https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.semaphoreslim.waitasync?view=net-5.0
и прочие асинхронные замены синхронным блокировщикам потоков. Вопрос насколько эффективны это другой вопрос. Суть в том, что бы не блокировать очередь потоков.
Здесь обсуждали http://rsdn.org/forum/dotnet/7947025.flat
Автор: Sharov
Дата: 08.02.21
и солнце б утром не вставало, когда бы не было меня
Отредактировано 27.01.2022 12:11 Serginio1 . Предыдущая версия .
Re[18]: Убийца C и C++ (и не только)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.01.22 12:08
Оценка:
Здравствуйте, so5team, Вы писали:
S>Конечно. Только вот если у вас двум независимым и параллельно работающим stackless coroutines потребуется доступ к одним и тем же разделяемым данным, то и им так же нужно будет использовать особенные примитивы синхронизации.
Всё верно, тут 1:1. Просто это рушит на корню идею "я просто передам уже готовую и отлаженную thread proc в код старта зелёного потока".
S>Не обязательно все. Передача управления может выполняться неявно в специальных функциях, предназначенных для использования в зеленых потоках (типа lock, release, send, receive, listen, accept и т.д.)
А то. Получается крайне интрузивная концепция — не выходит просто взять и заменить обычный thread на зелёный. Надо ещё и спец.функциями пользоваться.
S>>А иначе на трёх-четырёх десятках тысяч зелёных потоков всё станет плохо.
S>С чего бы это? И почему этого не произойдет на 30-40K одновременно запущенных stackless coroutines?
Потому, что потребление памяти у них всё ещё отличается на порядок.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Убийца C и C++ (и не только)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 27.01.22 12:08
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:

S>>Я так понимаю, что зеленые потоки это файберы https://habr.com/ru/post/185706/ то бишь стекфул ?
S>Ага.
S>>Ну в .Net для рекурсивных обхода взяли yield и его же по сути для async await вместо файберов.
S>>Возможно, что бы упростить работу сборщика мусора, что бы еще не держать информацию о стеке файбера
S>В основном — по соображениям эффективности. Я тут уже давал ссылку на статью с подробным исследованием вопроса.
Да здесь еще обсуждали http://rsdn.org/forum/philosophy/8038391.flat
Автор: mrTwister
Дата: 28.06.21
и солнце б утром не вставало, когда бы не было меня
Re[19]: Убийца C и C++ (и не только)
От: so5team https://stiffstream.com
Дата: 27.01.22 12:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>Не обязательно все. Передача управления может выполняться неявно в специальных функциях, предназначенных для использования в зеленых потоках (типа lock, release, send, receive, listen, accept и т.д.)

S>А то. Получается крайне интрузивная концепция — не выходит просто взять и заменить обычный thread на зелёный. Надо ещё и спец.функциями пользоваться.

Зато сделать это проще, чем расставлять повсюду co_await, co_return и co_yield. Уж с co_ интрузивнее дальше некуда. Не говоря уже про то, что ментальная модель кода на простых потоках (нативных или зеленых) сильно проще, чем с co_await/co_return, т.к. с co_ далеко не сразу очевидно кто на ком стоял, особенно если реализации co_await умеют перекидывать короутины с одного контекста на другой.

S>>>А иначе на трёх-четырёх десятках тысяч зелёных потоков всё станет плохо.

S>>С чего бы это? И почему этого не произойдет на 30-40K одновременно запущенных stackless coroutines?
S>Потому, что потребление памяти у них всё ещё отличается на порядок.

И при чем здесь память? Если к stackfull coroutine привязано 10K вместо 1K у stackless, это же не значит, что в активный working set будет входить все эти 10K.
Re[15]: Убийца C и C++ (и не только)
От: alex_public  
Дата: 27.01.22 12:40
Оценка:
Здравствуйте, Sinclair, Вы писали:

Ужас насколько всё не верно. ))) Ну по пунктам:

S>Плюс стекфул — легко реализовать в библиотеке. Можно напилить на более-менее любом языке.


Можно даже не пилить библиотеку, т.к. оно есть например в АПИ некоторых ОС (например Винды). Но это не плюс, а мелкое следствие из реального преимущества. Которое заключается в том, что при использование таких сопрограмм не надо модифицировать существующий код! В противоположность бесстековым, в которых обязательным условием является модификация всего стека вызова (заражение его async'ами).

Я на этом форуме даже когда-то приводил простенький пример. Берём классический парсер (большой и сложный код) чего-то там и передаём ему в качестве исходных данных не обычный std:string, а "асинхронный" string (у которого функция вернуть буфер, является сопрограммой). В итоге получаем реактивный парсер, в который можно заталкивать данные порциями. И всё это было реализовано с помощью библиотеки boost::coro (там стекфул сопрограммы) в пару строк.

В случае же бесстековых сопрограмм тебе для тех же целей пришлось бы переписывать (ну как минимум расставлять везде async и await) весь сложный код парсера.

S>Минус — затраты на поддержание стека.


Это как раз ерунда. Потому что размер стека же указывается программистом. Т.е. максимум что тут можно высосать из пальца, это необходимость программисту включить мозг для оценки этого значения (у бесстековых это делает компилятор).

Реальный же минус смотри ниже, там где плюс бесстековых. )))

S>Плюс стеклесс — хорошая масштабируемость.


Ну вообще то в большинстве языков разницы практические не будет. )))

И только в таких языках как C++ и Rust возможно небольшое преимущество по производительности у бесстековых. Однако совсем не по тем причинам, что ты тут описывал. А в силу максимально развитого оптимизатора, который может банально инлайнить наш конечный автомат (а в стекфул сопрограммах наоборот ещё и немного процессорного времени тратится на сохранение состояния регистров).

Но это будет преимущество именно в потребление ЦПУ, которое не всегда коррелирует с масштабируемостью.

S>Минус — затраты в разработке платформы; не во всякий язык ложатся хорошо. По-хорошему, нужна поддержка от компилятора.


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

А реальный минус указан выше (заражение всего стека вызовов), в описание плюсов стекфул сопрограмм.
Re[16]: Убийца C и C++ (и не только)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 27.01.22 13:03
Оценка:
Здравствуйте, alex_public, Вы писали:



S>>Плюс стеклесс — хорошая масштабируемость.


_>Ну вообще то в большинстве языков разницы практические не будет. )))


Как я уже писал раньше
https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%B0

Синхронизирующие примитивы ядра. Мьютексы, Семафоры и т. д. Это и есть основной источник проблем с производительностью. Недостаточно продуманная работа с синхронизирующими примитивами может приводить к десяткам тысяч, а в особо запущенных случаях — и к сотням тысяч переключений контекста в секунду. [источник не указан 2477 дней]


Для async await применяется WaitAsync https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.semaphoreslim.waitasync?view=net-5.0
то есть при ожидании переключения потоков не происходит, просто освобождается поток из пула потоков и берется новая или продолжается уже запещенная задача
https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-5-asyncsemaphore/
и солнце б утром не вставало, когда бы не было меня
Отредактировано 27.01.2022 13:23 Serginio1 . Предыдущая версия .
Re[17]: Убийца C и C++ (и не только)
От: reversecode google
Дата: 27.01.22 13:16
Оценка:
стеклесс уже давно завезли
ну тогда вы обсуждаете абстрактных коней в вакуме если вы не С++ дев
тема то давно съехала о нём
Re[20]: Убийца C и C++ (и не только)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.01.22 13:32
Оценка: +1
Здравствуйте, so5team, Вы писали:

S>Зато сделать это проще, чем расставлять повсюду co_await, co_return и co_yield. Уж с co_ интрузивнее дальше некуда.

Ну, то есть для начала надо сделать fiber-аналоги основных процедур, а уж потом оно само.

S>Не говоря уже про то, что ментальная модель кода на простых потоках (нативных или зеленых) сильно проще, чем с co_await/co_return, т.к. с co_ далеко не сразу очевидно кто на ком стоял, особенно если реализации co_await умеют перекидывать короутины с одного контекста на другой.

co_await и co_return — это как я понимаю как раз артефакты тех местностей, куда не завезли языковых реализаций. Потому, что там, где завезли — там async/await, и код вполне себе выглядит и работает.

S>И при чем здесь память? Если к stackfull coroutine привязано 10K вместо 1K у stackless, это же не значит, что в активный working set будет входить все эти 10K.

Ну так чудес-то не бывает. Тем более, что 1к для stackless — это явно перебор. Там обычно десятки байт.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Убийца C и C++ (и не только)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.01.22 13:44
Оценка:
Здравствуйте, alex_public, Вы писали:
_>Можно даже не пилить библиотеку, т.к. оно есть например в АПИ некоторых ОС (например Винды). Но это не плюс, а мелкое следствие из реального преимущества. Которое заключается в том, что при использование таких сопрограмм не надо модифицировать существующий код! В противоположность бесстековым, в которых обязательным условием является модификация всего стека вызова (заражение его async'ами).
Опять же — унылая история файберов хорошо описана в обсуждаемой статье.
_>Я на этом форуме даже когда-то приводил простенький пример. Берём классический парсер (большой и сложный код) чего-то там и передаём ему в качестве исходных данных не обычный std:string, а "асинхронный" string (у которого функция вернуть буфер, является сопрограммой). В итоге получаем реактивный парсер, в который можно заталкивать данные порциями. И всё это было реализовано с помощью библиотеки boost::coro (там стекфул сопрограммы) в пару строк.
_>В случае же бесстековых сопрограмм тебе для тех же целей пришлось бы переписывать (ну как минимум расставлять везде async и await) весь сложный код парсера.
Да, совершенно верно. Но этот парсер работает ненамного лучше, чем блокирующее ожидание на основе какого-нибудь pipe. Попытка применить этот парсер для разбора чего-нибудь типа строк, массово приезжающих в http-сервер, будет очень активно кушать память и адресное пространство. А уплотнять ничего нельзя — в стек могут ссылаться активные указатели.

_>Это как раз ерунда. Потому что размер стека же указывается программистом. Т.е. максимум что тут можно высосать из пальца, это необходимость программисту включить мозг для оценки этого значения (у бесстековых это делает компилятор).

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

_> Поддержка компилятора нужна не "по-хорошему", а обязательно. Потому что иначе просто невозможно — кто ещё будет делать преобразование кода сопрограммы в конечный автомат? ))) Но я бы не стал называть это минусом.

Отчего же? Можно просто потребовать переписать всю программу в терминах явных continuations и всё, никакой стейт-машины не надо. Вот только читаться это будет настолько плохо, что лучше всё же иметь нормальный язык с соответствующим компилятором.
_>А реальный минус указан выше (заражение всего стека вызовов), в описание плюсов стекфул сопрограмм.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Убийца C и C++ (и не только)
От: alex_public  
Дата: 27.01.22 14:08
Оценка: +3
Здравствуйте, reversecode, Вы писали:

R>всегда можно найти какую то синтетическую конструкцию которая лучше реализована в том или другом языке

R>вон хаскель постоянно противоставляют по лаконичности и краткости
R>а толку ?

Rust и C++ приблизительно равны по большей части возможностей языков. Единственная область, где Rust однозначно сильнее — это метапрограммирование. И данный пример это демонстрирует. В идеале для решения данной крайне распространённой (а вовсе не синтетической) задачи от языка требуется наличия в нём какого-либо вида интроспекции (для производительных языков очевидно подходит только статическая). Этого нет ни в C++, ни в Rust (а в D кстати есть). Но при этом метапрограммирование (синтаксические макросы) Rust'а позволило красиво и удобно решить эту проблему, а метапрограммирование C++ нет.

R>но код в расте дует очевидно его концепция

R>из за которой все обрастает боксами рефцелами и прочей мишурой на ровном месте
R>итог 20 строчек раста
R>заменяются двумя на С++
R>а огромный проект на расте
R>двумя файликами на С++

Смешной ты. Ты же должен сам понимать, что вот можно взять практически любой C или C++ код и буквально дословно перевести его в код Rust'а, просто засунув в один большой unsafe блок.

Поэтому если где-то на Rust'е пишут более многословный код (без unsafe), то это не потому что по другому нельзя, а потому что хотят добиться от компилятора каких-то дополнительных проверок. Чтобы он что-то там ещё (помимо привычного в C++) постоянно контролировал сам. И вот чтобы объяснить ему правила этих дополнительных требований и требуется дополнительный объём кода.

Более того, как раз из-за этой возможности (возложить на компилятор дополнительную работу) многие и переходят на Rust.

R>а когда довезут паттерн мачинг в С++

R>так вообще раст уйдет за D

Лучше бы они рефлексию наконец родили. Которую комитет C++ уже более 10 лет обсуждает, но никак не может родить. Хотя бы банально стащить реализацию из D, работающую уже более десятилетия. И то было бы не плохо. Но не могут никак договориться...
Re[7]: Убийца C и C++ (и не только)
От: Skorodum Россия  
Дата: 27.01.22 14:12
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Когда-то давно, когда деревья были большими, а компиляторы тупыми, они умели выдавать ровно 1 ошибку и сразу падать. И люди мечтали, что компиляторы будут выдавать все возможные ошибки сразу.

Гугл говорит, что и сейчас умеют:

$ g++ -Wfatal-errors foo.cpp


З.Ы. IDE + LSP + clang == 0 compile time errors
Re[9]: Убийца C и C++ (и не только)
От: Skorodum Россия  
Дата: 27.01.22 14:19
Оценка: +3
Здравствуйте, vaa, Вы писали:

vaa>Андрей Александреску так не считает, раз один из признанных гуру C++ посвятил 10 лет развитию ДИ2

vaa>Видимо есть нюансы
Только он не разработчик, а евангелист. В свое время одним из первых понял насколько извращенно можно использовать шаблоны в плюсах, но это не значит, что так надо.
Re[21]: Убийца C и C++ (и не только)
От: so5team https://stiffstream.com
Дата: 27.01.22 14:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>Зато сделать это проще, чем расставлять повсюду co_await, co_return и co_yield. Уж с co_ интрузивнее дальше некуда.

S>Ну, то есть для начала надо сделать fiber-аналоги основных процедур, а уж потом оно само.

Я не понял, если мы собираемся использовать вызовы send/receive/accept/listen и им подобные из stackless короутин, то они как-то автоматически станут асинхронными?

S>>Не говоря уже про то, что ментальная модель кода на простых потоках (нативных или зеленых) сильно проще, чем с co_await/co_return, т.к. с co_ далеко не сразу очевидно кто на ком стоял, особенно если реализации co_await умеют перекидывать короутины с одного контекста на другой.

S>co_await и co_return — это как я понимаю как раз артефакты тех местностей, куда не завезли языковых реализаций. Потому, что там, где завезли — там async/await, и код вполне себе выглядит и работает.

Каким-то высокомерным снобизмом пахнуло. Только вот мы находимся в теме, посвященной D, C, C++ и, отчасти, Rust-у. Еще в контексте обсуждения упоминались Go, Erlang и Elixir. Можете показать куда из вышеперечисленного завезли async/await? Ну и, чтобы два раза не вставать, можете рассказать, чем co_wait/co_return/co_yield принципиально отличаются от async/await в ... (тут сами выберите что вам ближе)?

S>>И при чем здесь память? Если к stackfull coroutine привязано 10K вместо 1K у stackless, это же не значит, что в активный working set будет входить все эти 10K.

S>Ну так чудес-то не бывает. Тем более, что 1к для stackless — это явно перебор. Там обычно десятки байт.

Тем не менее, для того, чтобы получить серьезную просадку производительности нужно, чтобы размер working set у разного типа короутин различался существенно. Что вряд ли будет иметь место на практике, если у нас короутины выполняют одинаковый тип работы.
Re[5]: Убийца C и C++ (и не только)
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 27.01.22 14:50
Оценка: 4 (2)
Здравствуйте, alex_public, Вы писали:

_>А в D наоборот, после выхода в релиз развитие замерло. Не были добавлены ни новейшие идеи из C++ (типа семантики перемещения), ни трендовые идеи индустрии (типа сопрограмм). Видимо авторы языка решили что он уже совершенен (как когда-то решили авторы IE6) и надо только его дошлифовывать и развивать инфраструктуру.


Файберы были, про move-семантику тоже что-то было.
https://dlang.org/library/std/algorithm/mutation/move.html
Просто в языке с GC несколько другие акценты и приоритеты.

Развитие не замерло, оно стало хаотичным. Кучу времени и сил потратили на @nogc, чтобы угодить вечно ворчащим плюсовикам.
Потом еще был betterC, где GC даже не линкуется, но бесплатные в рантайме штуки вроде статической интроспекции и шаблонов еще доступны.
Потом была эпопея с @live, попытка поиграть в Раст и добавить контроль лайфтаймов. Недоделанная, как обычно.
Теперь недавно Уолтер включил чуть ли не целый компилятор Си, чтобы еще легче было импортировать сишные штуки. Опять недоделанное решение, конечно же, без препроцессора.
Между ними еще что-то развивали, во все стороны. GC улучшили, например.
Какого-то единого видения и направления развития не было и нет, потому "лебедь, рак и щука" — девиз развития Ди.

По мне так если бы язык просто был таким, что был описан в книжке Александреску, ну может с более удобной и регулярной статической интроспекцией и нормальной имплементацией компилятора, он был бы вполне прекрасен. Постоянно его растить и делать монстра типа С++ не надо.
Re[22]: Убийца C и C++ (и не только)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.01.22 14:53
Оценка:
Здравствуйте, so5team, Вы писали:

S>Я не понял, если мы собираемся использовать вызовы send/receive/accept/listen и им подобные из stackless короутин, то они как-то автоматически станут асинхронными?

Нет, не станут. Тут опять 1:1. Ну, кроме того, что в stackless у нас чуть больше контроля за тем, что происходит — в частности, возможность какие-то вещи принудительно сделать синхронно, а в каких-то случаях уступить управление.

S>Каким-то высокомерным снобизмом пахнуло.

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

S>Только вот мы находимся в теме, посвященной D, C, C++ и, отчасти, Rust-у. Еще в контексте обсуждения упоминались Go, Erlang и Elixir. Можете показать куда из вышеперечисленного завезли async/await?

Ну так вроде в С++ как раз же и завезли.
S>Ну и, чтобы два раза не вставать, можете рассказать, чем co_wait/co_return/co_yield принципиально отличаются от async/await в ... (тут сами выберите что вам ближе)?
Оказывается — ничем. Тогда не очень понятны опасения по поводу переключения co_wait в другой контекст.

S>Тем не менее, для того, чтобы получить серьезную просадку производительности нужно, чтобы размер working set у разного типа короутин различался существенно. Что вряд ли будет иметь место на практике, если у нас короутины выполняют одинаковый тип работы.


С учётом вашего профиля, вы скорее всего правы.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Убийца C и C++ (и не только)
От: alex_public  
Дата: 27.01.22 14:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

_>>Можно даже не пилить библиотеку, т.к. оно есть например в АПИ некоторых ОС (например Винды). Но это не плюс, а мелкое следствие из реального преимущества. Которое заключается в том, что при использование таких сопрограмм не надо модифицировать существующий код! В противоположность бесстековым, в которых обязательным условием является модификация всего стека вызова (заражение его async'ами).

S>Опять же — унылая история файберов хорошо описана в обсуждаемой статье.

Обсуждаемая статья — это отголосок известной попытки MS продавить свою реализацию сопрограмм в комитете C++. ))) И автор этой статьи был так сказать главным идеологом всего этого. Кстати, в итоге решение было довольно забавным. Решили принять в стандарт бесстековые сопрограммы (с логичной аргументаций что стекфул хорошо себя чувствуют и без стандарта, в виде библиотек), но не в реализации MS. )))

_>>Я на этом форуме даже когда-то приводил простенький пример. Берём классический парсер (большой и сложный код) чего-то там и передаём ему в качестве исходных данных не обычный std:string, а "асинхронный" string (у которого функция вернуть буфер, является сопрограммой). В итоге получаем реактивный парсер, в который можно заталкивать данные порциями. И всё это было реализовано с помощью библиотеки boost::coro (там стекфул сопрограммы) в пару строк.

_>>В случае же бесстековых сопрограмм тебе для тех же целей пришлось бы переписывать (ну как минимум расставлять везде async и await) весь сложный код парсера.
S>Да, совершенно верно. Но этот парсер работает ненамного лучше, чем блокирующее ожидание на основе какого-нибудь pipe. Попытка применить этот парсер для разбора чего-нибудь типа строк, массово приезжающих в http-сервер, будет очень активно кушать память и адресное пространство. А уплотнять ничего нельзя — в стек могут ссылаться активные указатели.

Это был всего лишь форумный пример задачки, которую бесстековые сопрограммы не способны решить в принципе. А не какое-то реальное решение из жизни. Так что смотри не на частность, а на общий принцип: бесстековые не позволяют тебе использовать уже разработанный код, если он написан не как сопрограмма. Это весьма фундаментальный минус.

_>>Это как раз ерунда. Потому что размер стека же указывается программистом. Т.е. максимум что тут можно высосать из пальца, это необходимость программисту включить мозг для оценки этого значения (у бесстековых это делает компилятор).

S>У бесстековых потребность в памяти гораздо меньше.

С чего бы это? Там фундаментальная разница будет только в размер контекста регистров (~350 байт для х86 с SIMD). Все локальные переменные из стека сопрограммы точно так же обязаны храниться в состояние конечного автомата. Разве что ты там вызовешь внутри какую-то внешнюю функцию, при этом не являющуюся сопрограммой и при этом требующую мегабайт стека. Но это опять же высосанный из пальца сценарий: на практике (а единственное место где вообще актуальна эта тема — это нагруженные сервисы) мы вполне себе представляем как выглядит обычный код таких сопрограмм.

S>Если бы можно было просто заткнуть все дыры уменьшением размера стека, можно было бы не заморачиваться никакими файберами, а просто передать этот размер в CreateThread.


Эм, основная проблема системных потоков как раз не в потребление памяти, а в том что при переключение между ними происходит прыжок в нулевое кольцо. В итоге на переключение может уходить времени больше чем на сами "вычисления".

_>> Поддержка компилятора нужна не "по-хорошему", а обязательно. Потому что иначе просто невозможно — кто ещё будет делать преобразование кода сопрограммы в конечный автомат? ))) Но я бы не стал называть это минусом.

S>Отчего же? Можно просто потребовать переписать всю программу в терминах явных continuations и всё, никакой стейт-машины не надо. Вот только читаться это будет настолько плохо, что лучше всё же иметь нормальный язык с соответствующим компилятором.

Ну так тогда это просто перестанет быть сопрограммой, банально из определения этого термина. ))) Это будет обычное асинхронное программирование, которое собственно изначально так и возникло. А только потом возникла идея "выпрямления" лапши из коллбэков с помощью сопрограмм в нечто похожее на классическое многопоточное программирование. Т.е. сами по себе сопрограммы — это просто синтаксический сахар для внешней красоты (где образцом являются те самые медленные системные потоки).

Но бесстековых сопрограмм без поддержки компилятора в принципе быть не может. ))) Ну разве что в теории в языке с очень развитым метапрограммированием, где можно будет самому закодировать нужную трансформацию (но я бы за такое не стал браться).
Re[23]: Убийца C и C++ (и не только)
От: so5team https://stiffstream.com
Дата: 27.01.22 15:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну, кроме того, что в stackless у нас чуть больше контроля за тем, что происходит — в частности, возможность какие-то вещи принудительно сделать синхронно, а в каких-то случаях уступить управление.


Как раз в каких-то сценариях возможность неявно сделать yield без явного указания co_yield/co_await в коде может быть отличным подспорьем. Представьте, что мы используем некий фреймворк, который дает нам аналоги Go-шных каналов (отсюда вызовы send/receive) и средства для асинхронной работы с сетью (или с СУБД, или еще с чем-то). Все это в виде обычных синхронных методов.

Мы тогда просто пишем свой код в простом линейном виде (та самая thread_proc), а уже ручками настройки фреймворка переключаем -- должны ли под капотом быть нативные нити или же зеленые. Это очень круто, вообще-то.

S>>Ну и, чтобы два раза не вставать, можете рассказать, чем co_wait/co_return/co_yield принципиально отличаются от async/await в ... (тут сами выберите что вам ближе)?

S>Оказывается — ничем. Тогда не очень понятны опасения по поводу переключения co_wait в другой контекст.

Так вот как раз в C++ не очень понятно, где, когда и что будет выполняться, когда вызывается co_await. Там несколько точек кастомизации на co_await и есть подозрение, что временами сложно будет понять к чему именно приведет co_await и где и что потом будет работать. У упоминавшегося здесь Полухина была статья с примерами на эту тему: https://habr.com/ru/company/yandex/blog/420861/
Re[18]: Убийца C и C++ (и не только)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.01.22 15:26
Оценка:
Здравствуйте, alex_public, Вы писали:

_>Обсуждаемая статья — это отголосок известной попытки MS продавить свою реализацию сопрограмм в комитете C++. ))) И автор этой статьи был так сказать главным идеологом всего этого. Кстати, в итоге решение было довольно забавным. Решили принять в стандарт бесстековые сопрограммы (с логичной аргументаций что стекфул хорошо себя чувствуют и без стандарта, в виде библиотек), но не в реализации MS. )))

Это интересно. А чем реализация MS так отличалась от того, что попало в 20й стандарт?

_>Это был всего лишь форумный пример задачки, которую бесстековые сопрограммы не способны решить в принципе. А не какое-то реальное решение из жизни. Так что смотри не на частность, а на общий принцип: бесстековые не позволяют тебе использовать уже разработанный код, если он написан не как сопрограмма. Это весьма фундаментальный минус.

Да, к сожалению, это так.
Но я вообще скептически отношусь к серебряным пулям, которые берут и превращают готовый отлаженный код во что-то другое. Так не бывает. Нельзя просто взять и щелчком пальцев превратить apache с его классическим 1 процесс на реквест в nginx или lighttpd.

_>С чего бы это? Там фундаментальная разница будет только в размер контекста регистров (~350 байт для х86 с SIMD). Все локальные переменные из стека сопрограммы точно так же обязаны храниться в состояние конечного автомата.

Нет, не все. А только те, которые нужны в продолжении. Компилятор очень хорошо умеет определять такие вещи.
_>Разве что ты там вызовешь внутри какую-то внешнюю функцию, при этом не являющуюся сопрограммой и при этом требующую мегабайт стека. Но это опять же высосанный из пальца сценарий: на практике (а единственное место где вообще актуальна эта тема — это нагруженные сервисы) мы вполне себе представляем как выглядит обычный код таких сопрограмм.
Почему внешнюю? Нужно тащить весь стек. Смотрите: авторы Go были вынуждены отказаться от сегментированного стека из-за просада производительности. При этом дефолтный размер стека горутины у них был 8 килобайт.
Это всё ещё примерно в сто раз больше, чем размер замыкания для типичной сопрограммы.

_>Эм, основная проблема системных потоков как раз не в потребление памяти, а в том что при переключение между ними происходит прыжок в нулевое кольцо. В итоге на переключение может уходить времени больше чем на сами "вычисления".

Ну а с зёлёными потоками у нас возникает проблема неравномерной загрузки, т.к. не всегда можно заранее предсказать объём работы в процедуре. Перемещать зелёные потоки между потоками ОС не осилил никто, несмотря на многолетние усилия.

_>Ну так тогда это просто перестанет быть сопрограммой, банально из определения этого термина. ))) Это будет обычное асинхронное программирование, которое собственно изначально так и возникло. А только потом возникла идея "выпрямления" лапши из коллбэков с помощью сопрограмм в нечто похожее на классическое многопоточное программирование. Т.е. сами по себе сопрограммы — это просто синтаксический сахар для внешней красоты (где образцом являются те самые медленные системные потоки).

А то.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Убийца C и C++ (и не только)
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.01.22 17:35
Оценка:
Здравствуйте, alex_public, Вы писали:

_>Это был всего лишь форумный пример задачки, которую бесстековые сопрограммы не способны решить в принципе. А не какое-то реальное решение из жизни. Так что смотри не на частность, а на общий принцип: бесстековые не позволяют тебе использовать уже разработанный код, если он написан не как сопрограмма. Это весьма фундаментальный минус.


Чему будет равно значение регистра r4 после переключения состояния туда-обратно?
push {r4, lr}
ldr r0, .L4
bl __emutls_get_address
mov r4, r0 ; (1) caches TLS address
bl writes_to_thread_local(int&) ; (2) OK
bl may_incur_suspend() ; (3) migrated
mov r0, r4
bl writes_to_thread_local(int&) ; (4) writes to cached address
pop {r4, lr}
bx lr
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.