Перейти к содержанию
    

Система, управляемая событиями и SST(super-simple tasker)

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

Не за что. Почитать мало что есть, нужно тренироваться. Для этого отлично подходит Javascript/NodeJS. А потом и на C++11(именно 11, на старых плюсах кода надо больше) сможете или на другом языке, который позволяет программировать в асинхронном стиле просто, без бубна. Можно и на C асинхронно писать, даже на ассемблере, только кода будет слишком много. Лучше поручить эту грязную работу компилятору.

Алгоритмы на любом языке одинаковые, отличается лишь внешний вид кода.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Еще огромное преимущество SST-модели в том, что там все высокоуровневые обьекты, а не низкоуровневые регистры и стеки. Это дает возможность запустить ту же программу на PC, и отлаживать ее там, даже не имея под рукой контроллера.

Также у меня для этого дела написаны мониторы очередей. Кода включен дебаг - любые действия с очередью отлавливает монитор(Observer Pattern, погуглите будете знать что это такое) и выводит информацию на экран - все видно, как на ладони.

Я компилирую проект(который в итоге будет работать на МК) под PC и разрабатываю, отлаживаю его именно на PC. Запускаю, делаю различные тесты, которые провести на МК не предоставляется возможным. И все видно, как на ладони, сколько какая очередь занимает, где нужно больше дать памяти, где меньше, да и целую кучу других тестов(в том числе и юнит-тестов при необходимости)

И только потом, когда софт более-менее сформирован уже подбираю под эту задачу МК, с необходимым количеством ресурсов с необходимым запасом. И потом просто компилирую проект под МК, без изменения кода!

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

 

 

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

Ок, делаю.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ок, делаю.

 

Да не парьтесь.

Гигантскую по вашим меркам RTOS MQX спокойно портировал вот на эту платформу - https://geektimes.ru/post/268918/

Вот здесь можете посмотреть порт этой RTOS - https://github.com/Indemsys/Light-Control-M...X_light_porting

 

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

 

А кому нужен чистый автоматный подход тот использует IAR visualState

Либо StateFlow в Matlab-е. Тож можно исходники нагенерить.

Вот с чем надо ваш подход сравнивать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

Кто-то слышал, а кто-то не слышал. Все мы учимся.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А топик так хорошо начинался. :rolleyes: Тоже что ли залезть на трибуну - рассказать про protothreads?... :biggrin:

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

Вытесняющая многозадачность (приоритетная многозадачность, англ. preemptive multitasking, дословно упреждающая многозадачность) — это вид многозадачности, при которой операционная система принимает решение о переключении между задачами по истечению некоего кванта времени[1].

:rolleyes:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

 

Не надо тут искажать семантику базовых понятий в embedded.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Теперь делаю то же самое в классической многопоточной модели на C. Это займет у меня гораздо больше времени, чем я потратил на реализацию JS.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

 

Привожу код клиента прямо сдесь.

Компилоровать так: gcc -O2 -Wall -lpthread client.c

 

По количеству строк кода - JS 42 строки, C - 105 строк.

#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;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

 

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

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

 

По-моему вот здесь кто-то уже всё протестировал http://zgadzaj.com/benchmarking-nodejs-bas...inst-apache-php

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

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Ну так сервак на JS показывает количество коннектов в секунду - это количество скачанных файлов в секунду. Правда не гиговых, а нормальных. Можете сами проверить на гиговом, если есть желание.

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

 

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

 

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

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

 

И так, осилил сервак, отловил баги, даже отладчик пришлось запускать :( Как просто на С наделать ошибок..

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

 

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

У клиента же все по другому - коннекты происходят в потоках и там на каждый коннект создается 1 пток.

 

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

 

По количеству строк JS тоже ессно выиграл 67 строк в JS против 151 на C. Асинхронное неблокирующее программирование проще и эффективное, чем многопоточное блокирующее.

Интересно такой же многопоточный сервак реализовать на чем-то аналогичном динамическом, типа Java. Думаю он загнется при сотне клиентов. А то силы не равны получаются, быстрый С против медленного JS, но последний все равно выиграл из за своей асинхронной натуры.

#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 уже не тянет..

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)". Будут возражения? Или переименуем всю тему? Автор, что думаете?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

 

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

 

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

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

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

 

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

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

 

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

 

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

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...