Обсуждение MT7688AN HLK-7688A

pvvx

Активный участник сообщества
Это по тому что в MIPS 24Kec нет MMU?
root@Omega-2807:/tmp# time ./test_vfork

real 1m 15.98s (76s)
user 0m 1.02s
sys 0m 31.05s

real 1m 13.36s (73s)
user 0m 0.89s
sys 0m 30.03s

real 1m 10.65s (71s)
user 0m 0.98s
sys 0m 29.60s

root@Omega-2807:/tmp# time ./test_fork

real 1m 15.89s (76s)
user 0m 1.14s
sys 0m 30.86s

real 1m 18.88s (79s)
user 0m 1.15s
sys 0m 31.46s
Код:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>


static int create_process(void) {
    pid_t pid;
    int status;

    pid = vfork();
    if (-1 == pid) {
        return errno;
    }

    if (pid == 0) {
        /* child */
        exit(EXIT_SUCCESS);
    }

    waitpid(pid, &status, 0);

    return EXIT_SUCCESS;
}

int main(void) {
    int i;
 
    for (i = 0; i < 100000; i ++) {
        create_process();
    }

    return EXIT_SUCCESS;
}
Это кошмар, при том, что ресурсов то для сохранения нет (открытых файлов и прочего, на что время и идет при fork()).
 

pvvx

Активный участник сообщества
Ubuntu 16.04.6 LTS 4.14.0 NanoPi-NEO-Plus2
100000 fork()
root@NanoPi-NEO-Plus2:/tmp# time ./test_fork
real 0m53.676s
user 0m30.708s
sys 0m23.939s

real 0m53.031s
user 0m30.612s
sys 0m23.501s

real 0m52.248s
user 0m29.964s
sys 0m23.259s

real 0m51.872s
user 0m29.806s
sys 0m23.083s

100000 vfork()
root@NanoPi-NEO-Plus2:/tmp# time ./test_vfork

real 0m6.963s
user 0m1.139s
sys 0m4.998s

real 0m7.056s
user 0m1.172s
sys 0m5.032s

real 0m6.943s
user 0m1.059s
sys 0m5.019s
 

sharikov

Active member
760 микросекунд на создание процесса на не самом производительном CPU - кошмар ?
Вы посмотрите сколько времени уходит на 1 системный вызов или на переключение контекста во взрослом CPU.
Если вы создает процессы за микросекунды - вы явно что -то делаете не так.
 

pvvx

Активный участник сообщества
760 микросекунд на создание процесса на не самом производительном CPU - кошмар ?
Вы посмотрите сколько времени уходит на 1 системный вызов или на переключение контекста во взрослом CPU.
Если вы создает процессы за микросекунды - вы явно что -то делаете не так.
Не я создаю, а система создает процессы. Берите любой bash...
На той версии ядра, что у MIPS 24Kec MT7688 различие fork() c vfork() должно быть в 4..5 раз. На AMD 2+ ГГц, на том-же ядре ~4 раза.
Если дурной MMU, то CPU будет копировать память, а не ставить флажки (записи в сегмент не было :))... RAM и не напасешся.
И 760 микросекунд - это голый контекст у задачи...
 

pvvx

Активный участник сообщества
Что будет когда система начнет дублировать все открытие файлы и прочие свои и кеши задачи?
На MIPS 4КЕс - аналогично - различие в 4..5 раз.
 

pvvx

Активный участник сообщества
Потому что с eCos я работал и мне не нужен полноценный Linux.
При оптимизации ядра и прочей шелухи разницы нет, кроме старости eCOS и массе дырок.

@sharikov - fork() vs. vfork()
Я не знаю MIPS 24КЕс и у меня подозрения, что что-то не врублено в ядре. Зачем переводить виртуальную память в физическую, если к ней обращения нет?
 

pvvx

Активный участник сообщества
Причина подозрения - более медленный по тактовой (в два раза минимум) 4KEc выполняет эти 100000 vfork() быстрее... fork() сравнивать сложнее - там от загрузки и раздутости системы зависит...
 

pvvx

Активный участник сообщества
И на счет сколько раз форкаются какие-либо CGI и прочее в web-сервере сами додумаетесь :) Подсказка - на каждый чуть ли не байт :)
 

sharikov

Active member
И на счет сколько раз форкаются какие-либо CGI и прочее в web-сервере сами додумаетесь :) Подсказка - на каждый чуть ли не байт :)
А так нельзя делать. Делают умнее, но для этого надо думать.

Если смотреть загрузку системы по top видно что при работе вашего теста процессор не нагружен ( у меня 38%)
В вашем примере тормозит waitpid(pid, &status, 0) видать сигнал родительскому процессу медленно передается.

Я его немного модифицировал убрав ожидание:
Код:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>

struct timespec create_time;
struct timespec delete_time;

struct timespec timespec_normalise(struct timespec ts)
{
    while(ts.tv_nsec >= 1000000000)
    {
        ++(ts.tv_sec);
        ts.tv_nsec -= 1000000000;
    }
  
    while(ts.tv_nsec <= -1000000000)
    {
        --(ts.tv_sec);
        ts.tv_nsec += 1000000000;
    }
  
    if(ts.tv_nsec < 0 && ts.tv_sec > 0)
    {
        /* Negative nanoseconds while seconds is positive.
         * Decrement tv_sec and roll tv_nsec over.
        */
      
        --(ts.tv_sec);
        ts.tv_nsec = 1000000000 - (-1 * ts.tv_nsec);
    }
    else if(ts.tv_nsec > 0 && ts.tv_sec < 0)
    {
        /* Positive nanoseconds while seconds is negative.
         * Increment tv_sec and roll tv_nsec over.
        */
      
        ++(ts.tv_sec);
        ts.tv_nsec = -1000000000 - (-1 * ts.tv_nsec);
    }
  
    return ts;
}

struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
{
    /* Normalise inputs to prevent tv_nsec rollover if whole-second values
     * are packed in it.
    */
    ts1 = timespec_normalise(ts1);
    ts2 = timespec_normalise(ts2);
  
    ts1.tv_sec  -= ts2.tv_sec;
    ts1.tv_nsec -= ts2.tv_nsec;
  
    return timespec_normalise(ts1);
}
struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
{
    /* Normalise inputs to prevent tv_nsec rollover if whole-second values
     * are packed in it.
    */
    ts1 = timespec_normalise(ts1);
    ts2 = timespec_normalise(ts2);
  
    ts1.tv_sec  += ts2.tv_sec;
    ts1.tv_nsec += ts2.tv_nsec;
  
    return timespec_normalise(ts1);
}
double timespec_to_double(struct timespec ts)
{
    return ((double)(ts.tv_sec) + ((double)(ts.tv_nsec) / 1000000000));
}

static int create_process(void) {
    pid_t pid;
    int status;
    struct timespec t1, t2, td;

    clock_gettime(CLOCK_MONOTONIC, &t1);

    pid = fork();
    if (-1 == pid) {
        return errno;
    }
    if (pid == 0) {
        /* child */     
        exit(EXIT_SUCCESS);
    }
    clock_gettime(CLOCK_MONOTONIC, &t2);
    td = timespec_sub(t2, t1);
    create_time = timespec_add(create_time, td);

/*    clock_gettime(CLOCK_MONOTONIC, &t1);
    waitpid(pid, &status, 0);
    clock_gettime(CLOCK_MONOTONIC, &t2);
    td = timespec_sub(t2, t1);
    delete_time = timespec_add(delete_time, td);
*/
    return EXIT_SUCCESS;
}
int main(void) {
    int i;  
    for (i = 0; i < 100000; i ++) {
        create_process();
    }
    wait(NULL);
    printf("Creation time    = %e\n", timespec_to_double(create_time) / 100000.0);
/*    printf("Termination time = %e\n", timespec_to_double(delete_time) / 100000.0);  */
    return EXIT_SUCCESS;
}
Результат:
[inline]gcc -O3 testfork.c -o testfork

time ./testfork
Creation time = 2.077773e-05

real 0m17.783s
user 0m0.276s
sys 0m16.017s[/inline]
 

nikolz

Well-known member
просто так..
Экспериментировал на nonos ESP по реализации многозадачности с управлением по внешним прерываниям и максимальным временем ожидания для задачи.
нечто подобие многозадачной системы
для хранения параметров задачи и переход к запуску новой затрачивается не более 128 байт и не более 5 мкс.
можно время реакции снизить до 2 мкс.
 

pvvx

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

Если смотреть загрузку системы по top видно что при работе вашего теста процессор не нагружен ( у меня 38%)
В вашем примере тормозит waitpid(pid, &status, 0) видать сигнал родительскому процессу медленно передается.
В том то и дело, что необходимо переписывать всё.
Чем и занимался более полу года при создании своей системы с web на таких SoC...
Самое быстрая передача между процессами выходит через shm
 

pvvx

Активный участник сообщества
просто так..
Экспериментировал на nonos ESP по реализации многозадачности с управлением по внешним прерываниям и максимальным временем ожидания для задачи.
нечто подобие многозадачной системы
для хранения параметров задачи и переход к запуску новой затрачивается не более 128 байт и не более 5 мкс.
можно время реакции снизить до 2 мкс.
Какая ещё на ESP многозадачность? Там ловить нечего – при постоянном переключении кода задач кеш у неё не справляется и производительность упирается в скорость SPI Flash, где и находится исполняемый код. Это аналогично процу с тактовой в 10 MHz как макс.

Да и какие там виртуальные устройства, если памяти нету, а вся система – это огрызок в виде CPU с малым кеш, без RAM вообще. Для включения одной лампочки многозадачности не требуется, на чем и специализируется ESP.
 

pvvx

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

pvvx

Активный участник сообщества
@sharikov - Т.к. система не переключает одну лампочку, а тысчи, то получается примерно такое:
Существует общая область в shm, в которой всегда есть срез моментальных состояний всех внешних устройств и датчиков – некое виртуальное устройство. Дрова работают каждые со своими данными в этой структуре – принимают и передают в/от свои исполнительные устройства. События и атомарность распределяются семафорами в той-же shm. Web-сервер является всего транслятором запросов и переводчиком данных из такой структуры в вид Json или XML или raw для websocket и т.д. Это решает задачу общей синхронизации. Система выходит страшная, т.к. внешних данных и событий довольно много и они разнообразные. От этого обработка-переработка данных из общей структуры происходит по специальным текстовым скриптам, что решает задачу универсальности. Применять стандартные базы данных в малой системе на SoC накладно по ресурсам, да и к такой системе, как оказалось, пришли и проектировщики систем управления АЭС :). А уж как там распределены процессы исполнения подзадач – через треды или форки и т.д. – это второстепенная задача, которая так-же требует оптимизации и распределения по ресурсам. Такое примерно и слепил и уже протестил, а счас дооптимизирую… Результаты выходят нормальные по быстродействию и скорости реакции всея системы на малых SoC, но до совершенства можно доводить всю жизнь... Ныне проще подобрать архитектуру железа самого CPU…
 

nikolz

Well-known member
Какая ещё на ESP многозадачность? Там ловить нечего – при постоянном переключении кода задач кеш у неё не справляется и производительность упирается в скорость SPI Flash, где и находится исполняемый код. Это аналогично процу с тактовой в 10 MHz как макс.

Да и какие там виртуальные устройства, если памяти нету, а вся система – это огрызок в виде CPU с малым кеш, без RAM вообще. Для включения одной лампочки многозадачности не требуется, на чем и специализируется ESP.
просто так поясняю.
-----------------
я решал следующую проблему.
При реализации различных протоколов работы с внешними устройствами особенно такого протокола каr 1-wire далее I2c и т д
возникает проблема необходимости ожидания процессором сигнала на шине интерфейса или формирования определенной длительности импульса.
В это время драйвер устройства , который и является одной из задач, можно остановить, а управление процессором отдать другой задаче.
Т е получается многозадачная система, в которой число задач равно числу датчиков
При этом диспетчер задач активируется либо текущей задачей ,либо внешним прерыванием, либо внутренним прерыванием от таймера.
Т е имеем многозадачную систему, в которой есть три параллельно работающих механизма (ядра) - cpu, GPIO и Timer.
А чтобы система не зависала в ожидании GPIO , ожидание внешнего прерывания делается с временными воротами.
--------------------
Что в результате получилось - полностью исключается проблема длинных циклов CPU в ожидании сигналов от GPIO
При этом, для поддержания работы например 100 датчиков, требуется примерно 8000 байт памяти.
В ESP8286 есть свободных примерно 40 000 байт, хватает и на многозадачность и на буфер данных.
-----------------------------------
В итоге у меня получилась многозадачная система, которая может работать с сотней датчиков, что для ESP8286 вполне достойно.
 

pvvx

Активный участник сообщества
При реализации различных протоколов работы с внешними устройствами особенно такого протокола каr 1-wire далее I2c и т д
возникает проблема необходимости ожидания процессором сигнала на шине интерфейса или формирования определенной длительности импульса.
Это неправильно спроектированная и разработанная система. Есть аппаратные драйвера 1-wire. К примеру с управлением по I2C. А у нормальных MCU работа I2C происходит по DMA и прерываниям.
Что там ожидать? События приема-передачи на прерывании и тут-же отработки?
То-то я и смотрю, что ваши графики измерения тока с INA219 и подобными имеют большой шаг между точками, а не микросек...
 

pvvx

Активный участник сообщества
@nikolz Начните с простейшего – что такое реал-тайм система и каково её время реакции на любое событие. На ESP главное событие – WiFi и обслуживание никаких других событий они не могут обеспечить в рамках сотен ms. Иначе происходит нарушение выдачи beacon (сдвиг во времени его передачи и некорректные значения timestamp в нем), что грозит увеличением потребления у всех деток AP в спящем режиме, не говоря уже о пропусках... Этим страдают все SoC cинтегрированным WiFi, если нет распределения дров WiFi по ядрам CPU. Из-за этого лучше использовать внешний чип WiFi.
 
Сверху Снизу