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

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Покритикуйте реализацию простейшего шедулера на C++, Разработано для 8ми битных контроллеров AVR
sigmaN
сообщение Feb 25 2017, 18:20
Сообщение #1


I WANT TO BELIEVE
******

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



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

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

В архиве проект Atmel Studio 7.0 с минимальной демонстрацией, можно погонять в симуляторе) Прикрепленный файл  TinyScheduler_ver5_Loki_.zip ( 175.14 килобайт ) Кол-во скачиваний: 53

Для тех у кого нет Atmel Studio вот ссылка на исходник шедулера http://pastebin.com/7YVSAFBn

Читайте комментарии в шапке файла. Там все подробности использования и настройки.


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
Herz
сообщение Feb 25 2017, 20:32
Сообщение #2


Гуру
******

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



Цитата(sigmaN @ Feb 25 2017, 20:20) *
В общем выкладываю с целью получения обратной связи.

Неконсистентно. Тогда уже следует ожидать "фидбэка".
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 25 2017, 21:37
Сообщение #3


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
zltigo
сообщение Feb 26 2017, 05:24
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(sigmaN @ Feb 25 2017, 20:20) *
Основным требованием при реализации был минимальный оверхед во всем.

Очень не похоже, что основное требование выполнено, например, крутить свой счетчик для каждой из задач идея сама по себе редко когда удачная. Ну и я понимаю, что оптимизация великая сила, и наверняка сделает все возможное, но писать такое:
Код
timers[i] = (timers[i]>0)? (timers[i]-1):(timers[i]);

это как бы уже перебор.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
Эдди
сообщение Feb 26 2017, 07:08
Сообщение #5


Знающий
****

Группа: Участник
Сообщений: 825
Регистрация: 16-04-15
Из: КЧР, Нижний Архыз
Пользователь №: 86 250



[offtop]
Скедулер, если что.
[/offtop]
Go to the top of the page
 
+Quote Post
Herz
сообщение Feb 26 2017, 07:45
Сообщение #6


Гуру
******

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



Цитата(Эдди @ Feb 26 2017, 09:08) *
[offtop]
Скедулер, если что.
[/offtop]

Вот-вот. Мы сейчас вступим в дискуссию о том, как писать правильно асе эти англо-термины русскими буквами...
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 26 2017, 10:35
Сообщение #7


I WANT TO BELIEVE
******

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



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

Цитата
timers[i] = (timers[i]>0)? (timers[i]-1):(timers[i]);
это как бы уже перебор.

Ну у меня 0 в счетчике это признак запуска задачи, так что проверять на 0 приходится.
Раньше было
Код
for(uint8_t i=0; i < TaskListLen; i++)
        {
            if( timers[i] > 0 )
              --timers[i];
        }
Это было на целых 2 байта длиннее! При прочих равных настройках оптимизации и т.д. Я, так сказать, подчинился основной цели и выбрал сэкономить 2 байта флэша путем ухудшения читабельности кода.

P.S.
Я ночью писал двольно длинную шапку коментов на английском, слушал выступления Скотта Майерса на CPPCon и в момент публицации этого вопроса на форуме у меня была полная голова фич, оверхеда и шедулеров ))))


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
zltigo
сообщение Feb 26 2017, 11:09
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(sigmaN @ Feb 26 2017, 12:35) *
Я вот сейчас немного подумал и.... всё равно не придумал как можно сделать оптимальнее. Как мне кажется во всех вариантах будет еще один байт.

Про "еще один байт" не понял. Если у Вас цель какие-то байты экономить, а не такты, то так и напишите.
Для, например, 256 задач (реальный случай во времена i8080 2MHz) крутить 256 счетчиков - весь пар свисток уйдет. Да и не меньших количествах уже не подарок.
Такие тривиальные вещи вообще незачем делать "универсальными", если стоит задача сэкономить все и вся. Правильнее писать по месту, что для подобных вырожденных случаев ничуть не сложнее.
Цитата
Раньше было
if( timers[i] > 0 )
--timers[i];
Это было на целых 2 байта длиннее! При прочих равных настройках оптимизации и т.д.

Не верю! Если, вдруг, не одно и тоже, то первым шагом борьбы за что либо, следует выкинуть используемый компилятор.
А самое читабельное:
Код
if( timers[i] )
    timers[i]--;


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 26 2017, 12:47
Сообщение #9


I WANT TO BELIEVE
******

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



Цитата
Про "еще один байт" не понял. Если у Вас цель какие-то байты экономить, а не такты, то так и напишите.
Ну вообще в данный момент да, основа это байты. Ибо AVR Tiny, задачи две - три...ну пять в худший день.


Цитата
Для, например, 256 задач (реальный случай во времена i8080 2MHz) крутить 256 счетчиков - весь пар свисток уйдет.
Полностью согласен. Надеюсь поделитесь алгоритмом для такого случая? Интересно. Предполагаю, что надо крутить один счетчик в прерывании, а в задаче хранить переменную со значением счётчика при котором надо срабатывать.
Потом в RunScheduled() брать текущее значение счетчика, проходиться по задачам и сравнивать. После выполнения задачи переменную увеличивать вперед на период.
Я правильно мыслю или есть более оптимальный вариант? Просто так получается да, такты прерывания мы экономим, но имеем кучу сравнений(и сравнений не с константами) в RunScheduled().

Цитата
Такие тривиальные вещи вообще незачем делать "универсальными"
Согласен, но я тут просто добрался до шаблонов в С++ и конечно же страдаю "переиспользованием". Но всё равно полезно )


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

Код
for(uint8_t i=0; i < TaskListLen; i++)    
    timers[i] = (timers[i]>0)? (timers[i]-1):(timers[i]);            
Program Memory Usage     :    160 bytes   15,6 % Full
Data Memory Usage         :    2 bytes   3,1 % Full
{
00000019  PUSH R1        Push register on stack
0000001A  PUSH R0        Push register on stack
0000001B  IN R0,0x3F        In from I/O location
0000001C  PUSH R0        Push register on stack
0000001D  CLR R1        Clear Register
0000001E  PUSH R24        Push register on stack
    RELOAD_TCNT0();    
0000001F  LDI R24,0x82        Load immediate
00000020  OUT 0x32,R24        Out to I/O location
--- D:\Poligon\Scheduler\TinyScheduler\TinyScheduler\Debug/.././TinyScheduler.hpp
            timers[i] = (timers[i]>0)? (timers[i]-1):(timers[i]);            
00000021  LDS R24,0x0060        Load direct from data space
00000023  CPSE R24,R1        Compare, skip if equal
00000024  SUBI R24,0x01        Subtract immediate
00000025  STS 0x0060,R24        Store direct to data space
00000027  LDS R24,0x0061        Load direct from data space
00000029  CPSE R24,R1        Compare, skip if equal
0000002A  SUBI R24,0x01        Subtract immediate
0000002B  STS 0x0061,R24        Store direct to data space
--- D:\Poligon\Scheduler\TinyScheduler\TinyScheduler\Debug/.././main.cpp -------
}
0000002D  POP R24        Pop register from stack
0000002E  POP R0        Pop register from stack
0000002F  OUT 0x3F,R0        Out to I/O location
00000030  POP R0        Pop register from stack
00000031  POP R1        Pop register from stack
00000032  RETI         Interrupt return


Предложенный вами вариант:
Код
for(uint8_t i=0; i < TaskListLen; i++)            
    if( timers[i] )
        timers[i]--;
Program Memory Usage     :    164 bytes   16,0 % Full
Data Memory Usage         :    2 bytes   3,1 % Full
{
00000019  PUSH R1        Push register on stack
0000001A  PUSH R0        Push register on stack
0000001B  IN R0,0x3F        In from I/O location
0000001C  PUSH R0        Push register on stack
0000001D  CLR R1        Clear Register
--- D:\Poligon\Scheduler\TinyScheduler\TinyScheduler\Debug/.././main.cpp -------
0000001E  PUSH R24        Push register on stack
    RELOAD_TCNT0();    
0000001F  LDI R24,0x82        Load immediate
00000020  OUT 0x32,R24        Out to I/O location
--- D:\Poligon\Scheduler\TinyScheduler\TinyScheduler\Debug/.././TinyScheduler.hpp
            if( timers[i] )
00000021  LDS R24,0x0060        Load direct from data space
00000023  TST R24        Test for Zero or Minus
00000024  BREQ PC+0x04        Branch if equal
                timers[i]--;
00000025  SUBI R24,0x01        Subtract immediate
00000026  STS 0x0060,R24        Store direct to data space
            if( timers[i] )
00000028  LDS R24,0x0061        Load direct from data space
0000002A  TST R24        Test for Zero or Minus
0000002B  BREQ PC+0x04        Branch if equal
                timers[i]--;
0000002C  SUBI R24,0x01        Subtract immediate
0000002D  STS 0x0061,R24        Store direct to data space
--- D:\Poligon\Scheduler\TinyScheduler\TinyScheduler\Debug/.././main.cpp -------
}
0000002F  POP R24        Pop register from stack
00000030  POP R0        Pop register from stack
00000031  OUT 0x3F,R0        Out to I/O location
00000032  POP R0        Pop register from stack
00000033  POP R1        Pop register from stack
00000034  RETI         Interrupt return
получилось даже 4 байта, а не 2.
Компилятор:
Цитата
Invoking: AVR8/GNU C Compiler : 4.9.2
"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-g++.exe" -funsigned-char -funsigned-bitfields -DDEBUG -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.1.102\include" -I"../include" -Os -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -g2 -Wall -mmcu=attiny13a -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.1.102\gcc\dev\attiny13a" -c -fno-threadsafe-statics -fno-exceptions -fno-rtti -MD -MP -MF "main.d" -MT"main.d" -MT"main.o" -o "main.o" ".././main.cpp"


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
zltigo
сообщение Feb 26 2017, 17:24
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(sigmaN @ Feb 26 2017, 14:47) *
Код
            timers[i] = (timers[i]>0)? (timers[i]-1):(timers[i]);            
00000021  LDS R24,0x0060        Load direct from data space
00000023  CPSE R24,R1        Compare, skip if equal
00000024  SUBI R24,0x01        Subtract immediate
00000025  STS 0x0060,R24        Store direct to data space
00000027  LDS R24,0x0061        Load direct from data space
00000029  CPSE R24,R1        Compare, skip if equal
0000002A  SUBI R24,0x01        Subtract immediate
0000002B  STS 0x0061,R24        Store direct to data space



Древний IAR компилятор, какой уж был по руками, уложился на одну комаду меньше, если Вы хотели объем минимизировать.

Код
                           if( timers[i] ) timers[i]--;          
   \   00000000   ....               LDI     R30, timers
   \   00000002   0FE0               ADD     R30, R16
   \   00000004   8100               LD      R16, Z
   \   00000006   2300               TST     R16
   \   00000008   F011               BREQ    ??TimerISR_0
   \   0000000A   950A               DEC     R16
   \   0000000C   8300               ST      Z, R16


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 26 2017, 17:41
Сообщение #11


I WANT TO BELIEVE
******

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



Ладно, Бог с ними с компиляторами. Вы алгоритмом проворачивания 256 задач не поделитесь?


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
zltigo
сообщение Feb 26 2017, 20:10
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(sigmaN @ Feb 26 2017, 14:47) *
Предполагаю, что надо крутить один счетчик в прерывании

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

Куча сравнений подается оптимизации и, главное, это уже происходит не каждый тик таймера, а по завершении очередной задачи. Причем по завершении есть вероятность, что вообще времени девать некуда, ибо ни одна из задач запуска не ждет. Кроме всего этого просто так взять первую попавшуюся задачу и запустить по причине того, что счетчик до 0 досчитал, скорее всего бессмысленно - надо таки с приоритетами разбираться, а это опять "страшные" сравнения.
Цитата(sigmaN @ Feb 26 2017, 19:41) *
Вы алгоритмом проворачивания 256 задач не поделитесь?

Там была очень специфичная система под 256 одинаковых задач. Но счетчики в прерывании не крутились. Причины выше.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 26 2017, 23:51
Сообщение #13


I WANT TO BELIEVE
******

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



Ну да, не крутить счетчики в прерывании это хорошая идея biggrin.gif
Переделал RunScheduled() на:
Код
if( static_cast<TimerT>((TimeService::GetTickCount() - timers[I])) >= Task::Period )
            {
                Task::Run();
                timers[I] = TimeService::GetTickCount();
            }

Т.е. в массиве timers[] теперь хранится время последнего запуска задачи.
На тестовом примере и по тактам и по байтам получилось очень даже хорошо! 08.gif

Правда, на AVR при частоте 8МГц мне аппаратный счетчик не удалось заставить работать с миллисекундной частотой и если нужны многомиллисекундные задержки то в каждой задаче дополнительный делитель делать придется.

Но даже когда я сделал миллисекундное прерывание в котором крутится софтовый счетчик
Код
static volatile uint8_t G_TimerTicks;
static uint8_t GetTickCount()
    {         
        return G_TimerTicks;
    };
ISR(TIM0_COMPA_vect)
{
    G_TimerTicks++;    
}
результат всё равно превзошел предыдущий и по байтам(на 4байта) и тем более по тактам!

Я в общем так сосредоточился на шаблонах и рекурсии, что слона в упор не заметил )))))))


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Feb 28 2017, 21:26
Сообщение #14


I WANT TO BELIEVE
******

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



Делюсь окончательным вариантом с учетом замечаний.

Сам планировщик:
http://pastebin.com/RqVLaFrJ

Проект Atmel Studio 7 для тестов и веселья Прикрепленный файл  TinyScheduler_ver6_ElecronixEDIT_.zip ( 174.88 килобайт ) Кол-во скачиваний: 40


P.S.
GCC опять веселит. Если собрать проект с оптимизацией -O1 то он почему-то окажется на 4 байта меньше, чем собранный с оптимизацией по размеру -Os


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Feb 28 2017, 22:09
Сообщение #15


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

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



Цитата(sigmaN @ Mar 1 2017, 02:26) *
Делюсь окончательным вариантом с учетом замечаний.

А вот если бы это была не ссылка на pastebin, а ссылка на репозиторий на github, то вместо того, чтобы перечитывать заново весь исходник, можно было бы посмотреть diff...
Это так, намёкsm.gif


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post

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

 


RSS Текстовая версия Сейчас: 23rd June 2025 - 00:51
Рейтинг@Mail.ru


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