Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Система, управляемая событиями и SST(super-simple tasker)
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2, 3, 4, 5
DASM
Цитата(brag @ Sep 8 2016, 12:45) *
Для безопасности есть MPU/MMU, если оно действительно нужно - тогда запросто берем и используем.

В случае единого стека всех потоков с трудом представляю его использование
sigmaN
Цитата
WaitForMultipleObjects блокирует пользовательский поток. Все, поток мертвый.
Ну во-первых там есть таймаут. Так что поток не мёртвый ) А во-вторых если ему делать больше нечего, то и поспать не помешает. Хотел донести до вас наконец, что за вашими On по любому стоят и Waitы и ветвления. Но видимо мозг уже слишком перевдут ))))))

Кстати даже этот WaitForMultipleObjects() подразумевает, что мы делегируем эту задачу ядру и оно проверяет за нас и сокеты и таймаут. И если что разбудит.

Ваша задача в SST тоже отработала и типа спит, её запустят когда надо будет. Хороша аналогия? Потому что в пределах ОС мой поток(процесс) это и есть задача. Кто бы мог подумать, а ))))
Мой поток будет разбужен(запущен) когда в сокете появятся данные. Осталось назвать файл OnSocketReadyRead.exe и вы сразу поймете что к чему.

Цитата
Вы, например, когда пишете код на С думаете, какой при этом байт-код получается?
Вы не поверите, но в той или иной степени ДА. Каждый Си программист с опытом работы так или иначе об этом думает. Без этого никуда. Хотя чем дальше тем умнее становятся компиляторы и возможно когда-нибудь настанет тот день, когда это делать вообще будет не нужно. Но на наш век работы хватит.
brag
Цитата(DASM @ Sep 8 2016, 12:49) *
В случае единого стека всех потоков с трудом представляю его использование

Без конкретной задачи и не представите.

Зачем надо MPU? Мне, например, нужен только для того, чтобы отловить переполнение общего стека, поскольку на этапе компиляции этого сделать невозможно. А проверкой за выходы за границы массивов и других проблем с памятью занимается компилятор во время компиляции. Я вот все больше присматриваюсь к языку Rust, там компилятор еще более строгий и еще больше делает проверок на этапе компиляции, чем C++. Там ошибок с памятью быть практически не может, разве что если Вы сами этого захотите - засунете код в специально для этого предназначенный Unsafe-блок.

Зачем нужен MMU? Он нужен, чтобы пользователь вашей системы смог запускать свои приложения на ней, и при этом криво написанные приложения не завалили всю систему. В пределах одной программы он не нужен.
sigmaN
Цитата
Хотя даже с гиговым файлом/файлами однопоточный javascript покажет выше производительность, чем классический многопоточный C. Проверим?
Огласите пожалуйста чётко и ясно что мы будем измерять и в каких условиях и конечно же проверим.

Ну, чтобы всё это было не просто бла бла бла, а конкретно. Что мы измеряем, в каких условиях, каких выводов ожидаем.
Я вот например ожидаю, что один клиент заблокируте всех остальных, либо вы изобретете свой велосипед, сделаете таймр, событие OnTimer и в нем будете в цикле бегать и раздавать каждому сокету по кусочку данных. Это максимум что вы сможете придумать для обхода проблемы )
brag
Цитата
Ну во-первых там есть таймаут. Так что поток не мёртвый )

Нет, он мертвый. Он не может в это время читать с диска, обрабатывать тысячи других событий, которые мы не прописали в WaitForMultipleObjects.
А мой SST - может запросто.

Цитата
Ваша задача в SST тоже отработала и типа спит, её запустят когда надо будет. Хороша аналогия? Потому что в пределах ОС мой поток(процесс) это и есть задача. Кто бы мог подумать, а ))))
Мой поток будет разбужен(запущен) когда в сокете появятся данные. Осталось назвать файл OnSocketReadyRead.exe и вы сразу поймете что к чему.

Ну как оно физически, да еще и под виндой я не знаю и знать особо не хочу. Я работаю на NodeJS/C++ итд а не на винде.

Цитата
Вы, например, когда пишете код на С думаете, какой при этом байт-код получается?
Вы не поверите, но в той или иной степени ДА. Каждый Си программист с опытом работы так или иначе об этом думает. Без этого никуда.

Ну а я, например не думаю. Если мне нужно что-то написать на ассемблере - я пишу на ассемблере и думаю только о нем. Меня не волнует, какой при этом будет байт-код. Меня волнует только ассемблерный код. Если я пишу на C++, тогда думаю только о нем, меня не парит что в это время делает компилятор. Если на Javascript - аналогично, думаю только Jvascript- ом. Если использую библиотеку - меня не волнует как и на чем она написана, меня волнуют только ее характеристики - производительность, простота использования и другие тонкисти.
От такого подхода продуктивность труда возрастает в десятки и сотни раз.

Цитата(sigmaN @ Sep 8 2016, 12:58) *
Огласите пожалуйста чётко и ясно что мы будем измерять и в каких условиях и конечно же проверим.

Ну, чтобы всё это было не просто бла бла бла, а конкретно. Что мы измеряем, в каких условиях, каких выводов ожидаем.
Я вот например ожидаю, что один клиент заблокируте всех остальных, либо вы изобретете свой велосипед, сделаете таймр, событие OnTimer и в нем будете в цикле бегать и раздавать каждому сокету по кусочку данных. Это максимум что вы сможете придумать для обхода проблемы )

Я уже огласил. Конкретно мы будем проверять сколько займет памяти многопоточная реализация на C, а сколько однопоточная na Javascript. А Вы нам предлагаете измерить пропускную способность Вашего Жесткого диска или канала Ethernet.
Что будет за сервер и клиент? Все очень просто:
Клиент подключается к серверу и получает данные, сами данные игнорирует, подключений устанавливает не одно, а столько, сколько Вы захотите.
Сервер - читает с жесткого диска Ваш файл и выдает клиенту.

Реализации будет две сервера и клиента. Одна однопоточная на JS, вторая многопоточная на C. Обе совместимы, то есть Вы можете запустить клиент на JS а сервер на C или наоборот. И сможете проверить, сколько памяти(и проца) заняла каждая из реализаций.
Идет, все устраивает, будете тестить?
arhiv6
brag, спасибо за ответ. Но всё-равно не всё понятно.
Цитата(brag @ Sep 8 2016, 16:11) *
А если задача использует DMA или просто прерывания, тогда эта задача будет только обрабатывать эти прерывания, а остальное время будет отдано другим.

1) Вот в этом месте я не могу представить - как происходит передача управления менее приоритетным задачам, при использовании SST. Пусть наша задача X имеет высший приоритет. Пусть нам в этой задаче надо сделать А, потом записать что-то во флешку, потом сделать B. Как это выглядит: сделали А, отправили с помощью DMA данные, по приходу прерывания от DMA сделали B. На время работы DMA управление можно отдать менее приоритетным задачам. Задаче Y управление отдать не можем - она прервана задачей X и к её стеку доступ мы получить не можем. Отдаем управление низкоприоритетной задаче Z. Если работу с ней мы закончим до прерывания DMA - всё будет хорошо - стек задачи Z нам уже не нужен, можем по прерыванию вернуться к X. А если Z будет работать ещё долго? Произойдёт прерывание DMA, как мы сможем восстановить контекст задачи X, если на вершине стека лежат данные задачи Z? А управление передать необходимо - ведь у X приоритет больше...
2) Даже если всё делать через очереди, то как в задаче X сохранить контекст (например локальные переменные) между A и B ? Это до меня пока не доходит
Нажмите для просмотра прикрепленного файла
brag
arhiv6 , Вы путаете задачу и обработчик события. Это 2 разные понятия.
Обработчик может либо работать, либо не работать(выйти и освободить кадр стека), либо быть вытеснен обработчиком более приоритетного события, стек которого ляжет поверх вытесненного обработчика. Это по сути обычная функция и в моей реализации SST большинство времени планировщик именно это и делает - просто вызывает функцию.
Цепочка вызовов функций физически выглядит так: postEvent() -> scheduler() -> handler(). При чем функция postEvent - шаблонная inline, фактически будет вызван только scheduler() затем handler(). Операция очень быстрая и это от части заслуга компилятора.
А вот задача уже имеет свое состояние и для ее выполнения может понадобится куча событий, которые будут возникать хоть на протяжении целого года.
И состояние этой задачи и есть так называемый контекст, только это не регистры, как в многопоточной модели, а обычные переменные и обьекты C++.
А физически хранятся они там же, где и сама задача - в ячейке очереди задач. Это динамическая память. И она будет освобождена, как только задача завершит свою работу.

Посмотрите мой исходник, который я привел несколькими постами выше http://electronix.ru/forum/index.php?showt...t&p=1447897
DfMassEraseTask - это задача, вернее ее описание. Таких задач может быть создано хоть 100(ну на сколько хватит динамической памяти).
Ее контекст - это переменные:
Код
Df* const df;
delegate<void()> cbk;
SST::TPriority cbk_priority;

Физически - это 3 указателя и 1 байт и того на Cortex-M3/M4 займет это дело 4 слова(из за выравнивания) - 16 байт. А сколько занял бы стек потока? Килобайт? или может 512 байт хватит? Как посчитать?
sigmaN
Цитата
Ну как оно физически, да еще и под виндой я не знаю и знать особо не хочу. Я работаю на NodeJS/C++ итд а не на винде.
Вот тут то мы и начинаем докапываться до сути. Как говорит один мой друг(тоже и этих вэб программистов. PHPшников. Пытающийся что-то изобразить в эмбэддэд) "я слишком высокоуровневый программист чтобы думать об этом".
Вы и о движке JS наверно тоже никогда не думали )

Цитата
Я уже огласил. Конкретно мы будем проверять сколько займет памяти многопоточная реализация на C, а сколько однопоточная na Javascript.
Хорошо. При этом мы конечно же подразумеваем, что в остальных характеристиках эти серверы обеспечивают одинаковый уровень обслуживания.
Как то:
1. Одновременное обслуживание всех клиентов даже если один или несколько клиентов дали сереру задачу, требующую ожидания результата(путь это будет не математика а ожидание нажатия буквы J на клавиатуре или что угодно требующее ожидания.
2. Сравнимые возможности сервера по масштабируемости. Ну т.е. обслуживаем от 1 до 1000клиентов. Используем эффективно многоядерность системы и т.д..
3. Мы гигабайты памяти которые отожрет движок JS тоже подсчитывать будем или как? biggrin.gif Да, с методологией измерений памяти и проца меня ознакомьте. А то это важный момент с кучей неочевидных моментов. )

Я не вижу смысла сравнивать кол-во потребляемой ослом и конем пищи. Эти животные выполняют разные задачи и у них разные возможности. Вы сэкономите пару байт тут, проиграете в производительности там(это типа разные концы линейки масштабирования).
Вам все еще нужно тестирование или дошло?
brag
Цитата
Вот тут то мы и начинаем докапываться до сути. Как говорит один мой друг(тоже и этих вэб программистов. PHPшников. Пытающийся что-то изобразить в эмбэддэд) "я слишком высокоуровневый программист чтобы думать об этом".

Я не PHP-шник, я наоборот начинал с C, когда этого стало мало - полез в плюсы, теперь и этого мало - работаю на других более высокоуровневых языках, типа JS. И еще планирую переходить из плюсов на Rust.
Цитата
Вы и о движке JS наверно тоже никогда не думали )

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

Цитата
1. Одновременное обслуживание всех клиентов даже если один или несколько клиентов дали сереру задачу, требующую ожидания результата(путь это будет не математика а ожидание нажатия буквы J на клавиатуре или что угодно требующее ожидания.

Запросто, только сами будете J жать? Или предложите другой вариант?

Цитата
2. Сравнимые возможности сервера по масштабируемости. Ну т.е. обслуживаем от 1 до 1000клиентов. Используем эффективно многоядерность системы и т.д..

Нагрузку даем по максимуму. 1 клиент это ни к чему. Многоядерность - ну если Вам так хочется - то запросто.

Цитата
3. Мы гигабайты памяти которые отожрет движок JS тоже подсчитывать будем или как?

Конечно.

Цитата
Да, с методологией измерений памяти и проца меня ознакомьте. А то это важный момент с кучей неочевидных моментов. )

Да все очень просто - если винда - в диспетчере задач смотрим сколько памяти осталось, если линукс - команды htop или free. Да и мерить то нечего - какая система первой ляжет - та и проиграла sm.gif

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

Ну вот Вы не видите, поэтому я сомневаюсь, что Вы его проведете. А если проведу я и покажу результаты, то Вы не поверите. Я к тому, что одну и ту же задачу, то есть наш сервер и клиент можно решить двумя способами - многопоточным и однопоточным. И производительность второго способа под серьезной нагрузкой будет в десятки раз выше.
arhiv6
brag, спасибо большое за разъяснение, теперь всё понятно. А можете посоветовать что почитать для тех кто хочет больше узнать про асинхронное программирование (не привязываясь к языку, чтобы упор был именно на алгоритмы)?
brag
Цитата(arhiv6 @ Sep 8 2016, 13:52) *
[/b]brag[/b], спасибо большое за разъяснение, теперь всё понятно. А можете посоветовать что почитать для тех кто хочет больше узнать про асинхронное программирование (не привязываясь к языку, чтобы упор был именно на алгоритмы)?

Не за что. Почитать мало что есть, нужно тренироваться. Для этого отлично подходит Javascript/NodeJS. А потом и на C++11(именно 11, на старых плюсах кода надо больше) сможете или на другом языке, который позволяет программировать в асинхронном стиле просто, без бубна. Можно и на C асинхронно писать, даже на ассемблере, только кода будет слишком много. Лучше поручить эту грязную работу компилятору.
Алгоритмы на любом языке одинаковые, отличается лишь внешний вид кода.
sigmaN
Лаадно, тогда вы проводите правильный тест, выкладываете и результаты и исходники. Чтобы каждый мог проверить при желании. А также увидеть как вы в своем приложении изобрели потоки заново)))
brag
Еще огромное преимущество SST-модели в том, что там все высокоуровневые обьекты, а не низкоуровневые регистры и стеки. Это дает возможность запустить ту же программу на PC, и отлаживать ее там, даже не имея под рукой контроллера.
Также у меня для этого дела написаны мониторы очередей. Кода включен дебаг - любые действия с очередью отлавливает монитор(Observer Pattern, погуглите будете знать что это такое) и выводит информацию на экран - все видно, как на ладони.
Я компилирую проект(который в итоге будет работать на МК) под PC и разрабатываю, отлаживаю его именно на PC. Запускаю, делаю различные тесты, которые провести на МК не предоставляется возможным. И все видно, как на ладони, сколько какая очередь занимает, где нужно больше дать памяти, где меньше, да и целую кучу других тестов(в том числе и юнит-тестов при необходимости)
И только потом, когда софт более-менее сформирован уже подбираю под эту задачу МК, с необходимым количеством ресурсов с необходимым запасом. И потом просто компилирую проект под МК, без изменения кода!
Многопоточная модель мне такого не позволит. На МК поток займет 256 байт стека, а на линукс-ПК он же займет с пол мегабайта. Мало того, необходимый размер стека еще зависит от компилятор(а иногда очень сильно зависит). В то время, как высокоуровневый обьект SST что там, что сям занимает одинаковое количество ячеек очереди на любом компиляторе и на любой платформе.


Цитата(sigmaN @ Sep 8 2016, 14:34) *
Лаадно, тогда вы проводите правильный тест, выкладываете и результаты и исходники. Чтобы каждый мог проверить при желании. А также увидеть как вы в своем приложении изобрели потоки заново)))

Ок, делаю.
sigmaN
Вы видимо думаете, что мы тут все совсем темные, даже про паттерн наблюдатель не слышали и гуглить про него должны )))))
AlexandrY
Цитата(brag @ Sep 8 2016, 14:40) *
Ок, делаю.


Да не парьтесь.
Гигантскую по вашим меркам RTOS MQX спокойно портировал вот на эту платформу - https://geektimes.ru/post/268918/
Вот здесь можете посмотреть порт этой RTOS - https://github.com/Indemsys/Light-Control-M...X_light_porting

В MQX есть в том числе и ваша гениальная идея с очередями задач вместо вытесняющей многозадачности.

А кому нужен чистый автоматный подход тот использует IAR visualState
Либо StateFlow в Matlab-е. Тож можно исходники нагенерить.
Вот с чем надо ваш подход сравнивать.
brag
Цитата
В MQX есть в том числе и ваша гениальная идея с очередями задач вместо вытесняющей многозадачности.

SST - это вытесняющая многозадачность.

Цитата
Вы видимо думаете, что мы тут все совсем темные, даже про паттерн наблюдатель не слышали и гуглить про него должны )))))

Кто-то слышал, а кто-то не слышал. Все мы учимся.
Kabdim
А топик так хорошо начинался. rolleyes.gif Тоже что ли залезть на трибуну - рассказать про protothreads?... biggrin.gif
Цитата(brag @ Sep 8 2016, 15:42) *
SST - это вытесняющая многозадачность.

Вытесняющая многозадачность (приоритетная многозадачность, англ. preemptive multitasking, дословно упреждающая многозадачность) — это вид многозадачности, при которой операционная система принимает решение о переключении между задачами по истечению некоего кванта времени[1].
rolleyes.gif
AlexandrY
Цитата(brag @ Sep 8 2016, 15:42) *
SST - это вытесняющая многозадачность.


SST - это невытесняющая автоматная лабуда. Кооперативка по сути.

Не надо тут искажать семантику базовых понятий в embedded.
brag
Цитата(AlexandrY @ Sep 8 2016, 16:03) *
SST - это невытесняющая автоматная лабуда. Кооперативка по сути.

Цитата
Совместная или кооперативная многозадачность
Тип многозадачности, при котором следующая задача выполняется только после того, как текущая задача явно объявит себя готовой отдать процессорное время другим задачам.

В SST не так - в SST любая задача может быть в любой момент прервана, а значит это уже не кооператив.

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

https://ru.wikipedia.org/wiki/%D0%9C%D0%BD%....81.D1.82.D1.8C

SST полностью подходит под это определение.

//---------------- update ------------------
И так, как и обещал сделать тест. Первый сделал. На написание кода ушло 24 минуты, +15 минут на тестириование. В итоге моя система Линукс на ноуте выдержала порядка 600 одновременных соединений.
При чем сервер и клиент запущены были на одной машине, нет сейчас 2х компов.
Дальше узким местом стало ядро линукс, которое при обработке сокетов сожрало проц! Сервер и клиент при этом заняли каждый по 15% проца и каждый по 160мб оперативки. Строго один поток для сервера, один для клиента.
При чем, когда сервер запущен сам, без единого коннекта он жрет столько же!
То есть оверхед только из за ОС, а Javascript гораздо больше сможет обрабатывать соединений.

// еще один апдейт - добавил измеритель количества коннектов в секунду. Ноут обрабатывает примерно 10000 коннектов в секунду!
Нажмите для просмотра прикрепленного файла

Теперь делаю то же самое в классической многопоточной модели на C. Это займет у меня гораздо больше времени, чем я потратил на реализацию JS.
brag
Хух, сишный клиент осилил. То segfault схлопнешь, то еще что-то, что в более совершенных языках отслеживается еще на этапе компиляции, да и работать гораздо удобнее. Давно на C не писал. Ну да ладно, то такое..

1000 потоков так и не смог создать - линукс падает намертво, приходится ресет жать. Кое как создает 400 потоков и сжирает всю память, комп жутко тормозит и выдает еле 600 коннектов в секунду против 10000 на JS. И это при том, что сервер остался на JS. Сейчас сделаю сервер на C, посмотрим сколько связка C-сервер/C-клиент выдаст. Подозреваю, что будет около 200 потоков и столько же одновременных коннектов.

Привожу код клиента прямо сдесь.
Компилоровать так: gcc -O2 -Wall -lpthread client.c

По количеству строк кода - JS 42 строки, C - 105 строк.
CODE
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include <netdb.h>
#include <netinet/in.h>

#include <unistd.h>
#include <pthread.h>


int connect_to_server(in_addr_t h_addr, int port){
// Create a socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
//exit(-1);
return -1;
}

struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = h_addr;
serv_addr.sin_port = htons(port);

// Now connect to the server
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
//exit(-1);
return -1;
}

// Now read server response
char buffer[1024];
while(1){
int n = read(sockfd, buffer, sizeof(buffer));
if(n<0){
//perror("ERROR reading from socket");
break;
}
//printf("%s",buffer);
}
close(sockfd);

return 0;
}

void* Thread_worker(void* threadid){
const int port = 5555;
const char* const host = "127.0.0.1";

struct hostent* const server = gethostbyname(host);
if(server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(-1);
}

in_addr_t s_addr;
bcopy(server->h_addr, &s_addr, sizeof(s_addr));

while(connect_to_server(&s_addr, port)==0){}

pthread_exit(NULL);
return 0;
}

int main(int argc, char *argv[]) {
const int MaxConnections = 400;
pthread_t threads[MaxConnections];
pthread_attr_t attr;

// thread attributes
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

int rc;

// Create threads
long t;
for(t=0; t<MaxConnections; t++){
rc = pthread_create(&threads[t], &attr, Thread_worker, (void*)t);
if(rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_attr_destroy(&attr);

printf("Joinning threads\n");
// join threads
void *status;
for(t=0; t<MaxConnections; t++){
rc = pthread_join(threads[t], &status);
if(rc){
printf("ERROR; return code from pthread_join() is %d\n", rc);
exit(-1);
}
printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status);
}

printf("Main: program completed. Exiting.\n");
pthread_exit(NULL);
return 0;
}
sigmaN
Интересный способ проверить сколько потоков может вывезти система )))

А мы уверены, что все клиенты параллельно качают файл размером гигабайт с сервера? А некоторые при этом ждут чего-то иногда?

Не думаю, что стоит изобретать велосипед на Си. Достаточно испробовать что-то серийное и прверенное.
Тот-же lighttpd в качестве системы реализующей то о чем вы говорите(один поток на асинхронном io) и что-нибудь типа апача(хотя не хотелось бы связываться с этим монстром) в качестве примера традиционного подхода.

По-моему вот здесь кто-то уже всё протестировал http://zgadzaj.com/benchmarking-nodejs-bas...inst-apache-php
Причем подход по грамотнее получился ибо поменьше изобретали велосипедов. Вон ApacheBench оказывается какой-то есть для этих целей.

Жаль, что памяти таки JS потребовалось больше даже в сравнении с апачем ))))

Аа, черт, они на апаче PHP крутили. Тест в топку!. Но инструментарий можно на вооружение взять.
brag
Цитата
А мы уверены, что все клиенты параллельно качают файл размером гигабайт с сервера? А некоторые при этом ждут чего-то иногда?

Ну так сервак на JS показывает количество коннектов в секунду - это количество скачанных файлов в секунду. Правда не гиговых, а нормальных. Можете сами проверить на гиговом, если есть желание.
И однопоточный JS клиент может скачать 10000 файлов в секунду, при этом система нормально работает, памяти полно свободной, торохтит только диск и нельзя создать сокет - закончились лимиты ядра. А многопоточный на С смог скачать только 600-700 файлов на том же компе - то есть в 16 раз меньше. При этом убил всю систему, остановить его можно только кнопкой reset, мышь и клава да и вся система зависает намертво.

Делаю аналогичный многопоточный сервер, посмотрим что сможет выдержать он.

Цитата
Не думаю, что стоит изобретать велосипед на Си. Достаточно испробовать что-то серийное и прверенное.

Тут даже сравнивать нечего. Сервак на NodeJS убивает любой другой многопоточный, а тормознутого Апача десять раз убивает. Он может только конкурировать с серваком на Go, при одних задачах лучше NodeJS, при других - Go. Я работал и на том и на другом.

И так, осилил сервак, отловил баги, даже отладчик пришлось запускать sad.gif Как просто на С наделать ошибок..
С серваком не все так плохо, как я думал. Хоть он потребляет и больше ресурсов(где-то на 100мб больше памяти, чем JS), чем такой сервак на JS, но и способен обрабатывать порядка 7000 коннектов(против 10-11 тыс JS) в секунду и примерно столько же сокетов, сколько и JS.

Думал почему, дебажил, и наконец понял - Коннекты принимаются в одном потоке - в самом низу функции main() в вечном цикле accept() блокирует и принимает коннекты, а их обработка происходит уже в порожденных потоках. Порожденный поток успевает быстро отдать файл и выходит, получается, одновременно создано потоков примерно штук 40.
У клиента же все по другому - коннекты происходят в потоках и там на каждый коннект создается 1 пток.

Но это простая ситуация - отдача файла, который ОС положила в кеш. Попробую на днях услижнить задачу, чтобы ос отдавала явные данные, например рандом, чтобы потоку нужно было потрудиться/подождать. Думаю тогда сервак на C станет таким же тормознутым, как и клиент и сожрет всю оперативку, в то время как JS будет запросто работать.

По количеству строк JS тоже ессно выиграл 67 строк в JS против 151 на C. Асинхронное неблокирующее программирование проще и эффективное, чем многопоточное блокирующее.
Интересно такой же многопоточный сервак реализовать на чем-то аналогичном динамическом, типа Java. Думаю он загнется при сотне клиентов. А то силы не равны получаются, быстрый С против медленного JS, но последний все равно выиграл из за своей асинхронной натуры.
CODE
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include <netdb.h>
#include <netinet/in.h>

#include <unistd.h>
#include <pthread.h>

long n_connections = 0;
long total_connections = 0;
pthread_mutex_t g_mutex;

void* Thread_connection(void* fd1){
long sock = (long)fd1;

// work with shared vars
pthread_mutex_lock(&g_mutex);
n_connections++;
total_connections++;
pthread_mutex_unlock(&g_mutex);
// ------------------

FILE* fd = fopen("file.txt","r");
if(fd==0){
perror("fopen");
goto exit1;
}

// read and send file - blocking!
char buffer[4096];
while(1){
// read chunk
size_t r = fread(buffer, 1, sizeof(buffer), fd);
if(r<=0)break;
// buffer[r]=0;
//printf("%s\n", buffer);
// send
r = send(sock, buffer, r, 0);
if(r<=0){
perror("Thread_connection: socket error");
break;
}
};
fclose(fd);

exit1:
// fuck...
pthread_mutex_lock(&g_mutex);
n_connections--;
pthread_mutex_unlock(&g_mutex);
// ------

close(sock);
pthread_exit(NULL);
return 0;
}

void* Thread_watcher(void* thr){
double tput = 0;
const double a = 0.9;
long prev_connections = 0;

while(1){
// work with shared vars
pthread_mutex_lock(&g_mutex);
long tcon = total_connections;
total_connections = 0;
prev_connections = n_connections;
pthread_mutex_unlock(&g_mutex);
// ----------------

// low-pass filter
tput = tput*(1-a) + tcon*a;

if(tput>=1){
printf("now approx %ld clients are connected. Tput: %.1f conn/s\n",
prev_connections, tput);
}

sleep(1);
}
return 0;
}


void startWatcher(){
// create watcher thread
pthread_t thr;
int rc = pthread_create(&thr, 0, Thread_watcher, (void*)0);
if(rc){
printf("startWatcher ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}

int main(){
int port = 5555;

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}

// Initialize socket structure
struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));


serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);

// Now bind the host address using bind() call.
if(bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}

// listen and accept connections in endless loop
listen(sockfd, 10000);

// create fucking mutexes
pthread_mutex_init(&g_mutex, NULL);
startWatcher();

// accept connections in endless loop
struct sockaddr_in cli_addr;
unsigned clilen = sizeof(cli_addr);
while(1){
// Accept actual connection from the client - blocking
long newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if(newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}

// create thread for each connection
pthread_t thr;
int rc = pthread_create(&thr, 0, Thread_connection, (void*)newsockfd);
if(rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
pthread_detach(thr);
}

return 0;
}


Цитата
Жаль, что памяти таки JS потребовалось больше даже в сравнении с апачем ))))

У меня node сожрал 160 метров. А у них 1.5 гига. Что-то у них не то с тестами. Будет время я сам прогоню апач и node, сравним с результатами, доступными в интернете.
Но все равно Node у них показал производительность в 6 раз больше апача.
Ну пусть PHP будет, так Js тоже полностью динамический и тяжелый язык, такой же тяжелый, как и PHP. Только PHP синхронный блокирующий, а Node асинхронный неблокирующий - на этом принципе и построен SST.

А если я запущу асинхронный сервак помимо сокетов линукса (есть для этого готовые библиотеки), то он уделает любой другой раз в 100-200 и даже в 1000. Посмотрите сканер портов Mass-scan - он способен "расплавить" любую сетевую карту - работает помимо сокетов, всего 2 потока(ридер и райтер) и полностью асинхронный https://github.com/robertdavidgraham/masscan

Та даже NodeJS на моем ноуте обгоняет сам линукс - у node еще куча запасов, а linux уже не тянет..
Herz
Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)". Будут возражения? Или переименуем всю тему? Автор, что думаете?
brag
Возражений нет, но я не автор. Про аллокатор у меня тоже есть материал, доберемся и до него. тк в SST без него и без ООП и динамических обьектов делать нечего.
Обычный сишный код там не уместен, все равно придете к динамическим обьектам, хоть и на С реализованным.

Теперь про асинхронщину - весь мир уходит от классических потоков. Тот же Nginx взять да и куча других проектов, не только JS.
Цитата
Nginx uses an asynchronous event-driven approach to handling requests, instead of the Apache HTTP Server model that defaults to a threaded or process-oriented approach, where the Event MPM is required for asynchronous processing. Nginx's modular event-driven architecture[18] can provide more predictable performance under high loads.

И работает под нагрузкой он примерно в 9 раз быстрее апача. А Node еще быстрее, тк Node - это язык, а не сервер - нет лишней прослойки.
sigmaN
Как весело мы ушли в сторону от обсуждения планировщика задач в эмбэддэд к тестированию вэб серверов под линуксом и вендой. Не могу сказать что это приавльно, но прикольно )

Очень жаль, что всё законичлось применением готового Node.js и обработкой пары собитий on, которые как вообще там появляются не ясно и вникать вы видимо не хотите.

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

Я уже говорил о том, что ваша программа(или движок V8 в случае с JS) это для опереционной системы задача(процесс/поток не так уж важно в данном случае). Получает эта задача процессорное время квантами ибо в ОС встроен нормальный планировщик а не SST. Ядру ОС планировщик тоже дает процессорное время. Теми-же квантами. В ядре вертятся сокеты, обработка пакетов сетевухи и т.д. ОК? Это понятно? Далее, используя API ядра(асинхронный ввод/вывод) на уже готовой операционной системе вы по сути просто делегируете генерацию событий связанных с сокетом ядру. Ядро там в нужное место записывает ваши пожелания и выполняет их, когда кванты времени планировщик выдаст. Там всё мясо, там всё веселье то происходит.
Собственно с обсуждения именно этого веселья мы с вами начали. И я уже пытался обратить на это внимание, но вы сказали, что вам это не важно, потом выкатили Node.JS где это действительно не важно. Круто, что я могу сказать) Как жаль, что немного не в тему.
Вы заявили, что в чудо сервере будет один поток и один стек, а на самом деле запустили задачу под нормально ОС, где ядро и планировщик задач раскидал всё за вас и раздал всем события, на которые вы гордо в одном потоке отреагировали. Ну право же, от изначальной темы беседы мы ушли так далеко, как только было возможно.

Вы же понимаете, что итоговый путь от пакета на сетевухе до вашего события On был пройден через несколько стеков и переключений контекстов(полных переключений, не куцых SSTшных).
И я не хочу сказать, что на SST вы в принципе не сделаете такой сервер. Сделаете. Кактусов покушав немного )

В итоге лично я могу сказать только то, что вы меня удивили как этот JS шевелится. Чуваки работающие над движком V8 однозначно знают своё дело!

И таки да, еще можно добавить, что для однотипных задач(обслуживание клиентов сервером это однотипная для каждого клиента задача) не стоит создавать поток.
Тем не менее это не отменяет необходимости нормального переключения задач вместо SST, где задачи приходится костылить до состояния зашел-вышел!

Уже не один человек высказал мнение, что SST это не так круто как вам кажется и имеет кучу ограничений и проблем. Однако вы по прежнему упрямствуете и, как вы сами выразились, вдуваете себе в голову On wink.gif
AHTOXA
Цитата(Herz @ Sep 9 2016, 00:38) *
Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)".

И перенести, наверное, в "Операционные системы".
brag
NodeJS свободно может работать без ОС и потоков вообще (и работает - есть даже реализации под Embedded ссылки я приводил), в то время, как классический многопоточный код на C работать без ОС не сможет - ему нужны треды, мютексы и семафоры.
Точно так же и SST - может работать как поверх любой ОС, так и сам на голом МК, хоть на AVR. Он не привязан к железу и свободно реализовывается в 200-300 строк на С++. Да, у меня часть написана на ассемблере(строчек на 20) но это было сделано только ради небольшого увеличения производительности на конкретном камне(Кортекс).

Ессно в конкретном тесте Node - это процесс по верх ОС, но это вынужденная мера, тк весь мир работает на линуксах и виндах, а не на голом железе.
И та же идея Node запросто работает на голом МК вообще без единой библиотеки. И может обрабатывать гораздо больше событий(не обязательно однотипных - любых, работа с периферией, внешними устройствами, итд) на том же МК, чем классическая FreeRTOS или любая другая. При чем код получается даже проще, а если начать мыслить в асинхронном стиле, то и подавно.
Мне, например было довольно сложно переключится с асинхронной модели мышления к синхронной, когда я делал сервак и клиент на С, много времени заняло по сравнению с Node.

99% задач, которые делают на FreeRTOS и подобных - решаются на SST более простым, натуральным способом и требуют в разы меньше памяти.

Цитата
Надеялся увидеть как вы с SST будете изобретать велосипед и отдавать клиентам данные порциями, чтобы хоть как-то обслуживать их всех параллельно

В этом нет необходимости. Наоборот, такая необходимость есть в многопоточной модели(посмотрите мой сишный код выше), тк операция чтения блокирующая, и нужно читать файл порциями, тк попросту я не могу загнать весь файл в память иначе даже при 20 потоках она кончится(или включится подкачка).
В асинхронном стиле этого делать не нужно - данные идут через высокоуровневые структуры и какими там порциями передаются я знать не знаю. Скорее всего такими же, какими отдает жесткий диск, вернее его кеш.
У меня в моем SST данные идут порциями, натуральными для конкретного устройства. Например, если это флешка - то страницами. При чем это все происходит через высокоуровневые структуры и пользователь не видит что там физически происходит.

Вот пример из моего же кода. Открытие, чтение файла и передача его в сокет в однопоточном асинхронном Node
Код
    var readable = fs.createReadStream('file.txt');
    readable.pipe(socket);

При чем если написать принт после этих строчек, то принт сработает до окончания передачи файла, а не после (если файл достаточно большого размера или канал медленный.

А вот в многопоточном синхронном С:
Код
FILE* fd = fopen("file.txt","r");
    if(fd==0){
        perror("fopen");
        goto exit1;
    }

    // read and send file - blocking!
    char buffer[4096];
    while(1){
        // read chunk
        size_t r = fread(buffer, 1, sizeof(buffer), fd);
        if(r<=0)break;
        // send
        r = send(sock, buffer, r, 0);
        if(r<=0){
            perror("Thread_connection: socket error");
            break;
        }
    };
    fclose(fd);

Где проще?
В моем SST все примерно так и выглядит - передача через высокоуровневые обьекты(очереди/пайпы называйте как хотите) и все прозрачно для пользователя, и очень быстро - оверхеда минимум, меньше уже практически невозможно.

Цитата
Уже не один человек высказал мнение, что SST это не так круто как вам кажется и имеет кучу ограничений и проблем. Однако вы по прежнему упрямствуете и, как вы сами выразились, вдуваете себе в голову On wink.gif

Покажите чем реально SST хуже потоков? Какую задачу на SST реализовать сложнее, чем на потоках? Давайте мне любой многопоточный код и я его переведу в более красивый и быстрый асинхронный SST-шный wink.gif
Мы говорим о реальных практических задачах, а не где то там что-то там требует тайм-кванты. Давайте реальную задачу с тайм-квантами и я покажу как от них избавится sm.gif

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

Вот пример аналогичного кода(чтение из терминала через Uart и передача другому чипу) в моем SST:
Код
void Term::UartReader::operator()(
        T_RxFifo_Base* fifo, const uint8_t* ptr, int len)
{
    term->telit->cmux.send(TelitMux_Gsm, ptr, len, [fifo](){
        fifo->signal_dataAccepted();
    });
}

По-больше кода чем у Node 3 строчки против одной, но это C++, такой он есть verbose язык.. Хотя могу и однй сделать, запросто, если будет такая необходимость.
shreck
2brag

В приведенном вами коде проскакивает
Код
delegate<void()>

Может быть выложите реализацию делегатов?
Сам использую свой велосипед и собираю велосипеды других с целью повышения образования.
Serhiy_UA
Цитата(Herz @ Sep 8 2016, 22:38) *
Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)". Будут возражения? Или переименуем всю тему? Автор, что думаете?

Конечно надо бы разделить тему на усмотрение модератора. Мне, к примеру, обсуждение уже совсем не понятным становится. Мой вопрос скорее относился к вводным понятиям о кучах, да и тема мною сформулирована вроде бы четко.
Хотя интересно было узнать то, что помимо стандартных new/delete, существуют и свои средства (стратегии) для управления кучами.
В институте был курс "Организация вычислительных процессов в ЭВМ", в частности с понятиями об операционных системах и их работе с очередями пользовательских программ, были и книги по этой теме. А сейчас какие есть книги (ru/eng) на эту же тематику, но c учетом новых подходов?
brag
Цитата
Хотя интересно было узнать то, что помимо стандартных new/delete, существуют и свои средства (стратегии) для управления кучами.

dew/delete никуда не деваются, но мы их можем перегружать и иметь свою реализацию. Потом есть всякие smart-pointerы и reference count-ы - это для того, чтобы голова за delete не болела. Но их надо использовать с осторожностью(равно как и delete)
Динамическая память напрямую связана с той средой, в которой работаете. В многопоточной среде динамическая память приводит к большим тормозам и проблемам - поскольку на время работы аллокатора надо блочить все потоки и планировщик, а возможно и часть прерываний. В то время как в SST-модели в этом нет необходимости, блочится только часть системы, а не практически вся.

Цитата
Может быть выложите реализацию делегатов?
Сам использую свой велосипед и собираю велосипеды других с целью повышения образования.

Выложу ниже. Велосипеды/не велосипеды, но эта реализация отлично работает многие годы. А сделал я ее потому что когда я попытался применить нормальный std::function на Atmega8, то это сразу сожрало всю оператирку(и все рано ее не хватило), потянуло за собой кучу ненужного мусора. В итоге сделал этот велосипед na 80 строк(вместо нескольких тысяч в std) за час.

Держите статическую версию, в большинстве случаев ее достаточно. А на слабых МК (до 8-16кб оперативки) только она и уместна
CODE
template<class T, int d_size=1> class delegate;

template<class TRet, class... TArgs, int d_size>
class delegate<TRet(TArgs...),d_size> {
public:
delegate(){}

// copy constructor and copy operator=
template<int size_another>
delegate(const delegate<TRet(TArgs...),size_another>& another){
operator=(another);
}

template<int size_another>
delegate& operator=(const delegate<TRet(TArgs...),size_another>& another){
static_assert(sizeof(another) <= sizeof(*this), "RHS too large");
wrapper_ = another._private_get_wrapper();
for(unsigned i=0; i<sizeof(another._private_get_data()) / sizeof(data_.vp[0]); i++){
data_.vp[i] = another._private_get_data().vp[i];
}
return *this;
}

// lambda, static function and function object
template<class TLambda>
delegate(const TLambda& lambda){
setFunction(lambda);
}

template<class TLambda>
delegate& operator=(const TLambda& lambda){
return setFunction(lambda);
}

// For lambda, static function and function object
template<class TLambda>
delegate& setFunction(const TLambda& lambda){
struct Wrapper{
Wrapper(const TLambda& lambda) : la(lambda) {}

static TRet wrapper(void* data, TArgs ... args){
Wrapper *w = static_cast<Wrapper*>(data);
return w->la(args...);
}

void* operator new(size_t, void *place){ return place; }

TLambda la;
};
static_assert(sizeof(Wrapper) <= sizeof(data_), "Wrapper too large");

new(&data_) Wrapper(lambda);
wrapper_ = &Wrapper::wrapper;
return *this;
}

// invoke wrapper
TRet operator()(TArgs ... args){
return wrapper_(&data_, args...);
}

TRet operator()(TArgs ... args)const{
return wrapper_(const_cast<t_data*>(&data_), args...);
}

private:
typedef TRet (*t_wrapper)(void*, TArgs...);

t_wrapper wrapper_;
union t_data{
void *vp[d_size];
} data_;

// really private
public:
const t_wrapper& _private_get_wrapper()const{ return wrapper_; }
const t_data& _private_get_data()const{ return data_; }
};


Несмотря на такое изобилие кода(который делается 1 раз) на самом деле для выполнения делегата компилятор генерирует 2 инструкции(на кортексе) - чтение указателя wrapper и прыжок на него sm.gif
А тело самой функции-делегата встраивает в wrapper, фактического вызова функции внутри wrappera нет.
Создание делегата тоже очень простая операция - запись указателя(одна инструкция) и запись данных(обычно тоже одна инструкция, в зависимости от обьема данных - сколько слов, столько и инструкций или одна STM для всех)
Serhiy_UA
Цитата(brag @ Sep 9 2016, 09:12) *
dew/delete никуда не деваются, но мы их можем перегружать и иметь свою реализацию. Потом есть всякие smart-pointerы и reference count-ы - это для того, чтобы голова за delete не болела. Но их надо использовать с осторожностью(равно как и delete)

В моем случае под 7 версией FreeBCD на индустриальном компьютере в режиме реального времени одновременно работают пять равно приоритетных программ. Каждая через new/delete (без перегрузок) создает для себя динамические объекты в куче. Если куча общая, то ОС должна четко организовывать управление кучей для всех программ. Хотел узнать (прочесть) об этом немного больше, хотя и так кое-что уже прояснилось.
sigmaN
Очень бы хотелось увидеть как бы вы обслуживали много клиентов, рисовали GUI и делали еще то что нужно и именно на голой системе и именно со своим SST и чтоб в масштабах ВСЕЙ системы был бы один стек и один поток. Собственно это то, с чего мы начинали нашу дискуссию.

Цитата
NodeJS свободно мо жет работать без ОС и потоков вообще (и работает - есть даже реализации под Embedded ссылки я приводил),

Кто асинхронный ввод/вывод то будет обслуживать?
Всё это твёрдо стоит на крепких ногах операционной системы
http://man7.org/linux/man-pages/man7/aio.7.html
https://msdn.microsoft.com/ru-ru/library/wi...3(v=vs.85).aspx
И для того чтобы организовать то-же самое вам придется реализовать примерно такие-же механизмы в своей системе.
И вот тут уже будет существенная разница между SST + один стек на всё провсё и нормальным подходом!
Хватит уже нам тут рекламировать как хорошо и естественно всё проходит без блокировок, проснитесь и откройте глаза )

Цитата
Вот пример из моего же кода. Открытие, чтение файла и передача его в сокет в однопоточном асинхронном Node

Показать как мало кода нужно на верхнем уровне, когда всё завёрнуто красиво мы все можем. Это уже больше похоже на какое-то поклонение высокоуровневому коду )

Начали мы с SST где один стек и один поток на всех в системе давайте и будем продолжать в том-же духе. А не гордиться тем как здорово можно с помощью ядра ОС поставить в очередь IO операцию и подписаться на события (говоря языком JS) от этой операции.
Правильно уже вам тут говорили, вы даже реализацию TCP/IP со своим SST запаритесь делать.
brag
Цитата
Очень бы хотелось увидеть как бы вы обслуживали много клиентов, рисовали GUI и делали еще то что нужно и именно на голой системе и именно со своим SST и чтоб в масштабах ВСЕЙ системы был бы один стек и один поток. Собственно это то, с чего мы начинали нашу дискуссию.

Так я именно это и постоянно делаю! У меня GUI, сложные вычисления, куча IO(флешки,ацп,датчики 1wire) отлично работают на SST без единых тормозов и проблем. На многопоточной оси аля FreeRTOS постоянно были проблемы, то гонки, то оверхед большой, то еще что-то.
Цитата
И для того чтобы организовать то-же самое вам придется реализовать примерно такие-же механизмы в своей системе.

Ну так они реализованы - это SST sm.gif При чем довольно просто, сам SST - это около 300 строк кода, динамическая очередь - почти 200 строк кода, очередь задач - 60 строк кода, делегат(Вы уже его видели выше) - 80 строк кода

Цитата
Правильно уже вам тут говорили, вы даже реализацию TCP/IP со своим SST запаритесь делать.

Сделана и отлично работает.

Цитата
Начали мы с SST где один стек и один поток на всех в системе давайте и будем продолжать в том-же духе.

Ну так я показал, как на своем SST я работаю с DataFlash - привел полный код операции ее стирания. Что там непонятного? Могу любую другую задачу показать как я делаю ее на SST. Говорите какую - приведу код, тк у меня практически все задачи уже решены в виде простейших библиотек и я их использую когда надо - строю программу из этих кубиков.

Цитата(Serhiy_UA @ Sep 9 2016, 10:23) *
В моем случае под 7 версией FreeBCD на индустриальном компьютере в режиме реального времени одновременно работают пять равно приоритетных программ. Каждая через new/delete (без перегрузок) создает для себя динамические объекты в куче. Если куча общая, то ОС должна четко организовывать управление кучей для всех программ. Хотел узнать (прочесть) об этом немного больше, хотя и так кое-что уже прояснилось.

Ну в таких сложных ОС, как FreeBSD управление кучей очень сложное. Куча для каждого процесса своя, а для потоков внутри этого процесса общая, и ессно ядру нужно ею управлять так, чтобы это не сказывалось на других процессах, там все довольно сложно и вникать туда смысла нет - жизни не хватит.
Зато есть смысл делать динамику на embedded, где и памяти мало, и других ресурсов, но чувствовать себя там практически так, как будто под PC пишешь.

В FreeBSD там все довольно грамотно да и работать в асинхронном стиле(без ожиданий и циклов) там гораздо проще, чем в линуксе. В линуксе до сих пор нет нормальных асинхронных IO, есть костыли вроде epoll. Тогда как в FreeBSD есть Kqueue. Думаю, если мой тест запустить на FreeBSD, то NodeJS еще сильнее убьет в хлам классические pthread.

А тем временем провел другой тест - сервер выдает не файл(который ОС выдает из кеша, а не читает по факту с диска) а рандомные данные по таймеру по 100 байт каждые 10 милисекунд вечно, пока клиент не отключится.
Вот так это выглядит на NodeJS, в однопоточном асинхронном стиле:
Код
        var timer;
        function on_socket_closed(){
            n_connections--;
            clearInterval(timer);
        }

        // Periodically send random data to client
        timer = setInterval(function(){
            var str;
            for(var i=0; i<100; i++){
                str+= String.fromCharCode(Math.random()*256);
            }
            socket.write(str);
        }, 10);

То есть для каждого клиента мы создаем таймер, в обработчике события которого генерируется рандом и потом ставится в очередь на отправку клиенту. Не шибко эффективный способ,тк время генерации рандома велико - тк это мы делаем синхронным блокирующим способом - в цикле for. правильно было бы прoчитать эти данные из готового асинхронного рандом-генератора (типа /dev/urandom) и передать клиенту - моментально, одной строчкой. Но тк я буду делать то же самое на С, только в многопоточном стиле - специально сделал так, чтобы сразу не убить в хлам многопоточную модель sm.gif
И того такой сервер на моем ноуте может обслуживать 3900 клиентов одновременно и занимает при этом 80МБ оперативки. При чем клиент запущен на том же ноуте, асинхронный тот же, что и прежний, на JS.

Сейчас наклепаю на C в мнгопоточном блокирующем стиле.
sigmaN
Всё всё, хватит, мы уже поняли что на Node.js можно в две строки запустить сервер и он будет быстро обрабатывать большое кол-во клиентов.
Покажите уже как всё это реализуется когда во всей системе один поток и один стек для всех ) Собсвтенно выше я уже написал всё тоже самое)

Ваш пример не работает в один поток потому что ядро работает параллельно с вами, в классическом потоке, со своим стеком и шлет вам в юзерспэйс приветы(ивэнты) с глубины ring0 sm.gif
brag
Так я уже показал, как я работаю с Датафлеш. Привел полный код операции стирания http://electronix.ru/forum/index.php?showt...t&p=1447897
Хотите на код самой SPI посмотреть? Запросто, сейчас выложу
Вот
CODE
#include <drv/LPC17xx/ssp.hpp>
#include <kernel-sst/taskqueue.hpp>

class SspTaskQueue : public TaskQueue<48>{
public:
SspTaskQueue(Ssp* ssp, SST::TPriority priority):
TaskQueue(priority),
ssp(ssp) {}

// proxy, use inside tasks
int dmaChannelRx()const{ return ssp->dmaChannelRx(); }
int dmaChannelTx()const{ return ssp->dmaChannelTx(); }

bool DmaWriteRead(const void *src, void *dst, int len){
return ssp->DmaWriteRead(src,dst,len);
}

void setup(uint8_t size, bool cpol, bool cpha, uint16_t clkdiv){
return ssp->setup(size, cpol, cpha, clkdiv);
}

void flush()const{
return ssp->flush();
}

private:
Ssp *ssp;
};

Или мало?
Тогда вот
CODE
#include "dma.hpp"

class Ssp{
public:
Ssp(int port);
// DMA
int dmaChannelRx()const{ return dma_channel_rx; }
int dmaChannelTx()const{ return dma_channel_tx; }

bool DmaWriteRead(const void *src, void *dst, int len){
if(Dma::isChannelsBusy(dma_channel_rx,dma_channel_tx))return false;
start_dma_read(dst,len);
start_dma_write(src,len);
return true;
}

// Дальше можно не смотреть, это чисто платформо-зависимый код
private:
void start_dma_write(const void *src, int len){
Dma::startChannel(dma_channel_tx,
src, (void*)&ssp->DR, 0, len,
DMACCControl_xBSize_1, DMACCControl_xBSize_1,
DMACCControl_DWidth_8, DMACCControl_DWidth_8,
true, false,
static_cast<DMA_Peripheral>(0), static_cast<DMA_Peripheral>(dma_peripheral_tx),
DMA_TransferType_Mem2Periph
);
}

void start_dma_read(void *dst, int len){
Dma::startChannel(dma_channel_rx,
(void*)&ssp->DR, dst, 0, len,
DMACCControl_xBSize_1, DMACCControl_xBSize_1,
DMACCControl_DWidth_8, DMACCControl_DWidth_8,
false, true,
static_cast<DMA_Peripheral>(dma_peripheral_rx), static_cast<DMA_Peripheral>(0),
DMA_TransferType_Periph2Mem
);
}

private:
LPC_SSP_TypeDef *ssp;
int8_t dma_channel_rx;
int8_t dma_channel_tx;
uint8_t dma_peripheral_rx;
uint8_t dma_peripheral_tx;
uint8_t dummy_ff;

Хватит? Или еще dma кинуть? Там кроме платформозависимого больше ничего нет.

Цитата
Ваш пример не работает в один поток потому что ядро работает параллельно с вами, в классическом потоке, со своим стеком и шлет вам в юзерспэйс приветы(ивэнты) с глубины ring0 sm.gif

Кто мешает работать без этого ядра, потоков и ring0, на простом МК вроде Атмега8 и получить точно такое же поведение программы, как в NodeJS ? Ведь для программы потоки не нужны, они нужны(а нужны ли?) конкретной оси(линуксу) для планировки программы.
alexunder
Цитата(sigmaN @ Sep 9 2016, 09:52) *
Всё всё, хватит, мы уже поняли что на Node.js можно в две строки запустить сервер и он будет быстро обрабатывать большое кол-во клиентов.
Покажите уже как всё это реализуется когда во всей системе один поток и один стек для всех ) Собсвтенно выше я уже написал всё тоже самое)

Ваш пример не работает в один поток потому что ядро работает параллельно с вами, в классическом потоке и шлет вам вюзерспэйс приветы(ивэнты) с глубины ring0 sm.gif

Вероятно, есть критерии, когда SST уместна, а когда лучше применять многопоточный вариант.
brag
Цитата(alexunder @ Sep 9 2016, 10:57) *
Вероятно, есть критерии, когда STT уместна, а когда лучше применять многопоточный вариант.

Естественно есть. Например пользовательские ОС, где пользователь может запускать свои или чужие приложения. И то, в нормальных ОС, типа FreeBSD для каждого процесса есть по сути свой SST - это kqueue. То есть приложения планируются в многопоточном стиле, а работу внутри приложений можно делать в однопоточном асинхронном, с приоритетами - вобщем полный SST. В линуксе (по крайней мере на сколько мне известно) есть костыль в виде epoll, через который и реализовано все в таких штуках, как Nginx или NodeJS.
NodeJS - это не SST, у него один приоритет, а у SST их много. Если в NodeJS где либо втулить тяжелый или вечный цикл - вся программа умрет, а если это сделать в SST - то будет продолжать работать, умрут только менее приоритетные задачи. SST - это продолжение NodeJS.
alexunder
Цитата(brag @ Sep 9 2016, 10:00) *
Естественно есть. Например пользовательские ОС, где пользователь может запускать свои или чужие приложения.

Нет ли у Вас ссылок на какую-нибудь литературу по SST (необязательно для embedded)?
brag
Сделал сервер сишный блокирующий многопоточний. Как и предполагалось, система умерла сразу, как только к серверу подключился nodejs клиент sm.gif
Так что это миф, когда говорят, что в многопоточной ОС криво написанное приложение не сможет повлиять на другие - очень даже может, и не просто повлиять, а завалить всю ОС намертво. Еще один плюс в пользу асинхронщины sm.gif
Сколько памяти заняло - не знаю, тк система умерла, пришлось жать reset, но думаю, что всю.
Что успел заметить - сервак успел обработать 1160 коннектов(это 1162 потока - 1160 клиентских + вотчер + главный поток, принимающий коннекты)
Код
char buffer[4096];
    int i,r;
    while(1){
        for(i=0; i<100; i++)buffer[i] = rand();
        r = send(sock, buffer, 100, 0);
        if(r<=0){
            perror("Thread_connection: socket error");
            break;
        }
        usleep(10000);
    };

Так что однопоточные приложения круче и проще многопоточных, даже на многопоточных осях, не говоря уже про чисто асинхронные, типа SST sm.gif

Цитата(alexunder @ Sep 9 2016, 11:16) *
Нет ли у Вас ссылок на какую-нибудь литературу по SST (необязательно для embedded)?

Да там то и ссылок особо быть не может. А то, что есть - в этом топике приводили, в том числе и на русском.
Тут нужно научиться мыслить асинхронно, без блокировок - перестроить свой мозг. Привыкнуть применять всякие замыкания, колбеки. А затем SST само собой получится.
Для тренировки отлично подходит замечательный инструмент - NodeJS, язык яваскрипт - синтаксис у него сишный, для C-программиста проблем не будет.

Для работи на МК в асинхронном стиле вообще ничего левого не нужно - нужен голый C++ и умение программировать. А тот, кто будет дергать ивенты там уже есть - это контроллер прерываний. Какие ще на МК могут быть ивенты, кроме прерываний?

В асинхронном программировании нужно стараться не использовать циклов вообще. А если использовать, то очень короткие.
Если вы видите, что у вас длинный цикл(больше, чем скажем 8 шагов для МК и около 100 для PC) - значит уже делаете что-то не так, особенно, если это операции ввода/вывода (тот же printf). В асинхронщине рулит рекурсия, а это уже шаги в сторону функционального программирования.
Речь идет не о сложных математических задачах, а о программировании в целом. Конечно, если умножение матриц или какой-нибудь криптографический алгоритм требует 100 000 циклов, то трогать их не нужно. Просто эту операцию нужно выполнять на другом уровне приоритета, чтобы не мешало остальным(ІО, GUI).
Сергей Борщ
Почерпнул много интересного из этого обсуждения. Прямо как будто зашел на электроникс лет 8 назад.

brag, не могли бы вы прокомментировать этот кусочек кода:
CODE
template<class T, int d_size=1> class delegate;

template<class TRet, class... TArgs, int d_size>
class delegate<TRet(TArgs...),d_size> {
public:
Как так? Сначала предварительно объявляем шаблон с двумя параметрами, а потом уже полное объявление с другим количеством? На всякий случай каюсь, до изучения шаблонов с переменным числом параметров я пока не дошел...
brag
Сергей Борщ, это называется Partial Template Specialization. http://en.cppreference.com/w/cpp/language/..._specialization
Сначала обьявляется шаблон с аргументами - тип и int. При чем последний имеет дефолтный параметр 1. Этот шаблон в данном случаи пустой.
Затем этот шаблон специализируем, то есть указываем компилятору, что будет, если пользователь захочет инстанциировать шаблон с конкретными аргументами - то есть это тип TRet(TArgs...) - функция, лямбда или функциональный обьект. второй - тот же инт.
Но сама специализация тоже шаблон - с аргументами, при чем переменного количества(эта фишка доступа только в C++11, в старых плюсах этого нет) sm.gif
Компилятор, увидев строки вида delegate<void(int,char)> подставит в TRet=void; Targs = int, char; size=1 (аргумент по умолчанию).

А если написать что-то типа delegate<int> или delegate<char, 2> - что тоже будет правильным, тк подходит под template<class T, int d_size=1> - тогда компилятор не найдет реализации шаблона(тк она у нас пустая) и со временем выдаст ошибку(или кучу ошибокsm.gif

Это так, на пальцах. Более подробно - в спецификации С++ и на выше приведенной мной ссылке. Ну и естественно - тренировка.

Вот еще один примерчик по-проще. Это шаблон динамической очереди исполняемых обьектов(в том числе функций и лямбда-функций). Туда можно пихать такие обьекты любого размера. В delegate тоже можно, только там делегат имеет фиксированный размер, и если обьект в него не влезет - компилятор выдаст ошибку.
В очереди уже память используется более эффективно - выделяется столько ячеек(32-битных слов), сколько нужно для обьекта и проверка происходит в рантайме(потому и динамическая).
Код
// Variable item size delegate FIFO **
template<class T> class VdelegateFIFO;

// main template
template<class ... TArgs>
class VdelegateFIFO<void(TArgs...)>{
sigmaN
Да, я тоже могу сказать, что узнал что-то новое из этой беседы.
Особенно меня удивил JS конечно же. Вот уж не ожидал так не ожидал ) Я JS использовал только в Qt, там он часть QML для создания интерфейсов.

В принципе реализация делегата более-менее стандартная. Нагугливается довольно легко.

У меня единственное остаются вопросы по SST конечно... постараюсь вкратце описать всё это.
Для начала давайте определимся, что любой объект с состоянием(для простоты рассматриваем класс в ООП) это так или иначе и есть конечный автомат. Методы класса в данном случае действуют как события приводящие или не приводящие к изменению состояния. Т.е. я хочу подчеркнуть, что в реальном мире у объекта много точек входа для изменения состояния.
Чтобы состояние менялось атомарно и состояние менялось строго в соответствии с графом(алгоритмом) при вызове методов из разных потоков(т.е. упростим до понятия "одновременно") вводятся мьютексы, сериализирующие изменение состояния. Т.е. делая эти изменения последовательными не смотря на то, что запросы на изменение пришли "одновременно".
В реальном мире в реальных системах состояний у объектов много и графы их изменений в масштабах системы довольно сложны, ребер(процесс смены состояния) у такого графа может быть довольно много.
Просто хочу чтобы мы это поняли.
Пеоеходим к SST:
Первое и самое главное ограничение это необходимость редизайна задачи(объекта, конечного автомата) таким образом, чтобы при выделении процессорного времени она быстренько run to completion. Следовательно мы должны проектировать наш объект(конечный атомат) таким образом, чтобы все изменения в состоянии происходили строго из одной точки входа(функции, которую дёргает SST планировщик). И обязательно принять условие, что только планировщик будет вызывать эту функцию. Обоснуем: извместно, что в любой момент выполнение может быть прервано другой задачей(более приоритетной, но это щас не важно) и не дай Бог та другая задача сделает что-то(вызовет метод), что изменит состояние первой задачи, которая находится в процессе смены состояния! Выполнение то было прервано на пол пути, где-то посередине ребра графа состояний конечного автомата. Бог знает в каком месте.
Я правильно мыслю? Не слишком блокировочно и многопоточно? wink.gif

Таким образом да, благодаря одному потоку мы уже имеем последовательное выполнение и якобы не нуждаемся в мьютексах, которые как раз и созданы для сериализации доступа.
Мы вроде как экономим память и проц т.к. не переключаем полный контекст. Безусловно ДА.
Обратная сторона медали:
Мы приходим к жесточайшим ограничениям межзадачного взаимодействия! Единственная точка входа в задачу заставит нас сообщения для этой задачи хранить в очереди. Реализация такой очереди требует унификации всех сообщений(событий) системы или подсистемы. Таким образом интерфейс задачи(объекта) превращается не в привычный набор методов, а в набор сообщений которые можно посылать объекту.
Из всего этого следуют требования очень вдумчивой расстановки приоритетов задач, что по дизайну заставит вас подпиливать задачи под систему, что нарушиает инкапсуляцию, увеличивает завязку частей системы друг на друга. Хотя-бы в части форматов этих очередей с сообщениями. В системе появляется много функторов(делегаты ваши) которые циркулируют туда-сюда...

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

По этим причинам SST это далеко не панацея, а хитрое название для давно и хорошо известного автоматного подхода https://ru.wikipedia.org/wiki/%D0%90%D0%B2%...%BD%D0%B8%D0%B5
способ сэкономить немного памяти/немного проца за счет немалых изменений в архитектуре системы.
Конечно же такой подход тоже является NP полным и позволит вам теоретически решить любую задачу.

Дискутировать тут можно только над целесообразностью такого преобразования системы. Можно рассуждать над побочными эффектами, над расширяемостью такой системы и т.д...
Как видим на примере современных ОС в реальном мире побеждает симбиоз потоков и разделения времени с событиями(асинхронный IO тому пример).
На чем наверно можно и сделать окончательный вывод )
brag
Цитата
Для начала давайте определимся, что любой объект с состоянием(для простоты рассматриваем класс в ООП) это так или иначе и есть конечный автомат.

Да. Тред, работающий на одноядерном процессоре - тоже конечный автомат, вернее не тред, а вся эта тредовая ОС sm.gif

Цитата
Чтобы состояние менялось атомарно и состояние менялось строго в соответствии с графом(алгоритмом) при вызове методов из разных потоков(т.е. упростим до понятия "одновременно") вводятся мьютексы, сериализирующие изменение состояния.

Потоков нет. Поток у нас один. Мютексов тоже ессно нет. Но есть приоритеты событий.

Цитата
Первое и самое главное ограничение это необходимость редизайна задачи(объекта, конечного автомата) таким образом, чтобы при выделении процессорного времени она быстренько run to completion. Следовательно мы должны проектировать наш объект(конечный атомат) таким образом, чтобы все изменения в состоянии происходили строго из одной точки входа(функции, которую дёргает SST планировщик). И обязательно принять условие, что только планировщик будет вызывать эту функцию.

Этим занимается компилятор. Я не могу сам вызвать функцию-обработчик - компилятор мне этого не позволит.
И это не ограничение, это другой стиль программирования, и очень классный! wink.gif

Цитата
Бог та другая задача сделает что-то(вызовет метод), что изменит состояние первой задачи, которая находится в процессе смены состояния! Выполнение то было прервано на пол пути, где-то посередине ребра графа состояний конечного автомата. Бог знает в каком месте.

Другим задачам запрещено менять состояние данной задачи. Любая коммуникация между задачами происходит через очереди(каналы, пайпы, fifo - называйте как хотите). Если задача А хочет изменить состояние задачи Б, тогда задача А ставит этот запрос в очередь, и как только задача Б будет готова принять эти изменения - она это сделает. Всем этим занимается не пользователь, а компилятор вместе с планировщиком.

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

У Вас для сериализации доступа используются тяжеленные мютексы и громадный стек, а у нас - легковесные быстрые очереди, довольно не редко lockfree.

Цитата
Таким образом интерфейс задачи(объекта) превращается не в привычный набор методов, а в набор сообщений которые можно посылать объекту.

Да, но это не ограничение, а другой стиль. Зачем сделали NodeJS, если есть PHP, Python и другие динамические языки. Разве ради того, чтобы загонять программистов в ограничения? Да на Javascripte такое можно сделать, что Php-шнику или Питонщику не снилось, и работать это будет очень быстро.

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

Расстановка приоритетов нужна как в асинхронной оси, так и в многопоточной синхронной. Да, можно конечно выдавать таймкванты длительностью 100мкс и не париться, но это будет жуткий тормознутый монстр с бешенным оверхедом и скорее всего такая система не будет адекватно реагировать на прерывания.
Нежели просто грамотно расставить приоритеты прерываний и пользовательских задач. Мало того, приоритет может меняться динамически(что у меня часто и происходит), но об этом позже, не будем пока лесть в дебри.
Да и большинство задач юзера имеют одинаковый приоритет. С приоритетами надо поработать только с системными задачами - та же шина SPI, Uart, Dma, итд. Ну и естественно - с прерываниями, они тоже могут друг друга прерывать(вытеснять) sm.gif У пользователя обычно всего 1-2-3 приоритета.

Цитата
В системе появляется много функторов(делегаты ваши) которые циркулируют туда-сюда...

Да, они там повсюду. Но это такой стиль и он очень хорошо себя зарекомендовал.

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

Часто готовый софт либо что-то делает одно(типа MP3, jpeg итд) и свободно ложится на SST. Либо имеет возможность работать в асинхронном неблокирующем стиле, хоть и через костыли (как, например Linux в отличии от FreeBSD). Но если софт жестко завязан на потоки и без них там никак - в мусорку этот софт. Например Апач - без PHP он бесполезный. А нафик PHP, если есть NodeJS? И нафиг тогда сервер(бесполезная прослойка), если на этом же Node такой сервер пишеться за 20 минут(на установку и настройку апача уйдет не меньше), а остальное время будет потрачено на бизнес-логику, которое могло быть потрачено на ту же логику на PHP? Значит втопку апач.
Точно так же и другой софт. Если там нет Async Nonblocking IO - дорога ему в мусорку, rm -rf * sm.gif

Цитата
По этим причинам SST это далеко не панацея, а хитрое название для давно и хорошо известного автоматного подхода https://ru.wikipedia.org/wiki/%D0%90%D0%B2%...%BD%D0%B8%D0%B5
способ сэкономить немного памяти/немного проца за счет немалых изменений в архитектуре системы.

Конечно это не панацея, и это автомат. Но довольно продвинутый, там кейсом не обойдешься, ибо простейшая задача будет иметь 100 и более кейсов sm.gif

Цитата
Как видим на примере современных ОС в реальном мире побеждает симбиоз потоков и разделения времени с событиями(асинхронный IO тому пример).
На чем наверно можно и сделать окончательный вывод )

Причина чисто историческая. Так исторически сложилось, что программирование и железо у нас императивное. Хотя есть более совершенные техники, например функциональное программирование, но они непонятны нынешним программистам.

Точно так же и с потоками - так исторически получилось, что ОС у нас многопоточные, навязывают нам такой стиль. Переписать то сами ОС можно запросто, только переписывать весь софт, который уже под них существует у человечества сил не хватит.
Поэтому выход один - допиливать в существующие ОС async IO, планировку процессов оставлять в sync-стиле, а сами приложения проектировать в async-стиле. Да любая нормальная софтина сейчас в async-стиле делается, тот же Qt, Node итд итп.

За всю историю, сколько я пишу на QT, я создавал треды только тогда, когда надо было в фоне непрерывно, на всем доступном процессорном времени что-то считать, например FFT. Остальное IO свободно работало в главном потоке(там, где и GUI) и система работала шустро. FFT-поток получал данные через очередь, никаких мютексов - подключил свой велосипед под названием SPSC lock-free queue. Благо, у STL для этого есть atomic-переменные, и у Интела есть их аппаратная поддержка.
И то этот поток - вынужденная мера, тк в QT нет приоритетов событий. Если бы был приоритет - я бы просто поставил FFT приоритет ниже, чем в GUI и все, был бы абсолютно тот же эффект.

Гляньте на язык Java, вернее на приложения, которые на нем написаны. Как они тормозят. На любом железе тормозят жутко. Потому что там тонны потоков(хоть ява и позволяет программировать asnyc, особенно сейчас). И посмотрите, как летает однопоточный асинхронный Javascript в Node sm.gif

Цитата
Конечно же такой подход тоже является NP полным и позволит вам теоретически решить любую задачу.

Асинхронный подход позволяет решить любую задачу(равно, как и синхронный). Только это решение в 90-95% случаев лучше, красивее, эффективнее, чем на потоках. Примеров тому масса, они повсюду.

Или вот тот же мой сервак, который отправляет пользователю рандом 100 раз в секунду. Асинхронное решение простое, натуральное, и отправляет действительно строго каждые 10мс.
CODE
var timer;
function on_socket_closed(){
n_connections--;
clearInterval(timer);
}

// Periodically send random data to client
timer = setInterval(function(){
var str;
for(var i=0; i<100; i++){
str+= String.fromCharCode(Math.random()*256);
}
socket.write(str);
}, 10);


В то время как синхронное решение горбатое, с жутким джиттером, тк сами вызываем функцию sleep непонятно когда, да еще и send может блокироваться.
CODE
char buffer[4096];
int i,r;
while(1){
for(i=0; i<100; i++)buffer[i] = rand();
r = send(sock, buffer, 100, 0);
if(r<=0){
perror("Thread_connection: socket error");
break;
}
usleep(10000);
};

Что бы было меньше джиттера, нам придется создать еще один поток - таймер, создать семафор, в таймере его устанавливать(инкрементировать) а в отправителе декрементировать. Жуть - 2 потока только ради того, чтобы периодически отправлять набор байт sad.gif
А что будет, если будет реальная задача? Когда таймеров понадобится штук 5? Даже представить сложно, как это все будет тормозить и глючить...
sigmaN
Кстати справедливости ради надо добавить, что идея именно посылать объекту сообщение идет еще со времен первых экспериментов с объектно ориентированным программированием.
Именно такая парадигма была принята в языке Smalltalk. Где-то, кажется у Фулера, читал что это типа тру ООП подход, позволяющий полностью инкапсулировать объект. Где-то там даже была критика С++, который жестко формализируя сигнатуру каждого метода как-бы выдает часть информации о себе, нарушая инкапсуляцию.

Из ныне живых(пока еще) языков, использующих для вызова метода именно посылку сообщения я могу припомнить Objective-C
Цитата
компилируемый объектно-ориентированный язык программирования, используемый корпорацией Apple, построенный на основе языка Си и парадигм Smalltalk. В частности, объектная модель построена в стиле Smalltalk — то есть объектам посылаются сообщения. Язык Objective-C является надмножеством языка Си, поэтому Си-код полностью понятен компилятору Objective-C.
https://ru.wikipedia.org/wiki/Objective-C

Когда-то немного баловался приложеньками для айфона.
Может кому будет интересно почитать как они умудрились сделать такую веселую надстройку над Си
http://steps3d.narod.ru/tutorials/objective-c-tutorial.html искать главу Как работает механизм сообщений

Но использовать SST и один стек в реальном проекте для проца мощнее AVR я бы всё равно сейчас не стал ))
brag
Smalltalk прикольная штука, но не прижилась. Но там нет очередей, просто абстракция другая, неявных интерфейсов. Хотя и в Go они тоже неявные.
Нам не нужно посылать каждому обьекту по первому чиху сообщение, тем более пихать его в очередь.
Немало кода(можно сказать - большинство) работает в обычном неблокирующем ОО стиле, как в Qt, например или том же JS. Сообщения начинаются там, где нужно работать с чем-то внешним по отношению к себе. Например, читать файл с флешки или температуру с DS18B20. Или тогда, когда надо делать тяжелые вычисления.

Цитата
Но использовать SST и один стек в реальном проекте для проца мощнее AVR я бы всё равно сейчас не стал ))

Применяю однопоточный подход начиная с AVR и заканчивая многоядерными серверами. На многоядерных, правда стеков(и потоков) столько, сколько и ядер, но это не важно. Планировщик сам раскидывает события по нужным потокам(ядрам). Для меня все выглядит, как 1 поток.
Конечно, систему с нуля писать под эти серверы я не собираюсь, есть замечательные ОС в которых все уже сделано, в том числе и SST, только более навороченный.
Но под МК от 8 до 400 мгц и DSP/DSC от 50 до 700мгц с лихвой хватает своей простейшей SST-ос. А часто, на относительно простых проектах, даже и SST не нужен - достаточно того функционала, который дает железо(проц и контроллер прерываний) - пользовательский код крутится в одном самом низком приоритете, а системный - на всех остальных более высоких приоритетах, у каждого проца это количество свое - от двух до 256 и выше.
Многозадачность может спокойно быть и без ОС вообще. При чем код будет даже проще, чем с ОС(тем же SST) - тк всего один приоритет. Этого достаточно для рисования GUI, обработки всевозможных кнопочек и тачскринов, одновременно с этим читать файлы с карт памяти, контролировать температуру, заряжать батарею и много чего еще без всяких замираний и тормозов, система летает, как самолет на довольно простом МК.
Я даже не представляю, какой МК понадобился бы, если решить в многопоточном стиле те задачи, которые я на них запросто решаю в асинхронном однопоточном - с одним единственным стеком. Наверное пришлось бы брать простой PC + линукс(или какой-нибудь андроид) и не париться, только рентабельность такой системы будет никакая, равно как и энергопотребление бешеное.

Да и код монстроидальный и глючный выходит, невозможно писать нормальный код на мютексах + shared memory, очень много времени требуется на отладку. На сложных проектах рано или поздно запутаешься, да и мютексы не гарантируют отсутствие гонок(зато приносят кучу проблем) - вы можете наложить локи не в той последовательности или не на те данные и все - поведение программы будет зависеть не от вас, а от случая.. А гонки очень трудно вылавливать, даже имея специальный для этого софт. я уже проходил через это.
Зато очереди дают гарантию, что все отработает так, как надо. Приходится их применять и в многопоточном стиле(вместо мютексов, хотя сами очереди могут быть построены на мютесках, а могут и нет. Но все равно эти мютексы локализованы в одном месте - в коде очереди, а не разбросаны по всему проекту.
А когда в многопоточном ПО появляются очереди - вы уже на пол пути к однопоточному стилю. Со временем там появятся асинхронные таймеры, Active-objectы с приоритами, async nonblocking IO в конце концов, и все - вы программируете асинхронно, пора выкидывать вашу RTOS в мусорку, как ненужную прослойку(типа апача) sm.gif
sigmaN
Цитата
все - вы программируете асинхронно, пора выкидывать вашу RTOS в мусорку, как ненужную прослойку
Вот что мне не нравится - это ваша фанатичность ) Видимо молоды еще. Или темперамент такой )
brag
Ок, с фанатичностью убавим wink.gif
Мне тоже лень было, но таки переборол себя и выкинул, теперь доволен, хотя по началу было сложно, но деваться некуда - код и так со временем ставал практически асинхронным, редко где оставались блокирующие вызовы. Но был очень горбатым тк работал под синхронной ОС - гибрид такой получался, да и опыта мало было, пришлось на JS поработать, чтобы набраться. Потом я удалил ОС, и в месте с ней десятки тысяч строк кода и начал новые проекты делать вообще без ОС, и строил велосипеды так, чтобы чувствовать себя как можно ближе к JS. На некоторых проектах такого подхода не хватало(а РТОС взять религия не позволила) и тут я вспомнил, что где-то читал про какой-то там SST не обратив на него особого внимания, тк работал на нормальных РТОС. Нашел, разобрался и понял, что это именно то, что мне нужно. Еще месяца 4 практики и получился крутой фреймворк, убивающий по производительности и простоте кода любую RTOS. Как убивает Node любую синхронную систему, как по простоте кода(сервак можно написать парой тройкой строк), так и по скорости и памяти, хотя JS довольно тяжелый и медленный язык.

Вот сейчас мне тоже лень изучать Rust, все плюсами довольствуюсь. Не могу решиться найти время и силы на это, но вижу, что придется, а то все таки плюсы - опасный язык, хотя безопасность повышается использованием высоко-уровневых библиотек(STL), но с ними трудно работать на слабых МК до 50мгц/8кб, надо что-то подпилвать, что-то выпиливать, а это я не люблю, проще самому сварганить велосипед на сотню строк за пол часа (собственно так и было сделано).
Нужен язык, где безопасность идет сразу из коробки и такой же нативный и быстрый, как плюсы, со всеми шаблонами, лямбдами и другими вкусностями.
psL
Цитата(brag @ Sep 8 2016, 20:22) *
1000 потоков так и не смог создать - линукс падает намертво, приходится ресет жать. Кое как создает 400 потоков и сжирает всю память, комп жутко тормозит и выдает еле 600 коннектов в секунду

Каждый поток в линукс создается со стеком ~8Мб по умолчанию, вот память и кончается. Можно изменить pthread_attr_setstacksize
Но лучше все же для тестирования использовать стандартные инструменты, например http://httpd.apache.org/docs/2.2/programs/ab.html
Цитата(brag @ Sep 8 2016, 22:21) *
Тут даже сравнивать нечего. Сервак на NodeJS убивает любой другой многопоточный, а тормознутого Апача десять раз убивает. Он может только конкурировать с серваком на Go, при одних задачах лучше NodeJS, при других - Go. Я работал и на том и на другом.

Видимо гуру не знает, что "Сервак на NodeJS" написан на основе libuv, например. А nodejs "в продакшене" прячут за тем же nginx...

А так, в принципе, можно договориться до того, чтобы запретить размещение обьектов в стеке средствами C++. Или использовать только реентерабельные функции и статические данные в С. Тогда никакого стека вообще не будет, и никакой кучи тоже.
sigmaN
Цитата
Видимо гуру не знает, что "Сервак на NodeJS" написан на основе libuv, например. А nodejs "в продакшене" прячут за тем же nginx...
Ахахахаа. Вот это поворот.
А я и думаю чё за шайтан Javascript такой быстрый, а! У меня в Qt он чуть что не так - тормозить начинает.
Во думаю ребята движок V8 до чего допилили! А оказывается эта вся шайтан-машина только патроны в Сишный пулемет заряжает ))))))
brag
Цитата
Каждый поток в линукс создается со стеком ~8Мб по умолчанию, вот память и кончается. Можно изменить pthread_attr_setstacksize

А потом в один прекрасный момент стека не хватит и Ваше приложение слетит. В однопоточном стиле такой опасности нет.
Просчитать стек невозможно, когда приложение сложное, с виртуальными функциями и рекурсией. Да и сколько стека не выделяй - все равно асинхронное будет в разы быстрее, и проще.
Кста посмотрел, стека у меня выделяeтся 2мб (ничего не менял). Итого получается, что 1000 потоков сожрали всю память и линукс сдох, помог только reset. Когда асинхронное приложение уперлось в какие-то лимиты или тормоза ядра и до полного использования ресурсов системы еще очень далеко, и при этом обрабатывала почти в 10 раз больше клиентов. Система нормально работала даже во время работы сервака и клиента, только сокет создать невозможно было. После отключения клиента все продолжало дальше работать без тормозов.

Цитата
Видимо гуру не знает, что "Сервак на NodeJS" написан на основе libuv, например. А nodejs "в продакшене" прячут за тем же nginx...

Конечно я не знаю что там внутри, меня это не интересует, главное - результат. А Nginx - асинхронный, поэтому не удивительно.

Цитата
А я и думаю чё за шайтан Javascript такой быстрый, а! У меня в Qt он чуть что не так - тормозить начинает.
Во думаю ребята движок V8 до чего допилили! А оказывается эта вся шайтан-машина только патроны в Сишный пулемет заряжает ))))))

Быстрый не JS, быстрая сама технология однопоточного асинхронного IO и программирования в целом. JS язык довольно медленный, для вычислений не годится.
Qt - асинхронный неблокирующий event-driven стиль. Будет где-то тяжелый цикл, блокирующий вызов или другой тупняк - будут жуткие тормоза.
Могу и на C сделать быстро асинхронно(и без libuv), скорее всего получится даже быстрее(или так же), чем Node. Тут рулит технология, а не язык или инструмент.

Цитата
А так, в принципе, можно договориться до того, чтобы запретить размещение обьектов в стеке средствами C++.

Зачем отказываться от такой быстрой и удобной штуки,как стек? Как раз в асинхронном стиле стек используется очень интенсивно.
Попробуйте в функциональном стиле попрограммировать, где все переменные - исключительно константы, где нет циклов(вообще), где компилятор может свободно перестанавливать порядок вызова функций сам, без Вашего ведома ради оптимизации,.... И скажите, что так можно договорится до программирования в машинном коде и вообще без стека, кучи и оперативки sm.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.