реклама на сайте
подробности

 
 
> Система, управляемая событиями и SST(super-simple tasker), Выделено из "ООП. Классы и динамические объекты"
brag
сообщение Sep 7 2016, 09:07
Сообщение #1


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Недавно я начал пользоваться динамической памятью. Раньше пользовался только статикой, то есть placement new. Со статикой сложно работать с такими шаблонами, как фабрика обьектов, временные задачи итд.
Программирую в основном под cortex-m3/m4
Код реализации аллокатора вышел довольно простым и быстрым, критически секции есть, но очень короткие(там прочитать/записать пару байт).
При чем я не использую этих всяких монстров под названием RTOS, у меня свой простейший асинхронный планировщик задач, занимает строк 300 кода с комментариями(погуглите запрос "super simple tasker", поймете что это).
В купе с динамическим аллокатором, variable element-size queue, новыми фишками C++ (шаблоны, лямбда-функции, auto) получается очень простой, безопасный и мощный фреймворк.
Вспоминаю работу с классическими Thread-ами(с ихними стеками - куча кода было написано для анализа программ на предмет нужного обьема стека, и теперь эта куча кода отправлена на мусорку) со всеми этими тяжеленными мютексами, семафорами, вечными циклами как страшный сон.

Если кому интересно - расскажу и покажу как это все работает.
Go to the top of the page
 
+Quote Post
17 страниц V   1 2 3 > »   
Start new topic
Ответов (1 - 99)
sigmaN
сообщение Sep 7 2016, 10:07
Сообщение #2


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Ну так извините меня, классические, как вы говорите, Thread-ы и ваш планировщик задач это мягко говоря разные вещи!
Да, конечно, есть ряд применений где такой подход достаточен.

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

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

Ну а если у вас нормальное переключение контекста и всё что я написал не актульно то поздравляю - вы изобрели потоки. Скоро в этой системе появятся мьютексы и семафоры )


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
Serhiy_UA
сообщение Sep 7 2016, 10:24
Сообщение #3


Знающий
****

Группа: Свой
Сообщений: 721
Регистрация: 23-10-08
Из: next to Odessa
Пользователь №: 41 112



Цитата(brag @ Sep 7 2016, 12:07) *
Не очень недавно я начал пользоваться динамической памятью.
.......
Если кому интересно - расскажу и покажу как это все работает.

А какая у Вас стратегия управления кучей, хотел иметь представление о следующем:
1. Как ведется учет занятой и свободной памяти? Как ищется для объекта подходящий по размеру участок памяти?
2. Как в куче объединяются соседние пустые участки для увеличения непрерывного пространства под размещение следующих объектов? В какие моменты, и при каких условиях эти действия выполняются?

А фраза «Не очень недавно», это получается как бы «давно»?

Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 11:56
Сообщение #4


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

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

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

В этом нет никакой необходимости.

Цитата
Представим что в системе есть задачи А Б и В. Задача А ресурсоемкая и долго считает, задача Б должна ожидать результатов. А задачу В надо крутить постоянно и независимо от А и Б.
С применением нормальных потоков вы можете синхронизировать задачи А и Б только в тех местах где это требуется остальные же живут своей жизнью(и возможно иногда синхронизируются там друг с другом).

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

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

Да ничего подпиливать не нужно:
Код
enum { TASK_V_PRIORITY = 1, TASK_A_PRIORITY = 2, TASK_B_PRIORITY = 3};
void task_V(){
    while(true){
      // do some heavy work
    }
}
void task_A(){
//  do work
  return;
}
void task_B(){
//  do work
  return;
}

SST::runTask(task_V, TASK_V_PRIORITY);

void on_event_a(){
   SST::runTask(task_A, TASK_A_PRIORITY);
}
void on_event_b(){
   SST::runTask(task_B, TASK_B_PRIORITY);
}


Цитата
Ну а если у вас нормальное переключение контекста и всё что я написал не актульно то поздравляю - вы изобрели потоки. Скоро в этой системе появятся мьютексы и семафоры )

Да, это полноценные потоки, зато без выделения стека, инверсии приоритетов, livelock-ов, и кучи других проблем. Тут идеология и техника другая.
Мютексы и семафоры не появятся. Зачем они нужны? Тут всем рулят очередя. В отличии от тредовой архитектуры тут нет вышеперечисленных проблем. А переполнение очереди легко отлавливается на программном уровне, при чем оно может произойти даже прозрачно для пользователя. В то время, как переполнение стека приводит к падению всей системы, а глюки с мютексами вообще очень трудно находимые, целый год может отработать нормально и в один прекрасный момент глюканет, потом еще год будете моделировать ситуацию искать баг. Потом со временем будете вынуждены написать свой анализатор гонок(как в свое время делал я, потому что Relacy не детектирует даже самую простую реальную ситуацию https://github.com/dvyukov/relacy/issues/3 ) и анализатор стека(а что если рекурсия, что если куча виртуальных функций, порядок и уровень вложения которых на этапе компиляции неизвестен?)

Цитата
А фраза «Не очень недавно», это получается как бы «давно»?

Извиняюсь, имелсь в виду недавно. исправил.

Цитата
А какая у Вас стратегия управления кучей, хотел иметь представление о следующем:
1. Как ведется учет занятой и свободной памяти?

Через linked-list. служебное поле занимает одно 32-битное слово, это и есть оверхед, все остальное - полезные данные.
Каждый блок указывает на предыдущий и следующий. Для удаления блока достаточно сходить не предыдущий и следующий, и если они(один из них) пустые - обьеденить с удаляемым.

Цитата
Как ищется для объекта подходящий по размеру участок памяти?

Разные алгоритмы пробовал, но для моих задач лучше всего работает first-fit, фрагментация на одинаковом уровне, а добавление гораздо быстрее - добавление происходит в последний освобожденный(обьедененный) блок.

Цитата
2. Как в куче объединяются соседние пустые участки для увеличения непрерывного пространства под размещение следующих объектов? В какие моменты, и при каких условиях эти действия выполняются?

Ну уже написал выше от части, дополню:
1. идем в предыдущий блок, если пустой - увеличиваем его размер на N+1 слов. Где N - размер удаляемого блока.
2. идем в следующий блок. если он пустой - N=размер этого блока, повторяем процедуру 1. для предыдущего и выходим. Если не пустой - меняем указатель prev на блок, который получился в п1. все.
Физически блок выглядит вот так:
|prev_offset_word[14 bits], reserved[2 bits], size_words[14 bits], mask[2 bits]; uint32_t data[size_words]|


В самом аллокаторе ничего сложного нет. Doubly linked list на основе 14-битных "ссылок". Этого хватит, чтобы покрыть 64кб памяти - для большинства задач этого достаточно.
Фит-алгоритм возможно придется подобрать, который больше понравится.
1. best-fit (ищем блок минимального размера) - проблема - остаются очень мелкие бесполезные фрагменты. нужно искать блок - тормоза.
2. first-fit (добавляем в первый попавшийся блок, обычно тот, который недавно очистили) - мне больше всего нравится - во первых, быстро, во вторых, по моей статистике меньше всего фрагментации.
3. worst-fit (добавляем в блок самого большого размера) - тоже неплохо работает, но надо искать блок.

Блокировок прерываний нет, тк аллокатор не используется в прерываниях и других высоко-приоритетных задачах sm.gif У меня всего 32 приоритета юзера + еще 16-256 приоритетов прерываний(зависит от конкретного процессора). Задачи и прерывания более высокого приоритета всегда вытесняют(прерывают) задачи более низкого.
Обычно аллокатор нужен для задач с приоритетом 0,1,2 - это всякого рода UI и другой медленный ввод-вывод. Тогда ставим приоритет аллокатору 4 и все, никаких блокировок не нужно - аллокатор всегда будет вытеснять задачи, которые его используют.
Go to the top of the page
 
+Quote Post
Serhiy_UA
сообщение Sep 7 2016, 12:16
Сообщение #5


Знающий
****

Группа: Свой
Сообщений: 721
Регистрация: 23-10-08
Из: next to Odessa
Пользователь №: 41 112



brag, Спасибо за пояснения!

Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 7 2016, 12:17
Сообщение #6


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(brag @ Sep 7 2016, 14:07) *
Если кому интересно - расскажу и покажу как это все работает.

Конечно интересно!


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
arhiv6
сообщение Sep 7 2016, 12:18
Сообщение #7


Знающий
****

Группа: Свой
Сообщений: 633
Регистрация: 21-05-10
Из: Томск
Пользователь №: 57 423



Цитата(brag)
Если кому интересно - расскажу и покажу как это все работает.

Интересно. Где можно посмотреть код, примеры?


--------------------
Go to the top of the page
 
+Quote Post
Kabdim
сообщение Sep 7 2016, 12:36
Сообщение #8


Знающий
****

Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842



Цитата(brag @ Sep 7 2016, 12:07) *
Если кому интересно - расскажу и покажу как это все работает.

Присоединюсь к интересующимся. Если я правильно понял то для более низкоприоритетной задачи высокоприоритетная по сути является прерыванием? И соответственно равноприоритетные задачи не предусмотрены?
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 12:47
Сообщение #9


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Присоединюсь к интересующимся. Если я правильно понял то для более низкоприоритетной задачи высокоприоритетная по сути является прерыванием?

Да, совершенно верно.

Цитата
И соответственно равноприоритетные задачи не предусмотрены?

Конечно предусмотрены! И они выполняются в порядке очереди. Так же, как и прерывания. Закончилась одна задача - начинается выполнение другой. Или закончилось одно прерывание(а второе висело в пендинге) - начинается выполнение другого.

В дополнение - я считаю вечные циклы и мютексы - каменным веком. Необходимость мютексов возникает из за вечных циклов. Нет вечных циклов - нет и мютексов.
Зачем нужны вечные циклы? Какая их практическая цель? Везде, где я их встречаю - их запросто можно оттуда выкинуть. Зачастую это код вида:
Код
void Thread1(){
    while(true){
        if(must_i_exit)break;
        if(is_user_clicked_helo_button){
             messageBox("Hi dear user");
        }
    }
}
void Thread2(){
    while(true){
        if(must_i_exit)break;
        if(mp3_frame_ready){
             decodeMp3Frame(frame);
        }
   }
}

То есть и за вечных циклов наm понадобятся тяжеленные мютексы, семафоры - работа с которыми зачастую приводит к блокировке всех прерываний внутри кода самих семафоров; 2 довольно больших стека, заведомо неизвестного и трудно просчитываемого размера. И так под каждый тред - по сути под обычную функцию!

Теперь давайте попробуем откажемся от этих циклов:
Код
void on_helo_button_clicked(){
    messageBox("Hi dear user");
}

void on_mp3_frame_received(const Mp3Frame* frame){
    decodeMp3Frame(frame);
}

Код стал даже проще, теперь нам нужен один стек, теперь нам не нужны мютексы и блокировки. Мало того, сами функции, выполняющие работу остались прежние.

Что происходит при блокировке:
1. блокируются, как минимум все потоки. и как максимум - все прерывания
2. сохраняются все регистры в стеке(а это 16 4-байтовых слов для кортекса без FPU)
3. производится поиск готового к выполнению потока. очень тяжелая задача, особенно при наследовании приоритетов.
4. восстанавливаются из стека все регистры.
5. разблокируется все обратно

Теперь что происходит при добавлении задачи в очередь:
1. блокируется все потоки и прерывания, которые могут работать с этой очередью. на кортексе это одна инструкция.
2. резервируется в очереди место под задачу. Фактически данные задачи состоят из аргументов функции, то есть там пара-тройка 4-байтовых слов. В отличии от сохранения контекста, где их как минимум 16, а на практике больше.
Физически резервирование места это чтение двух указателей, простое условие, затем инкремент одного из них и запись в память. Это несколько простых инструкций.
3. блок снимается.
4. записывается в зарезервированное место данные задачи(наши аргументы функции). При этом все прерывания и все задачи продолжают работу
5. опять блокируемся
6. помечается зарезервированный участок очереди, как валидный - пара инструкций
7. снимаем блок
Все. И того операция очень простая и понятная, мало того, любое переполнение очереди нормально отлавливается программно и зачастую это recoverable error - прозрачный для пользователя, в то время как переполнение стека - это труба.
Код такой очереди занимает 240 строк вместе с комментами и C++ными приколами. Сколько строк исполняемого кода занимает мютекс и переключение контекста в какой-нибудь FreeRTOS sm.gif?

Цитата
Интересно. Где можно посмотреть код, примеры?

Ну часть я уже показал. Если это все поняли - готовлю следующую. Чтобы понять почему работает аллокатор, надо разобраться как работают задачи и очереди.
Пока задавайте вопросы, если есть.

Выше я показал почему не нужны треды. А вот как можно без них обойтись - рассказано в этом материале http://www.embedded.com/design/prototyping...r-Simple-Tasker
Я конечно пошел дальше, сделал некоторые моменты более оптимально с учетом конкретной архитектуры(архитектур) и реализовал все на c++ чтобы было просто и удобно и безопасно пользоваться. Об этом позже.
Go to the top of the page
 
+Quote Post
AlexandrY
сообщение Sep 7 2016, 13:48
Сообщение #10


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



Цитата(brag @ Sep 7 2016, 15:47) *
Я конечно пошел дальше, сделал некоторые моменты более оптимально с учетом конкретной архитектуры(архитектур) и реализовал все на c++ чтобы было просто и удобно и безопасно пользоваться. Об этом позже.


Здесь с завидной регулярностью находятся первооткрыватели метода конечных автоматов Miro Samek-а.
Но индустрия уже давно проехала этот этап.
Вытесняющая асинхронная многозадачность сейчас даже в Arduino применяется.
Никто не пишет софт годный под модель SST.
Приняв SST вы останетесь голым, даже FatFS или lwIP не сможете портировать.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 13:57
Сообщение #11


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Вытесняющая асинхронная многозадачность сейчас даже в Arduino применяется.

SST и есть вытесняющая асинхронная многозадачность. Точно такая же, как и есть в мире PC, под названием Active Object паттерн итд. Где физически создается тред под конкретную задачу, отрабатывает и выходит.
Или взглянуть на NodeJS/javascript - там хоть и один поток, но все асинхронно без блокировок. Или ту же Qt. Мир потихоньку, вернее очень даже резво, движется в сторону неблокирующей модели.

Цитата
Никто не пишет софт годный под модель SST.

Готовый софт мы не используем, мы пишем софт сами. Вот библиотеки - другое дело. Но последние редко зависимы от тредовой модели.

Цитата
Приняв SST вы останетесь голым, даже FatFS или lwIP не сможете портировать.

Портируем без проблем, при необходимости. Что есть такого в lwIP, что не ляжет на SST-модель? У меня давно написан свой fat и простейший ip-стек, который вообще не требует никакой ОС, то есть может работать на любой. Если нужно будет что-то готовое по-сложнее - запросто портируем на SST-модель. Тем более, на сколько я знаю, lwIp работает и в standalone, вообще без ОС.
Минусов я у нее не вижу, а вот очевидных плюсов достаточно. Перешел на нее я не так давно, и не по причине быстродействия, а по причине того, что сложный код под тредовую блокирующую модель рано или поздно либо ставал мертвым и глючным, либо сам превращался в active-object и другие асинхронные паттерны, появлялись очереди(каналы, как во всех модерн-языках типа Go) то есть все превращалось в SST. Треды были нужны только ради возможности работы этого всего. Создание запуск и удаление треда сопровождались огромными затратами ресурсов и блокировками. Потом я их выкинул за ненадобностью и заменил на простой и быстрый SST, где создание задачи занимает пару десятков инструкций sm.gif
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 7 2016, 15:50
Сообщение #12


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Очень интересно. Видимо этот SST прошел мимо меня... Надо будет почитать..
Add: Прочел http://www.embedded.com/design/prototyping...r-Simple-Tasker
Вселье закончилось тем, что все были загнаны в угол принудительным limiting all intertask communication to events после чего было замечено, что выполняющаяся задача блокирует все задачи ниже себя приоритетом и гордо были изобретены и мьютекс и критические секции )
Цитата
Recall that the SST scheduler can only launch tasks with priorities higher than the initial priority with which the scheduler was entered. This means that temporarily increasing the current SST priority SST_currPrio_ blocks any tasks with priorities lower than this priority "ceiling." This is exactly what a priority-ceiling mutex is supposed to do. Listing 5 shows the SST implementation.



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



--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 16:20
Сообщение #13


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Все, что мы теряем отказываясь от классических тредов - это тайм-кванты - то есть переключение равно-приоритетных потоков по таймеру. Но где такое нужно в embedded? Даже в PC это зачастую не нужно. Это нужно во всяких линуксах и виндах, чтобы криво написанная программа не тормозила остальных в своем уровне приоритета.
Но даже если такое понадобится, например когда есть 2 тяжеленных алгоритма, например скажем mp3 и jpeg и надо чтобы они оба работали "одновременно", тогда и это не сложно решить на SST. Стек конечно придется выделить для этих двух задач и сделать простую переключалку по таймеру, но работать с остальными компонентами системы они запросто смогут через очереди - без всяких мютексов. И остальная система будет работать на едином стеке.
Но это в теории. На практике же любая работа с данными подразумевает деление на фреймы - получение и обработка фреймов по отдельности - Run to completion, а значит и необходимость в тайм-квант-планировщике практически отпадает.
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 7 2016, 16:43
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



Что то не пойму, речь о кооперативной "оси"?
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 7 2016, 17:04
Сообщение #15


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Никто же не мешает организовать обмен ивентами и в классической релизации потоков.

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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 17:14
Сообщение #16


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Что то не пойму, речь о кооперативной "оси"?

Нет, вытесняющей

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

Только зачем все так сложно, если можно гораздо проще и с гораздо меньшими затратами ресурсов? Зачем проверять, поступил или нет? Зачем писать if(постипило) значит тчо-то делаем, если можно написать - при поступлении что-то делаем?

Цитата
P.S. Никто не говорил, что многопоточное программирование это легко и просто. Но этот ваш SST больше отбирает чем дает.

Дает он очень много - это простота кода, скорость и память. А что отбирает? То, чем и так не пользуемся?
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 7 2016, 18:37
Сообщение #17


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

Например мне кажется, что применяя SST будет сложнее обеспечить риалтаймовость конкретной задаче т.к. даже принципиальная возможность запуска задачи в конкретный момент времени зависит от того что в данный момент времени запущено, какой высоты этот бутерброд и как скоро он размотается до того момента, чтобы позволить конкретной задаче запуститься. Да да, сейчас вы предложите сделтаь этой задаче высокий приоритет и дескаать она асинхронно запустится. Всё это прекрасно до того момента пока таких задач мало)

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
arhiv6
сообщение Sep 7 2016, 19:01
Сообщение #18


Знающий
****

Группа: Свой
Сообщений: 633
Регистрация: 21-05-10
Из: Томск
Пользователь №: 57 423



Цитата(DASM)
Что то не пойму, речь о кооперативной "оси"?

SST - всё-таки вытесняющая (по крайней мере более приоритетные задачи могут вытеснять менее приоритетные).
http://embedders.org/blog/teap0t/miro-same...ch-perevod.html


--------------------
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 7 2016, 19:37
Сообщение #19


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



Спасибо, интересно, почитал.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 20:04
Сообщение #20


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

Да, тайм-квантов прямо в SST нет, но их можно получить свободно поверх SST. Джиттер будет и там и там.
К стати в SST фактически джиттер приоритетных задач меньше - прерывания блокируются на очень короткое время(часто на 2-3 инструкции), а часто да еще и на заточенной под это архитектуре - вообще не блокируются(lock free).
Но где пример задачи, где реально нужны тайм-кванты?
Я думаю, они нужны в сложных системах, где пользователь запускает приложения, типа линукса, где есть процессы. Да и планировщики там далеко не тривиальные. А в прикладных программах и на мк до 200мгц и DSP я им применения не вижу.
Зато там отлично работает SST, повышает скорость разработке и снижает себестоимость проекта, а значит повышает рентабельность проекта. Там, где для тредов нужен мк на 200мгц и 128кб оперативки, я с SST и C++ обхожусь 72мгц и 16-32кб оперативки.

Цитата
Поэтому сравнивать нужно совокупность свойств(качеств) обеих систем и делать вывод о применимости в конкретном случае.

Вот хотелось бы такой конкретный случай увидеть. Не даите конкретный примерчик, где SST неуместно/сложно/плохо?
Лично я вижу обратное - там, где все легко и просто ложится на SST(события), при чем зачастую на один поток - вешают несколько тредов, при чем 90% времени они висят в блокировке и чего-то ждут.
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 7 2016, 20:43
Сообщение #21


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



Ну я бы не стал столь восторженно писать, не могут 99% людей сидящих на традиционных РТОС быть идиотами. Но определенный резон тут есть, не спорю.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 7 2016, 21:32
Сообщение #22


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата(DASM @ Sep 7 2016, 23:43) *
Ну я бы не стал столь восторженно писать, не могут 99% людей сидящих на традиционных РТОС быть идиотами. Но определенный резон тут есть, не спорю.

Я ниразу не назвал их идиотами. Выбор РТОС обусловлен не только самими потоками, но и зависимостями от других проектов и библиотек.
Я лишь показываю принцип. Сколько вижу примеров и реального использования тредов - везде одно и то же - куча сложного ненужного кода и памяти. И зачастую простейшая задача, которая ложиться не то что на SST, а даже на один поток в event-driven стиле реализована как минимум в 3-4х потоках с вытекающими отсюда проблемами.

Да и PC мир уже давно переходит на event-ы.
Как раньше выглядел Proxy-сервер? - Основной процесс в вечном цикле ждал входящее соединение, и для каждого соединения создавал отдельный процесс(даже не поток), и в каждом таком процессе создавалось по 2 потока - один ожидал данные от пользователя и передавал дальше, второй ожидал от удаленной стороны и передавал пользователю. И того на каждый коннект по 3 тяжелых потока, один из которых супер-тяжелый. Завалить такой сервак DDOS-атакой нет проблемы - память и вычислительные ресурсы быстро кончатся.

А теперь это выглядит гораздо проще - событие входящего коннекта(void on_connected(...) ) создает обьект, который в свою очередь подключается к удаленной стороне и имеет 2 обработчика событий - on_data_received_from_client и on_data_received_from_remote. Код стал в разы короче, работает в одном потоке и требует копеечные затраты памяти и проца, и способен обрабатывать на том же железе в разы(десятки раз, а иногда и сотни) больше запросов.
Мало того - события будут выполнятся тру-параллельно - планировщик сам будет раскидывать события по ядрам(на многоядерном процессоре)/процессорам.

Конечно тут есть свой порог вхождения. Дело в том, что так исторически сложилось, что мозг программиста работает в императивном стиле sm.gif Ему блокирующий код понятный, он(мозг) его может шаг за шагом выполнять. Чтобы начать думать событиями нужно перестраивать свой моск, привыкать, учить другие языки, где трудно работать иначе(например Javascipt-NodeJS).
Вообще сейчас понемногу тенденция переходит с императива на другие стили - декларативное и функциональное программирование.
Вот сейчас посмотрел на текущий свой код - в классе практически нет функций и конструктор пустой, зато инициализируется куча обьектов и переменных - медленно но уверенно движемся в декларативный стиль, сами этого не осознавая...

Куча кода вот так выглядит:
Код
// Define DF task
    class DfTask{
    public:
        DfTask(Df* df,
            DfBuffer* buffer,
            int len,
            const delegate<void()>& cbk,
            SST::TPriority cbk_priority):
                df(df),
                buffer(buffer),
                cbk(cbk),
                len(len),
                cbk_priority(cbk_priority)
        {}
    private:
        Df* const df;
        DfBuffer* const buffer;
        delegate<void()> cbk;
        const uint16_t len;
        SST::TPriority cbk_priority;
    };

Ни единой функции - одни переменные. Тело функции-конструктора - пустое!
Программы строим не из императивного кода, а из блоков - кубиков sm.gif
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 00:18
Сообщение #23


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Цитата
Да и PC мир уже давно переходит на event-ы....
А теперь это выглядит гораздо проще - событие входящего коннекта(void on_connected(...) ) создает обьект, который в свою очередь подключается к удаленной стороне и имеет 2 обработчика событий - on_data_received_from_client и on_data_received_from_remote.
Ну давайте не будем путать всё в кучу. Это решение чисто архитектурное. Инкапсулировать всё в объект и наружу вывести события. Это не коим образом не означает, что внутри этот объект не работает в двух потоках wink.gif


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 8 2016, 00:52
Сообщение #24


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



а что такое "императивный код"? https://ru.wiktionary.org/wiki/%D0%B8%D0%BC...%BD%D1%8B%D0%B9
Go to the top of the page
 
+Quote Post
arhiv6
сообщение Sep 8 2016, 02:23
Сообщение #25


Знающий
****

Группа: Свой
Сообщений: 633
Регистрация: 21-05-10
Из: Томск
Пользователь №: 57 423



Императивное программирование

Сообщение отредактировал arhiv6 - Sep 8 2016, 02:24


--------------------
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 8 2016, 02:37
Сообщение #26


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



"В этом случае программа представляет собой формальную теорию, а её выполнение является одновременно автоматическим доказательством этой теории, и характерные для императивного программирования составляющие процесса разработки (проектирование, рефакторинг, отладка и др.) в этом случае исключаются: программа проектирует и доказывает сама себя." - а это как ? wacko.gif " Как следствие, декларативные программы не используют понятия состояния, то есть не содержат переменных и операторов присваивания (см. также ссылочная прозрачность)." ... эээ как я отстал...
Go to the top of the page
 
+Quote Post
ViKo
сообщение Sep 8 2016, 04:35
Сообщение #27


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Допустим, надо стереть flash-память, что длится до 10 секунд. По теории SST для этого случая (события!) нужно создать свою задачу. Пропади пропадом такая ОС.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 07:03
Сообщение #28


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Ну почему же обязательно создавать задачу. Стирание флэша может быть ветвлением одной из существующих задач.... Т.е. аргумент как-бы не очень объективный )


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
ViKo
сообщение Sep 8 2016, 07:17
Сообщение #29


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Ветвление, это что? После стирания нужно дальше работать, писать и т. д.
Да, я не понял глубины идеи, кроме того, что все задачи работают до конца.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 07:27
Сообщение #30


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Ну давайте не будем путать всё в кучу. Это решение чисто архитектурное. Инкапсулировать всё в объект и наружу вывести события. Это не коим образом не означает, что внутри этот объект не работает в двух потоках wink.gif

Нет, физически там один поток и один стек.

Цитата
Допустим, надо стереть flash-память, что длится до 10 секунд. По теории SST для этого случая (события!) нужно создать свою задачу. Пропади пропадом такая ОС.

Если Вы не поняли как это работает и как этим пользоваться, то это не значит, что это работает плохо или этим неудобно пользоваться.
Цитата
Ну почему же обязательно создавать задачу. Стирание флэша может быть ветвлением одной из существующих задач.... Т.е. аргумент как-бы не очень объективный )

Для стирания флеша(физически) надо всего лишь:
1. Передать несколько байт через SPI (команда стирания, возможно команда разблокировки - RWEN) и слесть с шины.
2. Через некоторое время(по таймеру) опять занять шину и проверить -стерлось или нет. Если стерлось - все, готово. Если нет - опять запустить таймер.
Но это если на шине только одна флешка. А если там еще что-то - понадобится задача. В принципе ничем не сложнее первого варианта, только действия выполняем не сразу, а в теле задачи. И когда шина освободится выполнится тело задачи и флешка будет стерта.

Все эти действия выполняет не пользователь, а класс,работающий с флешкой.
Вот как это выглядит в реально работающем проекте, только принты переписал на русские, чтоб было понетнее:
Код
    // Запускаем стирание флешки
    df->MassErase([this](){
        // получаем уведомление, что флешка стерта. Этот код выполнится аж через 10 секунд после запуска стирания.
        printf("Теперь флешка чистая.\n");
    });
    printf("Стирание флешки запущено, обратно пути нет\n");

Сильно сложно?

Цитата
" ... эээ как я отстал...

Почитайте еще про функциональное программирование sm.gif
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 07:31
Сообщение #31


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Цитата
Нет, физически там один поток и один стек.
Хотелось бы посмотреть исходники такого сервера. Это в опэнсоурсе есть?


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 07:38
Сообщение #32


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата(sigmaN @ Sep 8 2016, 10:31) *
Хотелось бы посмотреть исходники такого сервера. Это в опэнсоурсе есть?

Конечо. При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной моделиsm.gif
https://nodejs.org/dist/latest-v4.x/docs/api/http.html
CODE
const http = require('http');
const net = require('net');
const url = require('url');

// Create an HTTP tunneling proxy
var proxy = http.createServer( (req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('okay');
});
proxy.on('connect', (req, cltSocket, head) => {
// connect to an origin server
var srvUrl = url.parse(`http://${req.url}`);
var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, () => {
cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node.js-Proxy\r\n' +
'\r\n');
srvSocket.write(head);
srvSocket.pipe(cltSocket);
cltSocket.pipe(srvSocket);
});
});

// now that proxy is running
proxy.listen(1337, '127.0.0.1', () => {

// make a request to a tunneling proxy
var options = {
port: 1337,
hostname: '127.0.0.1',
method: 'CONNECT',
path: 'www.google.com:80'
};

var req = http.request(options);
req.end();

req.on('connect', (res, socket, head) => {
console.log('got connected!');

// make a request over an HTTP tunnel
socket.write('GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n');
socket.on('data', (chunk) => {
console.log(chunk.toString());
});
socket.on('end', () => {
proxy.close();
});
});
});

При чем это еще довольно сложный код, но надеюсь будет понятно как он работает. Если я приведу более простой - то без опыта разработки в подобном стиле точно не поймете
Go to the top of the page
 
+Quote Post
ViKo
сообщение Sep 8 2016, 07:39
Сообщение #33


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Слезть с шины, и что делать? Висеть в задаче без дела, и сам не гам и никому не дам? Или отдать управление другой, пусть менее приоритетной, задаче? Как?
Go to the top of the page
 
+Quote Post
501-q
сообщение Sep 8 2016, 07:55
Сообщение #34


Участник
*

Группа: Участник
Сообщений: 38
Регистрация: 24-02-09
Из: Екатеринбург
Пользователь №: 45 296



Приветствую!

Модель SST хорошо работает до того момента, когда с одним и тем же ресурсом нужно работать из потоков с разным приоритетом.

Реальный пример: на шине висят пультик и второй МК, связь с которым должна завершиться в течении 300 мкс (каждые 3 мс). Или объединять логику пультика (которая в разных проектах одна и та же) с логикой связи со вторым МК, или разносить по разным потокам с их синхронизацией. Во втором случае при добавлении абонента на шину первые два потока вообще менять не надо. А SST?

Илья
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 08:16
Сообщение #35


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата(ViKo @ Sep 8 2016, 10:39) *
Слезть с шины, и что делать? Висеть в задаче без дела, и сам не гам и никому не дам? Или отдать управление другой, пусть менее приоритетной, задаче? Как?

Слесть - значит сказать драйверу, что мы с шины слезли. У нас не должна голова болеть что там с задачами и что будет дальше - этим занимается планировщик и частично - компилятор.
Приведу полный код самой процедуры(если ее можно так назвать) стирания.
Он немного сложный, поскольку не только шина может быть занята в это время, но и сама флешка. Даже наоборот - шина может быть свободна, а Flash-микросхема в это время будет выполнять внутри себя операцию стирания, а любую другую команду в лучшем случаи отвергнет, а в худшем - заглючит.
То есть нам нужны 2 задачи - первая, которая выполнится когда флешка освободится, а вторая - когда шина.
CODE
// функция занятия шины
void Df::setCallback_and_CS(const delegate<void()>& cbk){
// устанавливаем обработчик прерывания DMA
Dma::setChannel_onXfrc(spi->dmaChannelRx(), cbk);
// устанавливаем chip select
DFCS_n_pin::clear();
}

// ----- Mass Erase ------
// Описываем задачу
class DfMassEraseTask{
public:
DfMassEraseTask(Df* df,
const delegate<void()>& cbk,
SST::TPriority cbk_priority):
df(df),
cbk(cbk),
cbk_priority(cbk_priority)
{}

// сама функция задачи. будет выполнена, как только Flash станет свободна
void operator()(){ // DF_TASK_PRIORITY
// Создаем задачу и добавляем в очередь шины SPI
bool r = df->spi->enqueueTask([this](){ // SSP_TASK_PRIORITY
// занимаем шину
df->setCallback_and_CS([this](){ // Handler mode
// команда WREN успешно подана, временно снимаем chip-select (так требует документция на Flash)
DFCS_n_pin::set();
__nop();
__nop();

// опять устанавливаем CS
df->setCallback_and_CS([this](){
// команда ERASE закончилась, слазим с шины
DFCS_n_pin::set();
// сигнализируем драйвер шины, что мы с шины слезли. После этого шина может быть испльзована кем-то другим
df->spi->taskCompleted();

// Запускаем таймер, который время от времени будет проверять - закончилась операция стирания или нет
df->timer.start(11, [this](){ // DF_TASK_PRIORITY
// таймер сработал - запускаем проверку
df->waitWip(this);
});
});

// подаем команду ERASE
df->small_buffer[0] = Df::CMD_MASS_ERASE;
bool r = df->spi->DmaWriteRead(df->small_buffer, df->small_buffer, 1);
if(!r){
// тоже никогда сюда не попадем
printf("Df::DfMassEraseTask error: SPI is busy! It must not happen, it's a bug!\n");
}
});

// подаем WREN команду через SPI-DMA - для разблокировки записи флеш
df->small_buffer[0] = Df::CMD_WREN;
bool r = df->spi->DmaWriteRead(df->small_buffer, df->small_buffer, 4);
if(!r){
// сюда мы никогда не попадем, а если попали - значит где-то баг в реализации драйвера SPI
// поскольку шина по определению должна уже быть свободна
printf("Df::DfMassEraseTask error: SPI is busy - bug!\n");
}
});
// вдруг очередь переполнена
if(!r){
printf("Df::DfMassEraseTask error: spi fifo full\n");
}
}

public:
Df* const df;
delegate<void()> cbk;
SST::TPriority cbk_priority;
};

// функция стирания FLASH
bool Df::MassErase(
const delegate<void()>& cbk,
SST::TPriority cbk_priority = DF_TASK_PRIORITY)
{
// создаем задачу и добавляем в очередь FLASH
bool r = task_queue.enqueueTask(
DfMassEraseTask(this, cbk, cbk_priority)
);
if(!r){
printf("Df::MassErase error: task fifo full\n");
}
// и выходим
return r;
}

// процедура проверки WIP - write/eras in progress
void Df::waitWip(DfWriteTask_base* t){ // Thread mode
// Создаем задачу и добавляем в очередь шины SPI
bool r = spi->enqueueTask([t](){
// Шина свободна - занимаем ее
t->df->setCallback_and_CS([t](){ // Handler mode
// команда подана - снимаем CS
DFCS_n_pin::set();
// окончательно слазим с шины
t->df->spi->taskCompleted();

// проверяем
if(t->df->small_buffer[2]&1){ // retry
// если операция не закончилась(флешка все еще стирается) - опять запускаем таймер
t->df->timer.start(11, [t](){ // DF_TASK_PRIORITY
t->df->waitWip(t);
});
}else{ // если закончилась
// сигнализируем пользователя, что все прошло успешно
SST::postMessage(t->cbk_priority, t->cbk);
// завершаем задачу - теперь воспользоваться flash-кой сможет кто-то другой (кто стоит в очереди)
t->df->task_queue.taskCompleted();
}
});

// подаем команду через шину(DMA)
t->df->small_buffer[0] = CMD_RDSR;
bool r = t->df->spi->DmaWriteRead(
t->df->small_buffer,
t->df->small_buffer,
3);
if(!r){
printf("Df::waitWip error: SPI is busy - bug!\n");
}
});

// вдруг очередь SPI переполнена
if(!r){
printf("Df::waitWip error: spi fifo full\n");
}
}

Согласен, код довольно сложный. Но кто-то сможет привести более простой? Приведите - и я расскажу какие в вашем коде будут проблемы wink.gif
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 08:19
Сообщение #36


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Цитата
Конечо. При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной модели
Это шутка щас была да? lol.gif
Спасибо, поржал.
Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )
http://v8project.blogspot.com/

Пруфлинк бы на сравнение быстродействия серверов на JavaScript vs традиционный Сишный ))))) А то может мы тут действительно отстали от жизни совсем.

Кстати вам точно будет интересно. Советую сразу окно не закрывать, чувак там дальше показывает реальную магию https://youtu.be/gawmdhCNy-A


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 08:25
Сообщение #37


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Приветствую!

Модель SST хорошо работает до того момента, когда с одним и тем же ресурсом нужно работать из потоков с разным приоритетом.

Реальный пример: на шине висят пультик и второй МК, связь с которым должна завершиться в течении 300 мкс (каждые 3 мс). Или объединять логику пультика (которая в разных проектах одна и та же) с логикой связи со вторым МК, или разносить по разным потокам с их синхронизацией. Во втором случае при добавлении абонента на шину первые два потока вообще менять не надо. А SST?

Нет, можно с одним и тем же ресурсом работать абсолютно с любого уровня приоритета. Но если шина уже занята более низкоприоритетной задачей, тогда ничего не остается, как добавляться в очередь. Мы не можем прервать, скажем операцию записи в Flash, потому что это может привести к порчи старницы или всего блока/сектора.
Но очередь у нас приоритетная - и если приоритет нашей задачи самый высокий он станет в самое начало очереди и как только шина освободится - задача будет выполнена.
Так что с шиной можно работать даже из прерываний и одновременно из самых низкоприоритетных потоков(хоть Idle) - какая RTOS даст Вам такую возможность? Если Вы попытаетесь залочить мютекс из прерывания - это либо приведет просто к глюку и крешу всей системы, либо(если ОС написана корректно) - прерывание может быть заблокировано в ожидании этого мютекса - со всеми вытекающими из этого проблемами

Цитата(sigmaN @ Sep 8 2016, 11:19) *
Это шутка щас была да? lol.gif
Спасибо, поржал.
Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )
http://v8project.blogspot.com/

Пруфлинк бы на сравнение быстродействия серверов на JavaScript vs традиционный Сишный ))))) А то может мы тут действительно отстали от жизни совсем.

Значит Вы не разобрались. Пруфлинк Вам не поможет - давайте сделаем по другому. Я даю Вам 2 исходника - один сишный классический многопоточный, второй на javascript. Вы у себя запускаете, даете реальную нагрузку и смотрите, когда ресурсы системы закончатся при классической многопоточной модели на С, а когда в событийной на Яваскрипте, Ну и в процессе посмотрите, сколько будут занимать памяти оба под реальной нагрузкой(скажем хотя бы 100-200 одновременных коннектов)
Для этого Вам понадобятся 2 компа - на одном сервер, на втором клиент. Только клиент должен быть гораздо мощнее сервера, чтобы он был в состоянии дать ему реальную нагрузку.
NodeJS надеюсь установите без особого бубна, компилятор C думаю тоже wink.gif

Цитата
Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )
http://v8project.blogspot.com/

В том то и дело, что надо один раз напрячься и сделать, а потом пользоваться сколько угодно раз. Спасибо гуглу за их прекрасный движок и разработчикам NodeJS за то, что сделали из этого движка замечательный мощный, быстрый и удобный инструмент
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 08:33
Сообщение #38


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Прям с удовольствием потестирую )))))
Особенно создам ситуацию, когда оба сервера получают от клиента запрос требующий интенсивной обработки и буду весело наблюбдать как ваш JavaScript сервер перестанет обслуживать остальных клиентов.


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 8 2016, 08:37
Сообщение #39


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



На самом деле мы наверное действительно отстали от жизни. Сейчас цена железа стремится к нулю, а труд программистов такой тенденции не имеет. Посмотрите на тот же Андроид. Та же задача на голом С потребовала бы 400 Мгц (да тот же WinMobile вспомним). Но сейчас не спроста средний проц имеет 8 ядер под 2 ГГц, выходит дешевле и писать проще. Ладно, сие есть лирика, но динозавром выглядеть не хочется. Подозреваю что на SST можно решить ровно теже задачи, что и на обычной ОСи, и чаще всего это будет экономичнее и быстрее. Все же Javascript для эмбеддед старого доброго не слишком показательная и типичная задача. Ну реально смешно опрашивать кнопку в отдельном громоздком потоке в бесконечном цикле со всеми жирными объектами синхронизации. Мне представляется оптимальным некий гибрид обоих подходов к многозадачности. Ну как бы с идеей SST крутится всякая мелкая и не очень логика, а традиционный подход продолжает работать одновременно для не очень связных сложных задач, например веб-сервер крутится, GUI и что еще. На верхнем уровне это три разных задачи для традиционного планировщика, а внутри каждой - подход в духе SST. Может я не уловил сути и написал сейчас бред, тогда сорри.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 08:45
Сообщение #40


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Все же Javascript для эмбеддед старого доброго не слишком показательная и типичная задача.

Javascript очень даже уместен в embedded.

Цитата
Прям с удовольствием потестирую )))))

Отлично, готовлю код.

Цитата
Особенно создам ситуацию, когда оба сервера получают от клиента запрос требующий интенсивной обработки и буду весело наблюбдать как ваш JavaScript сервер перестанет обслуживать остальных клиентов.

Мы сейчас говорим об оверхеде на треды, а не о интенсивных математических задачах. О них поговорим позже, и там тоже много чего интересного.
JavaScript не предназначен для интенсивных математических вычислений, для этого есть другие прекрасные языки, которые свободно могут работать в паре с Javascript.
Так что давайте для начала протестируем оверхед - сервер на C и на JS будет просто принимать коннект и отправлять клиенту какой-нибудь ответ скажем по таймеру, 10 ответов в секунду.
Go to the top of the page
 
+Quote Post
arhiv6
сообщение Sep 8 2016, 08:47
Сообщение #41


Знающий
****

Группа: Свой
Сообщений: 633
Регистрация: 21-05-10
Из: Томск
Пользователь №: 57 423



brag, на сколько я понял, в обычном SST пока задача X не выполнена, менее приоритетные задачи блокированы. В таком случае, если эта задача X заняла ресурс - ту же шину SPI, мы никак не можем отдать управление менее приоритетным процессам и ядро простаивает. Если не сложно, можете подробнее описать, как правильно избавиться от этого ограничения? На сколько я понял, у Вас задача делится на несколько независимых (X1, X2) и они работают по событиям: X1 заняла шину и закончила работу, по событию освобождения шины вызвалась задача X2. Так?


--------------------
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 08:48
Сообщение #42


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

Наоборот, веб-сервер и GUI - это типичные задачи для SST и классические потоки здесь только создают проблемы. Да, их можно реализовать на классических потоках, но рано или поздно там появятся ActiveObjet-ы, а это значит, что треды пора выбрасывать в мусорку и переходить на более простой SST. в котором все является этими самыми Active Objectamи
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 08:54
Сообщение #43


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Ладно бы товарищ просто говорил что ребят, так проще и времени программиста меньше уходит. Все переходим на JS. Никто б с ним не спорил.
Но тут он совсем уже пошел по беспределу, заявив
Цитата
Нет, физически там один поток и один стек.

Цитата
При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной модели
После такого надо бы и пруфы в студию.
Потому как JS то и Multi-threading поддерживает так то.
Но у нас всё в один стек в один поток и в десятки и сотни раз круче сервер получается )))))))

Цитата
Мы сейчас говорим об оверхеде на треды, а не о интенсивных математических задачах. О них поговорим позже, и там тоже много чего интересного.
JavaScript не предназначен для интенсивных математических вычислений, для этого есть другие прекрасные языки, которые свободно могут работать в паре с Javascript.
Так что давайте для начала протестируем оверхед - сервер на C и на JS будет просто принимать коннект и отправлять клиенту какой-нибудь ответ скажем по таймеру, 10 ответов в секунду.
Давайте вы наконец осознаете, что классический сервер на то и создает потоки/процессы под кажого клиента, чтобы их обслуживать ПАРАЛЛЕЛЬНО. Параллелизм этот обеспечивается в данном случае таск шедулером операционной системы. О каком овэрхеде на трэды мы говорм, если у вас трэд один и стек тоже ))))) Нет трэдов нет оверхедов.

Я уже писал, что сравнивать потребление ресурсов нужно у двух систем обеспечивающих одинаковый функционал. Это мы наконец поймем или нет?

У меня создается впечатление, что у вас когда-то не заладилось с многопоточным программирвоанием и вы ушли в секту синглтрэад-одностековщиков )))))))))


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 8 2016, 09:04
Сообщение #44


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(sigmaN @ Sep 8 2016, 13:54) *
Но у нас всё в один стек в один поток и в десятки и сотни раз круче сервер получается )))))))

Вполне возможно, что так и будет. Потому что при большом наплыве клиентов накладные расходы на создание процесса на каждого клиента будут очень велики. Поэтому и в сях сейчас развивают асинхронное обслуживание сети. (async.io)
Процессоры сейчас быстрые, сети толстые, так что проще быстренько обслужить клиента, не создавая для него отдельного процесса, и перейти к следующему.
Насчёт жабаскрипта в эмбеддед я пока тоже не понялsm.gif


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 09:11
Сообщение #45


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
brag, на сколько я понял, в обычном SST пока задача X не выполнена, менее приоритетные задачи блокированы.

Верно!
Цитата
В таком случае, если эта задача X заняла ресурс - ту же шину SPI, мы никак не можем отдать управление менее приоритетным процессам и ядро простаивает.

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

Цитата
Если не сложно, можете подробнее описать, как правильно избавиться от этого ограничения? На сколько я понял, у Вас задача делится на несколько независимых (X1, X2) и они работают по событиям: X1 заняла шину и закончила работу, по событию освобождения шины вызвалась задача X2. Так?

Тут немножко стоит ввести терминологию. Я то понимаю и мне это естественно, но другим не очень. И так, терминология:
1. Событие(event) - это некоторое абстрактное понятие. Каждое событие имеет приоритет. Прерывание - это тоже событие.
2. Очередь событий(Event Queue) - это такая очередь, в которую можно добавлять события. При чем эти события сортируются в этой очереди в порядке:
а) - приоритета
б) - поступления
Тут есть некоторая оговорка - прерывание - это тоже событие, но размещением их в очереди занимаемся не мы, а процессор.
3. Обработчик события(Event Handler) - это функция связанная с конкретным событием, которая будет выполнена, как только будет для этого время.
4. Планировщик событий - достает события из очереди и выполняет для них соответствующие обработчики. При чем, если какой-то обработчик уже работает - он будет прерван обработчиком более приоритетного события. Планировщик состоит из 2х частей:
а) процессора, его контроллера прерываний который занимается планировкой событий приоритета выше или равного IRQ_MIN_PRIORITY. этот приоритет всегда выше приоритета обычных событий(user mode)
б) кусок кода на 200 строк, который занимается планировкой событий приоритета ниже IRQ_MIN_PRIORITY - то есть обычных пользовательских событий
5. Задача(Task) - это исполняемый обьект. Она тоже имеет свой приоритет. Задача состоит из:
а) обработчика события запуска задачи (обычная функция, физически у меня в С++ это operator() - почитайте про него)
б) любого количества обработчиков других событий. То есть задача может добавлять любые собития в очередь, а так же их обрабатывать.
в) любого количества внутренних переменных - это называется состояние задачи
6. Очередь задач - это очередь, в которую помещаются задачи в порядке их приоритета, затем в порядке попадания в очередь.

С этим все ясно?

Цитата
Но у нас всё в один стек в один поток и в десятки и сотни раз круче сервер получается )))))))

Так Вы тестировать сервак будете, делать для Вас(и остальных) код? или будете продолжать дальше глумиться, не понимая предмета?

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

Поздравляю, Вы поняли о чем идет речь wink.gif

Цитата
Насчёт жабаскрипта в эмбеддед я пока тоже не понялsm.gif

http://embeddednodejs.com/chapters.html
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 09:16
Сообщение #46


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Цитата
Поэтому и в сях сейчас развивают асинхронное обслуживание сети.
Это когда куча сокетов в массиве а потом в цикле WaitForMultipleObjects()? Старо как мир же )

Ну товарищ не уточнил мы хотим обслужить 10000 коннектов в секунду с отдачей каждому по 2 байта или подключаем 900 клиентов и каждый просит у сервера что хочет. В том числе и нечто, что заставляет сервер подождать(прихода некоего события или интенсивные вычисления потребуются).

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 09:22
Сообщение #47


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Это когда куча сокетов в массиве а потом в цикле WaitForMultipleObjects()? Старо как мир же )

Вы все равно ничего не поняли. Ключевое слово Wait - оно у вас в голове и у меня тоже было, и очень трудно его было оттуда выбросить.
WaitForMultipleObjects() заблокирует поток, а у нас нет блокировок, нет Wait, у нас есть On

Цитата
Ну товарищ не уточнил мы хотим обслужить 10000 коннектов в секунду с отдачей каждому по 2 байта или подключаем 900 клиентов и каждый просит у сервера что хочет. В том числе и нечто, что заставляет сервер подождать(прихода некоего события или интенсивные вычисления потребуются).

Сервер будет отдавать запрошенный пользователем файл - устроит? Тестить будете?
В математику давайте пока лесть не будем, об этом позже и там тоже все довольно интересно и тоже все отлично ложится на SST-модель. Просто Javascript не предназначен для сложных вычислений, для этого есть другие инструменты, которые свободно работают в паре с JS.

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

Параллельные вычисления можно проводить тоже без потоков, которые у Вас засели в голове. Планировщик запросто может раскидывать разные события по разных процессорах и обрабатывать таким образом несколько событий одновременно - сколько процессоров(ядер) столько и будет одновременно работать событий. При чем стеков нужно столько, сколько имеется процессоров.
А в Вашей многопоточной модели нужно стеков столько, сколько есть потоков(по несколько потоков на каждый коннект к серверу)
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 09:33
Сообщение #48


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Цитата
Вы все равно ничего не поняли. Ключевое слово Wait - оно у вас в голове и у меня тоже было, и очень трудно его было оттуда выбросить.

Могу в аналогичном тоне заявить, что наверно вы тоже ничего не поняли и у вас в голове On. Чтобы кто-то вызвал этот On должен быть где-то и Wait и цикл бесконечный.
Например в эмбэддэд с этим проще, потому что этот вэйт реализуется логикой отвечающей за генерацию прерывания по изменению уровня пина(например). И вы потом гордо залетаете в прерывание, оттуда вызываете свой On и радуетесь. Но кто-то до этого аппаратно делал Wait )

Даже в SST таки есть бесконечный цикл, который крутится и вызывает нужные On взависимости от ивэнтов в очереди.
Чем это отличается от WaitForMultipleObjects() для сокетов? Я не знаю. Наверно ни чем)
Но делая этот системный вызов я отдаю ресурсы другим потокам в ОС(ох уж эти потоки опять а!). Хотя таки да, мог бы крутить цикл и проверять сокеты вручную, потом ложить ивэнт в очередь а потом вызывать On И как глубоко бы вы не зкапывали этот цикл или вэйт он всё равно где-то есть.

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

Надеюсь вы таки поняли о чем я говорю ) Потому что хоть вы в это и не верите, а большинство и с принципами работы SST тут разобрались и про On знают не по наслышке тоже давно )

Цитата
Сервер будет отдавать запрошенный пользователем файл - устроит? Тестить будете?
В математику давайте пока лесть не будем, об этом позже и там тоже все довольно интересно и тоже все отлично ложится на SST-модель. Просто Javascript не предназначен для сложных вычислений, для этого есть другие инструменты, которые свободно работают в паре с JS.
Файл объемом 1Гб, скорость сети каждого клиента ограничиваем шейпером на роутере до....1мегабита ) Ну, чтоб точно заметно было как остальные клиенты сервера курят бамбук )
Не тратьте силы и так всё ясно же. Застрянет ваш сервер на одном клиенте и пока тот файл не скачает - всё это весело встанет колом.

Ааа, нет! Я придумал! В системе появится таймер, он будет генерировать событие onTimer, в этом событии вы будете проходиться по массиву с сокетами и каждому отправлять там по пару байт данных. Обходя сокеты по кругу. А потом скажите ну вот, все качают и никаких вэйтов ))))))))))


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 8 2016, 09:39
Сообщение #49


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



а не в безопастности ли выполнения коренное различие? в системе с различными стеками потоков проще убить взбунотовавщийся поток, он в теории не нарушит работу других.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 09:45
Сообщение #50


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата(sigmaN @ Sep 8 2016, 12:33) *
Могу в аналогичном тоне заявить, что наверно вы тоже ничего не поняли и у вас в голове On.

Это верно. Я выбросил из головы Wait и вдул туда On.

Цитата(sigmaN @ Sep 8 2016, 12:33) *
Чтобы кто-то вызвал этот On должен быть где-то и Wait и цикл бесконечный.
Например в эмбэддэд с этим проще, потому что этот вэйт реализуется логикой отвечающей за генерацию прерывания по изменению уровня пина(например). И вы потом гордо залетаете в прерывание, оттуда вызываете свой On и радуетесь. Но кто-то до этого аппаратно делал Wait )
Даже в SST таки есть бесконечный цикл, который крутится и вызывает нужные On взависимости от ивэнтов в очереди.

Нет, в этом нет необходимости. Хотя физически, как Вы подметили один такой цикл вполне может быть, это обусловлено самой архитектурой конкретного процессора. Но он пустой, кроме как инструкции sleep там больше ничего нет.


Цитата(sigmaN @ Sep 8 2016, 12:33) *
Чем это отличается от WaitForMultipleObjects() для сокетов? Я не знаю. Наверно ни чем)

А тут Вы уже не правы. WaitForMultipleObjects блокирует пользовательский поток. Все, поток мертвый. В то время, как в SST или яваскрипте в принципе нет такой возможности

Цитата
Но делая этот системный вызов я отдаю ресурсы другим потокам в ОС(ох уж эти потоки опять а!). Хотя таки да, мог бы крутить цикл и проверять сокеты вручную, потом ложить ивэнт в очередь а потом вызывать On И как глубоко бы вы не зкапывали этот цикл или вэйт он всё равно где-то есть.
Если вы в JS подписываетесь на какое-то событие, чтобы движок дёргал ваш On - это всего-лишь означает что нужный Wait и цикл скрыты где-то далеко.
Так что не шибко то разгоняйтесь с перестраиванием мозга на On.

Ну пусть себе будет этот цикл где-то один единственный, зачем программисту о нем думать? Хотя физически его может и не быть или он будет абсолютно пустой, но дело не в этом.
Вы, например, когда пишете код на С думаете, какой при этом байт-код получается? Какие инструкции при этом генерит компилятор, какие регистры использует?


Цитата(DASM @ Sep 8 2016, 12:39) *
а не в безопастности ли выполнения коренное различие? в системе с различными стеками потоков проще убить взбунотовавщийся поток, он в теории не нарушит работу других.

Для безопасности есть MPU/MMU, если оно действительно нужно - тогда запросто берем и используем.

Цитата
Файл объемом 1Гб, скорость сети каждого клиента ограничиваем шейпером на роутере до....1мегабита ) Ну, чтоб точно заметно было как остальные клиенты сервера курят бамбук )
Не тратьте силы и так всё ясно же. Застрянет ваш сервер на одном клиенте и пока тот файл не скачает - всё это весело встанет колом.

Идет. Будете тестить? Файлик сами приготовите любой. Только боюсь, в этом случаи мы не SST-модель будем тестить, а Ваш жесткий диск или Ваш порезанный канал. Но как только Вы подсунете нормальный файл на несколько КБ-МБ и сделаете канал, достаточный для закрузки сервака на полную - все станет совсем иначе.
Хотя даже с гиговым файлом/файлами однопоточный javascript покажет выше производительность, чем классический многопоточный C. Проверим?
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 8 2016, 09:49
Сообщение #51


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



Цитата(brag @ Sep 8 2016, 12:45) *
Для безопасности есть MPU/MMU, если оно действительно нужно - тогда запросто берем и используем.

В случае единого стека всех потоков с трудом представляю его использование
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 09:53
Сообщение #52


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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

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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 09:58
Сообщение #53


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата(DASM @ Sep 8 2016, 12:49) *
В случае единого стека всех потоков с трудом представляю его использование

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

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

Зачем нужен MMU? Он нужен, чтобы пользователь вашей системы смог запускать свои приложения на ней, и при этом криво написанные приложения не завалили всю систему. В пределах одной программы он не нужен.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 09:58
Сообщение #54


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 10:13
Сообщение #55


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Ну во-первых там есть таймаут. Так что поток не мёртвый )

Нет, он мертвый. Он не может в это время читать с диска, обрабатывать тысячи других событий, которые мы не прописали в 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 или наоборот. И сможете проверить, сколько памяти(и проца) заняла каждая из реализаций.
Идет, все устраивает, будете тестить?
Go to the top of the page
 
+Quote Post
arhiv6
сообщение Sep 8 2016, 10:14
Сообщение #56


Знающий
****

Группа: Свой
Сообщений: 633
Регистрация: 21-05-10
Из: Томск
Пользователь №: 57 423



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 ? Это до меня пока не доходит
Прикрепленное изображение


Сообщение отредактировал arhiv6 - Sep 8 2016, 10:17


--------------------
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 10:26
Сообщение #57


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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 байт хватит? Как посчитать?
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 10:34
Сообщение #58


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 10:47
Сообщение #59


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

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

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

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

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

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

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

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

Конечно.

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

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

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

Ну вот Вы не видите, поэтому я сомневаюсь, что Вы его проведете. А если проведу я и покажу результаты, то Вы не поверите. Я к тому, что одну и ту же задачу, то есть наш сервер и клиент можно решить двумя способами - многопоточным и однопоточным. И производительность второго способа под серьезной нагрузкой будет в десятки раз выше.
Go to the top of the page
 
+Quote Post
arhiv6
сообщение Sep 8 2016, 10:52
Сообщение #60


Знающий
****

Группа: Свой
Сообщений: 633
Регистрация: 21-05-10
Из: Томск
Пользователь №: 57 423



brag, спасибо большое за разъяснение, теперь всё понятно. А можете посоветовать что почитать для тех кто хочет больше узнать про асинхронное программирование (не привязываясь к языку, чтобы упор был именно на алгоритмы)?

Сообщение отредактировал arhiv6 - Sep 8 2016, 10:54


--------------------
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 10:57
Сообщение #61


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

Не за что. Почитать мало что есть, нужно тренироваться. Для этого отлично подходит Javascript/NodeJS. А потом и на C++11(именно 11, на старых плюсах кода надо больше) сможете или на другом языке, который позволяет программировать в асинхронном стиле просто, без бубна. Можно и на C асинхронно писать, даже на ассемблере, только кода будет слишком много. Лучше поручить эту грязную работу компилятору.
Алгоритмы на любом языке одинаковые, отличается лишь внешний вид кода.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 11:34
Сообщение #62


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 11:40
Сообщение #63


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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


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

Ок, делаю.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 12:34
Сообщение #64


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
AlexandrY
сообщение Sep 8 2016, 12:39
Сообщение #65


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



Цитата(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-е. Тож можно исходники нагенерить.
Вот с чем надо ваш подход сравнивать.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 12:42
Сообщение #66


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

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

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

Кто-то слышал, а кто-то не слышал. Все мы учимся.
Go to the top of the page
 
+Quote Post
Kabdim
сообщение Sep 8 2016, 12:51
Сообщение #67


Знающий
****

Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842



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

Вытесняющая многозадачность (приоритетная многозадачность, англ. preemptive multitasking, дословно упреждающая многозадачность) — это вид многозадачности, при которой операционная система принимает решение о переключении между задачами по истечению некоего кванта времени[1].
rolleyes.gif
Go to the top of the page
 
+Quote Post
AlexandrY
сообщение Sep 8 2016, 13:03
Сообщение #68


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



Цитата(brag @ Sep 8 2016, 15:42) *
SST - это вытесняющая многозадачность.


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

Не надо тут искажать семантику базовых понятий в embedded.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 15:18
Сообщение #69


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата(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 коннектов в секунду!
Прикрепленный файл  js1.zip ( 1.51 килобайт ) Кол-во скачиваний: 25


Теперь делаю то же самое в классической многопоточной модели на C. Это займет у меня гораздо больше времени, чем я потратил на реализацию JS.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 17:22
Сообщение #70


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Хух, сишный клиент осилил. То 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;
}
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 17:44
Сообщение #71


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Интересный способ проверить сколько потоков может вывезти система )))

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

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

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

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

Аа, черт, они на апаче PHP крутили. Тест в топку!. Но инструментарий можно на вооружение взять.


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 19:21
Сообщение #72


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

Ну так сервак на 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 уже не тянет..
Go to the top of the page
 
+Quote Post
Herz
сообщение Sep 8 2016, 19:38
Сообщение #73


Гуру
******

Группа: Модераторы
Сообщений: 10 983
Регистрация: 23-11-05
Пользователь №: 11 287



Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)". Будут возражения? Или переименуем всю тему? Автор, что думаете?
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 20:04
Сообщение #74


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Возражений нет, но я не автор. Про аллокатор у меня тоже есть материал, доберемся и до него. тк в 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 - это язык, а не сервер - нет лишней прослойки.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 8 2016, 20:43
Сообщение #75


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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

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

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

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

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

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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 8 2016, 21:03
Сообщение #76


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



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

И перенести, наверное, в "Операционные системы".


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 8 2016, 21:10
Сообщение #77


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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 язык.. Хотя могу и однй сделать, запросто, если будет такая необходимость.
Go to the top of the page
 
+Quote Post
shreck
сообщение Sep 9 2016, 02:16
Сообщение #78


Местный
***

Группа: Свой
Сообщений: 327
Регистрация: 24-06-06
Из: Томск
Пользователь №: 18 328



2brag

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

Может быть выложите реализацию делегатов?
Сам использую свой велосипед и собираю велосипеды других с целью повышения образования.
Go to the top of the page
 
+Quote Post
Serhiy_UA
сообщение Sep 9 2016, 04:53
Сообщение #79


Знающий
****

Группа: Свой
Сообщений: 721
Регистрация: 23-10-08
Из: next to Odessa
Пользователь №: 41 112



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

Конечно надо бы разделить тему на усмотрение модератора. Мне, к примеру, обсуждение уже совсем не понятным становится. Мой вопрос скорее относился к вводным понятиям о кучах, да и тема мною сформулирована вроде бы четко.
Хотя интересно было узнать то, что помимо стандартных new/delete, существуют и свои средства (стратегии) для управления кучами.
В институте был курс "Организация вычислительных процессов в ЭВМ", в частности с понятиями об операционных системах и их работе с очередями пользовательских программ, были и книги по этой теме. А сейчас какие есть книги (ru/eng) на эту же тематику, но c учетом новых подходов?
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 06:12
Сообщение #80


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Хотя интересно было узнать то, что помимо стандартных 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 для всех)
Go to the top of the page
 
+Quote Post
Serhiy_UA
сообщение Sep 9 2016, 07:23
Сообщение #81


Знающий
****

Группа: Свой
Сообщений: 721
Регистрация: 23-10-08
Из: next to Odessa
Пользователь №: 41 112



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

В моем случае под 7 версией FreeBCD на индустриальном компьютере в режиме реального времени одновременно работают пять равно приоритетных программ. Каждая через new/delete (без перегрузок) создает для себя динамические объекты в куче. Если куча общая, то ОС должна четко организовывать управление кучей для всех программ. Хотел узнать (прочесть) об этом немного больше, хотя и так кое-что уже прояснилось.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 9 2016, 07:36
Сообщение #82


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Очень бы хотелось увидеть как бы вы обслуживали много клиентов, рисовали 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 запаритесь делать.


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 07:47
Сообщение #83


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Очень бы хотелось увидеть как бы вы обслуживали много клиентов, рисовали 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 в мнгопоточном блокирующем стиле.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 9 2016, 07:52
Сообщение #84


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 07:57
Сообщение #85


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Так я уже показал, как я работаю с Датафлеш. Привел полный код операции стирания 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 ? Ведь для программы потоки не нужны, они нужны(а нужны ли?) конкретной оси(линуксу) для планировки программы.
Go to the top of the page
 
+Quote Post
alexunder
сообщение Sep 9 2016, 07:57
Сообщение #86


unexpected token
****

Группа: Свой
Сообщений: 899
Регистрация: 31-08-06
Из: Мехелен, Брюссель
Пользователь №: 19 987



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

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

Вероятно, есть критерии, когда SST уместна, а когда лучше применять многопоточный вариант.


--------------------
А у тебя SQUID, и значит, мы умрем.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 08:00
Сообщение #87


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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

Естественно есть. Например пользовательские ОС, где пользователь может запускать свои или чужие приложения. И то, в нормальных ОС, типа FreeBSD для каждого процесса есть по сути свой SST - это kqueue. То есть приложения планируются в многопоточном стиле, а работу внутри приложений можно делать в однопоточном асинхронном, с приоритетами - вобщем полный SST. В линуксе (по крайней мере на сколько мне известно) есть костыль в виде epoll, через который и реализовано все в таких штуках, как Nginx или NodeJS.
NodeJS - это не SST, у него один приоритет, а у SST их много. Если в NodeJS где либо втулить тяжелый или вечный цикл - вся программа умрет, а если это сделать в SST - то будет продолжать работать, умрут только менее приоритетные задачи. SST - это продолжение NodeJS.
Go to the top of the page
 
+Quote Post
alexunder
сообщение Sep 9 2016, 08:16
Сообщение #88


unexpected token
****

Группа: Свой
Сообщений: 899
Регистрация: 31-08-06
Из: Мехелен, Брюссель
Пользователь №: 19 987



Цитата(brag @ Sep 9 2016, 10:00) *
Естественно есть. Например пользовательские ОС, где пользователь может запускать свои или чужие приложения.

Нет ли у Вас ссылок на какую-нибудь литературу по SST (необязательно для embedded)?


--------------------
А у тебя SQUID, и значит, мы умрем.
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 08:26
Сообщение #89


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Сделал сервер сишный блокирующий многопоточний. Как и предполагалось, система умерла сразу, как только к серверу подключился 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).
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Sep 9 2016, 09:36
Сообщение #90


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Почерпнул много интересного из этого обсуждения. Прямо как будто зашел на электроникс лет 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:
Как так? Сначала предварительно объявляем шаблон с двумя параметрами, а потом уже полное объявление с другим количеством? На всякий случай каюсь, до изучения шаблонов с переменным числом параметров я пока не дошел...


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 09:58
Сообщение #91


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Сергей Борщ, это называется 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...)>{
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 9 2016, 10:42
Сообщение #92


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Да, я тоже могу сказать, что узнал что-то новое из этой беседы.
Особенно меня удивил 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 тому пример).
На чем наверно можно и сделать окончательный вывод )


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 11:55
Сообщение #93


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Для начала давайте определимся, что любой объект с состоянием(для простоты рассматриваем класс в ООП) это так или иначе и есть конечный автомат.

Да. Тред, работающий на одноядерном процессоре - тоже конечный автомат, вернее не тред, а вся эта тредовая ОС 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? Даже представить сложно, как это все будет тормозить и глючить...
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 9 2016, 19:24
Сообщение #94


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



Кстати справедливости ради надо добавить, что идея именно посылать объекту сообщение идет еще со времен первых экспериментов с объектно ориентированным программированием.
Именно такая парадигма была принята в языке 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 я бы всё равно сейчас не стал ))


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 20:04
Сообщение #95


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



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
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 9 2016, 20:21
Сообщение #96


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 9 2016, 20:51
Сообщение #97


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Ок, с фанатичностью убавим wink.gif
Мне тоже лень было, но таки переборол себя и выкинул, теперь доволен, хотя по началу было сложно, но деваться некуда - код и так со временем ставал практически асинхронным, редко где оставались блокирующие вызовы. Но был очень горбатым тк работал под синхронной ОС - гибрид такой получался, да и опыта мало было, пришлось на JS поработать, чтобы набраться. Потом я удалил ОС, и в месте с ней десятки тысяч строк кода и начал новые проекты делать вообще без ОС, и строил велосипеды так, чтобы чувствовать себя как можно ближе к JS. На некоторых проектах такого подхода не хватало(а РТОС взять религия не позволила) и тут я вспомнил, что где-то читал про какой-то там SST не обратив на него особого внимания, тк работал на нормальных РТОС. Нашел, разобрался и понял, что это именно то, что мне нужно. Еще месяца 4 практики и получился крутой фреймворк, убивающий по производительности и простоте кода любую RTOS. Как убивает Node любую синхронную систему, как по простоте кода(сервак можно написать парой тройкой строк), так и по скорости и памяти, хотя JS довольно тяжелый и медленный язык.

Вот сейчас мне тоже лень изучать Rust, все плюсами довольствуюсь. Не могу решиться найти время и силы на это, но вижу, что придется, а то все таки плюсы - опасный язык, хотя безопасность повышается использованием высоко-уровневых библиотек(STL), но с ними трудно работать на слабых МК до 50мгц/8кб, надо что-то подпилвать, что-то выпиливать, а это я не люблю, проще самому сварганить велосипед на сотню строк за пол часа (собственно так и было сделано).
Нужен язык, где безопасность идет сразу из коробки и такой же нативный и быстрый, как плюсы, со всеми шаблонами, лямбдами и другими вкусностями.
Go to the top of the page
 
+Quote Post
psL
сообщение Sep 9 2016, 20:59
Сообщение #98


Знающий
****

Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390



Цитата(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++. Или использовать только реентерабельные функции и статические данные в С. Тогда никакого стека вообще не будет, и никакой кучи тоже.
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Sep 9 2016, 22:16
Сообщение #99


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
brag
сообщение Sep 10 2016, 05:21
Сообщение #100


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Цитата
Каждый поток в линукс создается со стеком ~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
Go to the top of the page
 
+Quote Post

17 страниц V   1 2 3 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st June 2025 - 10:07
Рейтинг@Mail.ru


Страница сгенерированна за 0.02945 секунд с 7
ELECTRONIX ©2004-2016