sigmaN 0 25 февраля, 2017 Опубликовано 25 февраля, 2017 · Жалоба Всем привет. Вот тут на днях допилил свой Tiny таск шедулер. Основным требованием при реализации был минимальный оверхед во всем. Однако есть возможность конфигурировать его под разные задачи, опять же без оверхеда. Хотелось бы услышать конструктивную критику, возможно предложение каких-то фич новых... В общем выкладываю с целью получения обратной связи. В архиве проект Atmel Studio 7.0 с минимальной демонстрацией, можно погонять в симуляторе) TinyScheduler_ver5_Loki_.zip Для тех у кого нет Atmel Studio вот ссылка на исходник шедулера http://pastebin.com/7YVSAFBn Читайте комментарии в шапке файла. Там все подробности использования и настройки. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Herz 5 25 февраля, 2017 Опубликовано 25 февраля, 2017 · Жалоба В общем выкладываю с целью получения обратной связи. Неконсистентно. Тогда уже следует ожидать "фидбэка". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 25 февраля, 2017 Опубликовано 25 февраля, 2017 · Жалоба Не поверите, написал фидбэка и исправил, вспомнив про вашу любовь к великому и могучему. Но фичи исправить не подумал. Ожидаю комьюнити фидбэка ))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 1 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба Основным требованием при реализации был минимальный оверхед во всем. Очень не похоже, что основное требование выполнено, например, крутить свой счетчик для каждой из задач идея сама по себе редко когда удачная. Ну и я понимаю, что оптимизация великая сила, и наверняка сделает все возможное, но писать такое: timers[i] = (timers[i]>0)? (timers[i]-1):(timers[i]); это как бы уже перебор. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Eddy_Em 1 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба [offtop] Скедулер, если что. [/offtop] Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Herz 5 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба [offtop] Скедулер, если что. [/offtop] Вот-вот. Мы сейчас вступим в дискуссию о том, как писать правильно асе эти англо-термины русскими буквами... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба крутить свой счетчик для каждой из задач идея сама по себе редко когда удачная. Я вот сейчас немного подумал и.... всё равно не придумал как можно сделать оптимальнее. Как мне кажется во всех вариантах будет еще один байт. timers = (timers>0)? (timers-1):(timers); это как бы уже перебор. Ну у меня 0 в счетчике это признак запуска задачи, так что проверять на 0 приходится. Раньше было for(uint8_t i=0; i < TaskListLen; i++) { if( timers[i] > 0 ) --timers[i]; } Это было на целых 2 байта длиннее! При прочих равных настройках оптимизации и т.д. Я, так сказать, подчинился основной цели и выбрал сэкономить 2 байта флэша путем ухудшения читабельности кода. P.S. Я ночью писал двольно длинную шапку коментов на английском, слушал выступления Скотта Майерса на CPPCon и в момент публицации этого вопроса на форуме у меня была полная голова фич, оверхеда и шедулеров )))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 1 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба Я вот сейчас немного подумал и.... всё равно не придумал как можно сделать оптимальнее. Как мне кажется во всех вариантах будет еще один байт. Про "еще один байт" не понял. Если у Вас цель какие-то байты экономить, а не такты, то так и напишите. Для, например, 256 задач (реальный случай во времена i8080 2MHz) крутить 256 счетчиков - весь пар свисток уйдет. Да и не меньших количествах уже не подарок. Такие тривиальные вещи вообще незачем делать "универсальными", если стоит задача сэкономить все и вся. Правильнее писать по месту, что для подобных вырожденных случаев ничуть не сложнее. Раньше было if( timers > 0 ) --timers; Это было на целых 2 байта длиннее! При прочих равных настройках оптимизации и т.д. Не верю! Если, вдруг, не одно и тоже, то первым шагом борьбы за что либо, следует выкинуть используемый компилятор. А самое читабельное: if( timers[i] ) timers[i]--; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба Про "еще один байт" не понял. Если у Вас цель какие-то байты экономить, а не такты, то так и напишите. Ну вообще в данный момент да, основа это байты. Ибо 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" Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 1 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба Ладно, Бог с ними с компиляторами. Вы алгоритмом проворачивания 256 задач не поделитесь? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 1 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба Предполагаю, что надо крутить один счетчик в прерывании В микроконтролерах непременно есть счетчики, которые вообще крутить не надо. Аппаратные. То есть отпадает целый кусок кода, который вызыватся каждый тик. Просто так получается да, такты прерывания мы экономим, но имеем кучу сравнений.... Куча сравнений подается оптимизации и, главное, это уже происходит не каждый тик таймера, а по завершении очередной задачи. Причем по завершении есть вероятность, что вообще времени девать некуда, ибо ни одна из задач запуска не ждет. Кроме всего этого просто так взять первую попавшуюся задачу и запустить по причине того, что счетчик до 0 досчитал, скорее всего бессмысленно - надо таки с приоритетами разбираться, а это опять "страшные" сравнения. Вы алгоритмом проворачивания 256 задач не поделитесь? Там была очень специфичная система под 256 одинаковых задач. Но счетчики в прерывании не крутились. Причины выше. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 26 февраля, 2017 Опубликовано 26 февраля, 2017 · Жалоба Ну да, не крутить счетчики в прерывании это хорошая идея Переделал RunScheduled() на: if( static_cast<TimerT>((TimeService::GetTickCount() - timers[I])) >= Task::Period ) { Task::Run(); timers[I] = TimeService::GetTickCount(); } Т.е. в массиве timers[] теперь хранится время последнего запуска задачи. На тестовом примере и по тактам и по байтам получилось очень даже хорошо! :08: Правда, на AVR при частоте 8МГц мне аппаратный счетчик не удалось заставить работать с миллисекундной частотой и если нужны многомиллисекундные задержки то в каждой задаче дополнительный делитель делать придется. Но даже когда я сделал миллисекундное прерывание в котором крутится софтовый счетчик static volatile uint8_t G_TimerTicks; static uint8_t GetTickCount() { return G_TimerTicks; }; ISR(TIM0_COMPA_vect) { G_TimerTicks++; } результат всё равно превзошел предыдущий и по байтам(на 4байта) и тем более по тактам! Я в общем так сосредоточился на шаблонах и рекурсии, что слона в упор не заметил ))))))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 28 февраля, 2017 Опубликовано 28 февраля, 2017 · Жалоба Делюсь окончательным вариантом с учетом замечаний. Сам планировщик: http://pastebin.com/RqVLaFrJ Проект Atmel Studio 7 для тестов и веселья TinyScheduler_ver6_ElecronixEDIT_.zip P.S. GCC опять веселит. Если собрать проект с оптимизацией -O1 то он почему-то окажется на 4 байта меньше, чем собранный с оптимизацией по размеру -Os Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 28 февраля, 2017 Опубликовано 28 февраля, 2017 · Жалоба Делюсь окончательным вариантом с учетом замечаний. А вот если бы это была не ссылка на pastebin, а ссылка на репозиторий на github, то вместо того, чтобы перечитывать заново весь исходник, можно было бы посмотреть diff... Это так, намёк:) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться