Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: "Оптимизация" в WinAVR и как с этим бороться
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Страницы: 1, 2
MaxiMuz
Перейдя с ассемблера на WinAVR, невольно стал обращать на размер кода. Когда стал разбираться в конкретике генерируемого кода , хваленный на многих сайтах WinAVR обнажил свое несовершенство.
Короче, программка :
Код
#include <avr/io.h>
#include <inttypes.h>

#define Btn1 PB2
#define Btn2 PB3

#define  KeyMask (1<<Btn1)|(1<<Btn2)
#define sbi(p,b) (p |= (1<<b)) //Установить бит

volatile register int8_t Cnt asm("r19");    // фоновый счетчик
volatile register int8_t a asm ("r16");

int main (void)
{
asm("nop");
a = 1;

while(1)
{
if (~(PINB)&(KeyMask))
    {
    a |= 0x01;
    }
if ((--Cnt)==0)
    {
    if (a==1 )
        {
        a=0;
        sbi (PINB,PB2);
        }
    }
}
}

Соответственно , полученный код:
Код
a = 1;
     ldi    r16, 0x01

while(1)
{
[color="#FF00FF"]if (~(PINB)&(KeyMask))
     in     r24, 0x16
     ldi    r25, 0x00
     com    r24
     com    r25
     andi    r24, 0x0C
     andi    r25, 0x00
     or     r24, r25
     breq    .+2[/color]
{
a |= 0x01;
}
     ori    r16, 0x01

[color="#FF0000"]if ((--Cnt)==0)
     mov    r24, r19
     subi    r24, 0x01
     mov    r19, r24[/color]
     brne    .-26    
{
    if (a==1 )
     cpi    r16, 0x01
     brne    .-30
{
        a=0;
     ldi    r16, 0x00

sbi (PINB,PB2);
     sbi    0x16, 2
     rjmp    .-36

Конструкцию if ((--Cnt)==0) выделенную крас.цветом может заменить всего одна машинная команда: dec r19!
Вопрос: как заставить компилятор это делать ?

Во втором случае: if (~(PINB)&(KeyMask)) , невижу смысла во втором парном регистре r25. Можно использовать "tst r24". Можно как нибудь с этим бороться ?!
Не хочеться чтобы потом код получался неоправданно раздутым!
Может быть стоит подождать другую версию компилятора (пользуюсь WinAVR-20100110) или уже пререходить на совсем другой ?
Палыч
Цитата(MaxiMuz @ Nov 16 2011, 22:07) *
хваленный на многих сайтах WinAVR обнажил свое несовершенство.
В большенстве трансляторов с ЯВУ можно найти "несовершенство"...

Цитата(MaxiMuz @ Nov 16 2011, 22:07) *
Во втором случае: if (~(PINB)&(KeyMask)) , невижу смысла во втором парном регистре r25. Можно использовать "tst r24". Можно как нибудь с этим бороться ?!
А, вот тут - "неча на зеркало пенять..." (т.е. не нужно "мешать" восьми- и шестнадцатибитные операнды).
MaxiMuz
Цитата(Палыч @ Nov 16 2011, 21:45) *
А, вот тут - "неча на зеркало пенять..." (т.е. не нужно "мешать" восьми- и шестнадцатибитные операнды).

а где вы увидели 16ти битные операнды ?
PINB читается как 8ми битные
KeyMask тоже 8мь бит !
AHTOXA
Цитата(MaxiMuz @ Nov 17 2011, 00:07) *
Перейдя с ассемблера на WinAVR, невольно стал обращать на размер кода.

И зря. Если код помещается в чип и успевает сделать то, что должен, то какая разница, какого он размера?
Цитата(MaxiMuz @ Nov 17 2011, 00:07) *
Конструкцию if ((--Cnt)==0) выделенную крас.цветом может заменить всего одна машинная команда: dec r19!

Вам же написали, что volatile register писать нельзя. Какие претензии?
Что касается второго, то целочисленные константы по умолчанию имеют тип int. Попробуйте написать if (~(PINB)&((unsigned char)KeyMask)).

ЗЫ. Выделение цветом не работает в блоке code, если сильно надо, то пользуйтесь блоком codebox - там вроде работает.
Палыч
Цитата(MaxiMuz @ Nov 16 2011, 23:19) *
KeyMask тоже 8мь бит !
Почему? Обоснуйте.
MaxiMuz
Цитата(Палыч @ Nov 16 2011, 23:10) *
Почему? Обоснуйте.

смотрите по коду, предпоследния команда:
or r24, r25
я не думаю что старший байт с младшим компилятор сталбы склеивать !
Кстати , можно проверить по присваиванию
aaarrr
Цитата(MaxiMuz @ Nov 17 2011, 01:06) *
or r24, r25
я не думаю что старший байт с младшим компилятор сталбы склеивать !

Это обычная проверка пары регистров на 0.
ReAl
Цитата(MaxiMuz @ Nov 16 2011, 20:07) *
Код
#define  KeyMask (1<<Btn1)|(1<<Btn2)
KeyMask - результат вычисления выражения (пусть и в compile-time).
По стандарту языка С имеет тип как минимум int.
Дальше у gcc недооптимизация, спору нет. Но он обязан был сначала всё делать в int-ах и только потом натравить оптимизатор.
Genadi Zawidowski
Код
#define  KeyMask (1<<Btn1)|(1<<Btn2)


А скобочки для защиты от подводных граблей не хотите поставить?

Назначение регистров для переменных ОЧЕНЬ сильно мешает оптимизации.
neiver
Мда, можно вытащить ассемблерщика из ассемблера, но...
По поводу регистровых переменных. В них практически никогда нет реальной необходимости, особенно в достаточно сложных программах. Мнимая выгода из-за отсутствий-загрузок выгрузок из/в память, с лихвой перекрываются необходимостью тасовать и сохранять регистры, например, для вызова функций и тупяками компилятора из-за того, что он не может использовать регистры, занятые под эти переменные, так как умеет. Модификатор volatile с регистровыми переменными, как уже сказали, не работает. Если возникает реальзая ситуация, когда программа относительно проста и нехватает нескользих тактов для выполнения критического участка, то наверное имеет смысл переписать этот критический участок, а то и всю программу на ассемблере.
Код
if (~(PINB)&(KeyMask))

В Си есть такая штука - целочисленное расширение называется (integer propagation). Рекомендую погуглить и почитать, что это такое, многоя станет понятно.

Палыч
Цитата(neiver @ Nov 17 2011, 12:19) *
integer propagation

Integer promotion
Но, в данном случае - это не оно. Имеет место банальное использование в выражении операндов двух разных типов, "меньший" из которых приводится к "большему".
MaxiMuz
Цитата(aaarrr @ Nov 17 2011, 00:19) *
Это обычная проверка пары регистров на 0.

Как это компилятор резервирует старший байт , а потом проверяет младший со старшим ?
Как то не логично получается !

AHTOXA, Пробывал я выражение : if (~(PINB)&((unsigned char)KeyMask))
- Результат абсолютно тот же, и скобки как говорили в #define я поставил, один хрен !

Попробывал предварительное присваивание константы в регистр :
Код
volatile register int8_t Cnt asm("r19");    // фоновый счетчик
volatile register int8_t a asm ("r16");
volatile register int8_t b asm ("r17");

int main (void)
{
asm("nop");
a = 1;
b=KeyMask;

while(1)
{
if (~(PINB)&(b))
    {
    a |= 0x01;
    }
if ((--Cnt)==0)
    {
    if (a==1 )
        {
        a=0;
        b |= 0x08;
        sbi (PINB,PB2);
        }
    }
}
}

Тут Вообще "Оптимизатор" показал чудеса !!! sm.gif

Цитата(Genadi Zawidowski @ Nov 17 2011, 04:30) *
Назначение регистров для переменных ОЧЕНЬ сильно мешает оптимизации.

Если рассуждать как вы , то можно сказать и любая программа написанная в Си мешает оптимизации!!!
Вы еще бы посоветовали на Ассемблере писать sm.gif

Цитата(AHTOXA @ Nov 16 2011, 22:58) *
И зря. Если код помещается в чип и успевает сделать то, что должен, то какая разница, какого он размера?

А если не помещается, и делает с большими задержками по времяни ?


Цитата(AHTOXA @ Nov 16 2011, 22:58) *
ЗЫ. Выделение цветом не работает в блоке code, если сильно надо, то пользуйтесь блоком codebox - там вроде работает.

Кстати , а что за блок codebox ?
я про него ничего не слышал

Во втором случае, все просто ! sm.gif Я заранее знал решение, и хотел посмотреть что вы скажите (может чего новое)
Проблемка решается заменой конструкции:
if (~(PINB)&(KeyMask))

на несколько команд:
b=~(PINB);
b&=KeyMask;
if (cool.gif
{ ......}

Код сокращается всего лишь до:
Код
IN      R24,0x16
COM     R24          
ANDI    R24,0x0C
BREQ    PC+0x02


А вот с первой проблемой мусорных присваиваний , похоже все мрачно ! sad.gif
Палыч
Цитата(MaxiMuz @ Nov 17 2011, 19:19) *
... похоже все мрачно !
Если Вы считали, что компилятор выдаст точную копию Вашей программы на ассемблере, то, разочарую Вас, - такого не будет: компилятор всё-таки действует по некоему шаблону, что не всегда даёт "хороший" код. Иногда компилятору можно в его работе помочь. Atmel по этому поводу выпустил AppNote AVR035.
AHTOXA
Цитата(MaxiMuz @ Nov 17 2011, 21:19) *
А если не помещается, и делает с большими задержками по времяни ?
Вот когда упрётесь в такую ситуацию, тогда и начинайте оптимизацию sm.gif
Цитата(MaxiMuz @ Nov 17 2011, 21:19) *
Кстати , а что за блок codebox ?
я про него ничего не слышал

У меня он слева, под смайликами. Хотя можно и просто руками поменять [соde]lalala[/соde] на [соdebox]lalala[/соdebox]. Хотя это наверное зависит от применяемого редактора (правая верхняя кнопка : ).
neiver
Я к чему, тут про целочисленное расширение намекаю? Компилятор в данном случае делает ровно, то, что ему сказано. В вашем выражении нужна не битовая инверсия, а логическая:
Код
#define  KeyMask ((1<<Btn1)|(1<<Btn2))
...
if( ! (PINB & KeyMask) )
{
        ...
}

Посмотрите, и почувствуйте разницу.
_Pasha
Цитата(MaxiMuz @ Nov 17 2011, 19:19) *
Код сокращается всего лишь до:

Так не полагайтесь на CSE там, где это не надо. laughing.gif
Поясню: если в выражении присутствует хоть одна volatile-переменная, она все равно испортит общую оптимизацию выражения. Надо ж думать, прежде чем писать!

И смысла в if (~(PINB)&(KeyMask)) много меньше, чем в if( ! (PINB & KeyMask) ). Ой, уже опередили.
MaxiMuz
neiver , _Pasha да действительно, можно и так написать, результат будут тотже что и в моем варианте ! С этим более менее понятно.
neiver
Цитата(MaxiMuz @ Nov 18 2011, 12:38) *
neiver , _Pasha да действительно, можно и так написать, результат будут тотже что и в моем варианте ! С этим более менее понятно.

Неа, результат другой:
Код
6c:    86 b3           in    r24, 0x16; 22
  6e:    8c 70           andi    r24, 0x0C; 12
  70:    09 f4           brne    .+2      ; 0x74 <main+0x8>

На ЦЕЛУЮ команду меньше sm.gif
MaxiMuz
Цитата(AHTOXA @ Nov 16 2011, 22:58) *
Вам же написали, что volatile register писать нельзя. Какие претензии?

Это неправильное утверждение! Если из выражения "volatile register int8_t a asm ("r16");" убрать квалификатор volatile, то компилятор воообще обходит использование указанных регистров, используя свои. И хотя код получается короче, неизвестно как дальще поведет себя "оптимизатор" , если программа усложниться !

Цитата(neiver @ Nov 18 2011, 11:44) *
Неа, результат другой:
Код
6c:    86 b3           in    r24, 0x16; 22
  6e:    8c 70           andi    r24, 0x0C; 12
  70:    09 f4           brne    .+2     ; 0x74 <main+0x8>

На ЦЕЛУЮ команду меньше sm.gif

На ОДНУ команду не считается sm.gif
Так исторически сложилось что я проверяю на нулевой рез-т (не нажата не одна кнопка) и зацикливаю в начало
Палыч
Цитата(MaxiMuz @ Nov 18 2011, 13:00) *
На ОДНУ команду не считается

Если Вы пытатесь заставить GCC генерить вместо if(!(PINB & KeyMask)) одну команду SBRS (или SBRC), то у Вас ничего не получится. Хотя Atmel и утверждает, что система команд AVR ориентированна на язык Си, то компиляторописатели так не считают... В Вашем первом и втором случаях компилятор действует по шаблону: 1) берёт значение на регистр r24 (то, что Вы расположили некую переменную в регистр - изменит только источник данных: значение будет взято из другого регистра или из ОЗУ): 2) на регистре r24 вычисляет выражение; 3) при необходимости, из регистра r24 сохраняет вычисленное выражение; 4) сравнивает значение выражения (регистр r24) с нулём... Только ну очень хороший оптимизатор заменит эту последовательность одной командой. GCC так сделать не сможет. Для AVR я не знаю компилятора, в который встроен настолько сильный оптимизатор, что сможет повторить Вашу программу на ассемблере.
Имхо, если компилятор с ЯВУ генерит код "съедающий" на 10-15% больше ресурсов, чем аналогичная программа на ассемблере, на сегодняшний день - это ОЧЕНЬ хороший компилятор. Заметьте - программа(!!!, причем обычно довольно большая), а не отдельный оператор.
neiver
GCC прекрасно может сгенерировать одну команду sbis (или sbiс), но не когда в маске установлены два бита:
#define KeyMask ((1<<Btn1)|(1<<Btn2))
Оставим обин бит и будет вам счастье:
Код
    if(!(PINB&KeyMask))
  6c:    b2 9b           sbis    0x16, 2; 22

А если в маске несколько единичных бит, то только честно сравнивать с нулем.
Палыч
Цитата(neiver @ Nov 18 2011, 15:07) *
GCC прекрасно может сгенерировать одну команду sbis (или sbiс), но не когда в маске установлены два бита
Да, согласен...
Я собирался написать об операторе if ((--Cnt)==0) и команде DEC, но обширное обсуждение в этой теме оператора if(!(PINB & KeyMask)) сбило с мысли...
Сергей Борщ
QUOTE (MaxiMuz @ Nov 18 2011, 12:00) *
Это неправильное утверждение!
Вдумайтесь, что вы написали. Авторы компилятора указывают, что использовать volatile и register совместно нельзя. Вы обвиняете их во лжи. Достаточно смело, но бесперспективно - во-первых авторы компилятора не читают этот форум, а во-вторых - ну, продолжайте бороться с ветряными мельницами. Напоминает FAQ про яйцо и микроволновку.
_Pasha
Цитата(Палыч @ Nov 18 2011, 13:59) *
Имхо, если компилятор с ЯВУ генерит код "съедающий" на 10-15% больше ресурсов, чем аналогичная программа на ассемблере, на сегодняшний день - это ОЧЕНЬ хороший компилятор. Заметьте - программа(!!!, причем обычно довольно большая), а не отдельный оператор.

ГЦЦ не умеет сливать ПП из разных точек входа в поток с одной командой возврата. Еще - бывают напряги при работе с битовыми полями, сильное юзание регистра Х в адресных выражениях, не использует бит Т и не "подглядывает" за функциями, вызываемыми из прерываний - длинный контекст сохраняется. Больше никаких шероховатостей мною не замечено. А большую программу на асме еще написать надо, чтобы "сделать" современный Си на эти 10-15%.
neiver
Цитата(_Pasha @ Nov 18 2011, 18:36) *
и не "подглядывает" за функциями, вызываемыми из прерываний - длинный контекст сохраняется.

Подглядывает, еще как если функция inline.
MaxiMuz
Цитата(Палыч @ Nov 18 2011, 13:59) *
Если Вы пытатесь заставить GCC генерить вместо if(!(PINB & KeyMask)) одну команду SBRS (или SBRC), то у Вас ничего не получится. .....

с этим место собственно я разобрался.

Цитата(_Pasha @ Nov 18 2011, 17:36) *
ГЦЦ не умеет сливать ПП из разных точек входа в поток с одной командой возврата. .....

Не понятно о чем вы говорите

Цитата(_Pasha @ Nov 18 2011, 17:36) *
...... А большую программу на асме еще написать надо, чтобы "сделать" современный Си на эти 10-15%.

большая - маленькая зависит от типа МК. Привожу конкретный пример:
Код
http://www.radio.ru/archive/2010/07/a19.shtml

программка писалась на асме под ATtiny2313, нехватило памяти программ под вторую таблицу мелодий для звонка. Если бы тогда писал на Си то вообще не хватилобы памяти на одну таблицу ! Это при том что я еще проводил оптимизацию программы в ассеблере sm.gif

Цитата(Палыч @ Nov 18 2011, 14:17) *
Да, согласен...
Я собирался написать об операторе if ((--Cnt)==0) и команде DEC, но обширное обсуждение в этой теме оператора if(!(PINB & KeyMask)) сбило с мысли...

Что хотели написать то ?
ReAl
Цитата(_Pasha @ Nov 18 2011, 16:36) *
ГЦЦ не умеет сливать ПП из разных точек входа в поток с одной командой возврата.
Ну, да.
Но иногда такие места удаётся оформить в маленькие подпрограмки, которые вызывать в конце (в вумных книгах по программированию давно пишут, что не стоит бояться мелких подпрограмок :-) ). Тогда вкупе с --relax и получается слияние.

Цитата(neiver @ Nov 18 2011, 16:45) *
Подглядывает, еще как если функция inline.
Вот еще до LTO нужно добраться, так там должно за всем подглядывать уметь. Вроде бы к 5-ой аврстудии компилятор идёт уже с LTO.
Klen-овые сборки под Linux для ARM дают неплохой эффект. Для AVR у меня не запустились, что-то с библиотеками не срослось.


Цитата(MaxiMuz @ Nov 18 2011, 18:06) *
Что хотели написать то ?
Возможно то, что без зауми с asm("r19") и прочими проявлениями детской болезни левизны dec сам бы появился, и заставлять не пришлось бы (ну тут не dec, но ни по размеру кода, ни по времени выполнения не отличается — возможно, и это сказать хотели):
Код
#include <avr/io.h>
#include <inttypes.h>

#define Btn1 PB2
#define Btn2 PB3

#define  KeyMask (1<<Btn1)|(1<<Btn2)

int main (void)
{
    uint8_t Cnt = 0;
    uint8_t a = 0;

    while(1) {
        if ( !(PINB & (KeyMask)) )
            a |= 0x01;
        
        if ((--Cnt)==0)  {
            if (a==1 ) {
                a=0;
                PINB |= (1 << 2);
            }
        }
    }
}

Код
.global    main
    .type    main, @function
main:
/* prologue: function */
/* frame size = 0 */
    ldi r18,lo8(0)
.L8:
    ldi r25,lo8(0)
.L7:
    in r24,54-32
    andi r24,lo8(12)
    brne .L2
    ori r25,lo8(1)
.L2:
    subi r18,lo8(-(-1))
    brne .L7
    cpi r25,lo8(1)
    brne .L7
    sbi 54-32,2
    rjmp .L8
demiurg_spb
Паша верно перечислил основные недочёты avr-gcc, сравнив видимо с IAR, ну так и всё же IAR коммерческий продукт.
И кому так важны эти последние 5-10 % упущенной выгоды используют IAR.
А большинству это не принципиально и оно это большинство получает кайф от гнутого софта (и это не шутка).
Топик-стартеру прежде всего стоит перестать считать себя умнее компилятора и просто методично читать доки по Си и по компилятору. Глядишь через годик-другой всё наладится в голове и все нынешние "претензии" будут восприниматься с улыбкой.
ЗЫЖ приводить приемеры как мол круто я подковал блоху в позапрошлом столетии - это по меньшей мере не актуально и никого давно уже не удивляет.
neiver
Цитата(ReAl @ Nov 18 2011, 20:17) *
Вот еще до LTO нужно добраться, так там должно за всем подглядывать уметь. Вроде бы к 5-ой аврстудии компилятор идёт уже с LTO.
Klen-овые сборки под Linux для ARM дают неплохой эффект. Для AVR у меня не запустились, что-то с библиотеками не срослось.

К сожалению, в avr-gcc LTO не работает. Компилятор к 5-ой аврстудии скомпилирован без поддержки LTO, я пытался скомпилировать avr-gcc сам со включеной LTO - не собрался, видимо чего-то в в AVR бекэнде не хватает.
ReAl
Жаль.
ReAl
Цитата(MaxiMuz @ Nov 18 2011, 18:06) *
большая - маленькая зависит от типа МК. Привожу конкретный пример:
Код
http://www.radio.ru/archive/2010/07/a19.shtml

программка писалась на асме под ATtiny2313, нехватило памяти программ под вторую таблицу мелодий для звонка. Если бы тогда писал на Си то вообще не хватилобы памяти на одну таблицу ! Это при том что я еще проводил оптимизацию программы в ассеблере sm.gif
Поверьте старому С-шнику, избегающему по мере возможности написания программ на асме -- Вам показалось.
Что Вы проводили оптимизацию этой ассемблерной программы.
Бегло просмотрел программу из архива, даже толком не вникая в алгоритмы. Просто мелкая местная оптимизация — и несколько десятков байт как с куста. А если ещё вникнуть в алгоритмы, то и того более найдётся.
Примеры:
Код
#ifndef OPTIMISED
     sbrc ModeR,BitModeAlrm
     rjmp Begin
     sbrc ModeR,BitModeWtch
     rjmp Begin
#else;;; -2
     sbrs ModeR,BitModeAlrm
     sbrc ModeR,BitModeWtch
     rjmp Begin
#endif
Там таких два места. На этой мелочи — уже 4 байта.

Любой уважающий себя С-компилятор покраснел бы от стыда за такой код:
Код
; Определение интервала 1
#ifndef OPTIMISED
; if ( MemdP <= Thr_AdP ) goto TCAdi11; else goto TCAdi12;
     cpi MemdPH,Thr_AdPH
     brcs TCAdi11
     brne TCAdi12
     cpi MemdPL,Thr_AdPL
     brcs TCAdi11
     brne TCAdi12
#else;;; -4 bytes
; if MemdP >= (Thr_AdP + 1) ) goto TCAdi12
     ldi temp, high( Thr_AdP + 1 )
     cpi MemdPL, low( Thr_AdP + 1)
     cpc MemdPH, temp
     brcc TCAdi12
#endif
Код
#ifndef OPTIMISED
; if ( MemdP <= DeltaT ) goto TCA12; esle goto TCA11;
     cp MemdPH,DeltaTH
     brcs TCA12
     brne TCA11
     cp MemdPL,DeltaTL
     brcs TCA12
     brne TCA11
#else
;;; -6
; if ( DeltaT >= MemdP ) goto TCA11;
     cp  DeltaTL,MemdPL
     cpc DeltaTH,MemdPH
     brcc TCA11
#endif
И таких мест там не одно.
Тут вообще что-то странное было.
CODE
;_________ Запись таблицы регистрации нажатий в EEPROM
;*
;*
CLI
push temp2
push ZL
push ZH
#ifndef OPTIMISED
clr ZL
clr ZH
b4: mov temp,ZL
ldi temp2,low(Edata1)
add temp,temp2
OUT EEAR,temp
ldi temp2,low(data1)
add ZL,temp2
LD temp,Z
sub ZL,temp2
OUT EEDR,temp
SBI EECR,EEMPE;
SBI EECR,EEPE
b5: sbic EECR,EEPE
rjmp b5
cpi ZL,MaxCount*2-1
breq b6
ld temp,Z+
rjmp b4
b6: ldi temp,low(EMrk1)
OUT EEAR,temp
ldi temp,0xab
OUT EEDR,temp
SBI EECR,EEMPE;
SBI EECR,EEPE

#else ;;; -14b

ldi ZL, low(data1)
clr ZH
ldi temp2, Edata1
b4:
ld temp, Z+
rcall WrEE
inc temp2
cpi temp2, MaxCount*2-1
brne b4
b6: ldi temp2,low(EMrk1)
ldi temp,0xab
rcall WrEE
#endif

pop ZH
pop ZL
pop temp2
SEI

m9:
CLR EnGen
clr count
m11:
;______ Задержка
ldi temp,ZDelayL
mov CL,temp
rcall DelayTime
Begin__:
rjmp Begin

#ifdef OPTIMISED
WrEE:
OUT EEAR,temp2
OUT EEDR,tempЯ
SBI EECR,EEMPE;
SBI EECR,EEPE
WrEEw: sbic EECR,EEPE
rjmp WrEEw
ret
#endif
Эта запись вообще дважды повторяется, в слегка разном окружении. Её всю в подпрограмму надо пустить.
Ещё tiny2313 есть регистры GPIOR, аж три штуки. Все флаги успешно летят туда без увеличения объёма кода с незначителным увеличением времени. Освобождается два регистра, причём "верхних". Явно где-то поможет сократить код.
Я бы постарался выделить регистровую пару Y под указатель на блок данных в DSEG, заменив все LDS/STS на LDD/STD. Ещё так на глаз минимум полсотни байт.
Напоминаю — это всё не особо вникая в суть кода.
MaxiMuz
Цитата(neiver @ Nov 18 2011, 20:23) *
К сожалению, в avr-gcc LTO не работает. Компилятор к 5-ой аврстудии скомпилирован без поддержки LTO, я пытался скомпилировать avr-gcc сам со включеной LTO - не собрался, видимо чего-то в в AVR бекэнде не хватает.

У меня к сожелению, не достаточно глубокие познания в тонкостях компиляторов и знания абревиатур, да и практики маловато. Хотел узнать , что такое LTO ?

Цитата(ReAl @ Nov 19 2011, 01:38) *
Поверьте старому С-шнику, избегающему по мере возможности написания программ на асме -- Вам показалось.
Что Вы проводили оптимизацию этой ассемблерной программы.
Бегло просмотрел программу из архива, даже толком не вникая в алгоритмы. Просто мелкая местная оптимизация — и несколько десятков байт как с куста. А если ещё вникнуть в алгоритмы, то и того более найдётся.
Примеры:
Код
#ifndef OPTIMISED
     sbrc ModeR,BitModeAlrm
     rjmp Begin
     sbrc ModeR,BitModeWtch
     rjmp Begin
#else;;; -2
     sbrs ModeR,BitModeAlrm
     sbrc ModeR,BitModeWtch
     rjmp Begin
#endif
Там таких два места. На этой мелочи — уже 4 байта.

Я уважаю людей которые хорошо разбираются в С-ях ! sm.gif
Да, действительно я не достаточно тщательно провел оптимизацию данной программы ! И в приведнном примере , я возможно проглядел, либо просто не стал обращать внимание на эти мелочи ...

п.с. а про код "записи в ЕЕПРОМ" я прекрасно знал , просто в этой программе не стал его выделять в отдельную процедуру.

Цитата(ReAl @ Nov 19 2011, 01:38) *
Любой уважающий себя С-компилятор покраснел бы от стыда за такой код:
Код
; Определение интервала 1
#ifndef OPTIMISED
; if ( MemdP <= Thr_AdP ) goto TCAdi11; else goto TCAdi12;
     cpi MemdPH,Thr_AdPH
     brcs TCAdi11
     brne TCAdi12
     cpi MemdPL,Thr_AdPL
     brcs TCAdi11
     brne TCAdi12
#else;;; -4 bytes
; if MemdP >= (Thr_AdP + 1) ) goto TCAdi12
     ldi temp, high( Thr_AdP + 1 )
     cpi MemdPL, low( Thr_AdP + 1)
     cpc MemdPH, temp
     brcc TCAdi12
#endif
Код
#ifndef OPTIMISED
; if ( MemdP <= DeltaT ) goto TCA12; esle goto TCA11;
     cp MemdPH,DeltaTH
     brcs TCA12
     brne TCA11
     cp MemdPL,DeltaTL
     brcs TCA12
     brne TCA11
#else
;;; -6
; if ( DeltaT >= MemdP ) goto TCA11;
     cp  DeltaTL,MemdPL
     cpc DeltaTH,MemdPH
     brcc TCA11
#endif
И таких мест там не одно.

Суть правильная, только ваш оптимизатор почемуто в метках переходов ошибся ! при условии С=0 должна выполняться какраз противоположная ветвь !
Вот так доверяй потом оптимизаторам !!
И в данном куске, почему я сначала старшие байты решил проверять : хотел ускорить алгоритм проверки. Хотя сейчас понимаю что это не к чему было.

И всеже вернусь к использованию регистровых глобальных перемнных. В теме:заголовок
"Второй раз повторяю, GCC не поддерживает volatile register переменные. Не
поддерживает - это значит что нет гарантии что любой код использующий
volatile register переменные будет работать при любом уровне оптимизации. К
сожалению варинг сообщающий об этом пропал где-то в 2005 году и если я
правильно понял его вернули в феврале 2008. Попробуйте откомпилировать
тестовый пример с "volatile register" GCC 4.4 с клюем -Wall. Или с ключом
-Wvolatile-register-var на 4.3.

Анатолий. "
были упомянуты ключики. Я не нашел в дока назначение этих ключей, плохо ориентируюсь описании WinAVR. Есть ли возможность отключить в опциях использование компилятором конкретных регистров ?
ReAl
Цитата(MaxiMuz @ Nov 20 2011, 12:55) *
Хотел узнать , что такое LTO ?
Link-Time-Optimiser
Зачатки его есть и в нынешнем линкере, ключ --relax. Это, например, превращение линкером длинного вызова в короткий. На этапе компиляции ещё неизвестно, насколько близко лягут куски из разных файлов и на больших кристаллах компилятром используется только длинный.
LTO делает полную оптимизацию, зная уже всё о линкуемой программе (включая устройство библиотечных функций, если они были скомпилировані с соо-тветствующим ключом).

Цитата(MaxiMuz @ Nov 20 2011, 12:55) *
Да,
действительно я не достаточно тщательно провел оптимизацию данной
программы ! И в приведнном примере , я возможно проглядел, либо просто
не стал обращать внимание на эти мелочи ...
Ну там дальше и не такие мелочи на cp/cpc набегают, как тут на пропусках.

Цитата(MaxiMuz @ Nov 20 2011, 12:55) *
п.с. а про код "записи в ЕЕПРОМ" я прекрасно знал , просто в этой программе не стал его выделять в отдельную процедуру.

И какой смысл после этого было говорить, что "на ассемблере влезла одна мелодия и то после оптимизации, а С и одна не влезла бы" ? ;-)
У С по длине выигрывает не любая ассемблерная реализация — только потому, что она ассемблерная biggrin.gif — а только действительно вылизанная.
Прикидочно, оценивая возможное ужатие Вашего кода байт на 150-200, — тут и С-шная реализация должна легко влезть.

Цитата(MaxiMuz @ Nov 20 2011, 13:58) *
Суть правильная, только ваш оптимизатор почемуто в метках переходов ошибся ! при условии С=0 должна выполняться какраз противоположная ветвь !
Ну там в одном из мест я для нужной нестрогости сравнивания поменял порядок операндов, мог забыть про смену полярности перехода. Это не важно, ошибки в коде любой длины бывают. Главное то, что код можно написать короче.
Там ещё ошибки есть, например, в одном из мест надо так:
Код
cpi    temp2, Edata1 + MaxCount*2-1
Это до меня уже в субботу утром, в метро по дороге на работу в дрёме дошло.
Отмазки:
1) Ну дак в пол второго ночи и наспех (вставать-то в шесть), да копипастом-редактированием (из головы я бы сразу правильно писал) и не такого наворотить можно
2) А С-компилятор и не ошибся бы с "полярностью" перехода.

Цитата(MaxiMuz @ Nov 20 2011, 13:58) *
Есть ли возможность отключить в опциях использование компилятором конкретных регистров ?
-ffixed-reg, например
-ffixed-reg=r2
Только нужно быть уверенным, что библиотека тоже этот регистр не использует.
MaxiMuz
Цитата(ReAl @ Nov 20 2011, 15:23) *
........................................
-ffixed-reg, например
-ffixed-reg=r2
Только нужно быть уверенным, что библиотека тоже этот регистр не использует.

После вставики в Makefile
Код
CFLAGS += -ffixed-reg=r16
CFLAGS += -ffixed-reg=r18

Компилятор выдал ошибку:
Код
cc1.exe: warning: unknown register name: reg=r16
cc1.exe: warning: unknown register name: reg=r18

Что я не так делаю ?
И где можно найти описание всех ключей компилятора GCC
demiurg_spb
Цитата(MaxiMuz @ Nov 23 2011, 15:30) *
где можно найти описание всех ключей компилятора GCC
Как всегда в гугле, забив в строке поиска:
gcc optimization options
AHTOXA
Код
CFLAGS    += -ffixed-r16
CFLAGS    += -ffixed-r18
ReAl
Тьху, ну конечно, -ffixed-reg
Что-то меня на = потянуло :-)

Цитата(demiurg_spb @ Nov 23 2011, 15:39) *
Как всегда в гугле, забив в строке поиска:
gcc optimization options
Если бы гугл умел посылать пользователя на его локальный диск в

file:///C:/WinAVR/doc/gcc/HTML/gcc-4.3.2/gcc/index.html

в частности,
file:///C:/WinAVR/doc/gcc/HTML/gcc-4.3.2/gcc/AVR-options.html
file:///C:/WinAVR/doc/gcc/HTML/gcc-4.3.2/gcc/Code-Gen-Options.html

а также

file:///C:/WinAVR/doc/avr-libc/avr-libc-user-manual/index.html

так он ещё бы трафик пользователю сэкономил.

___________________________
— А я так XP-шку до конца и не прошёл...
— Так это же не игрушка, а ОС
— Откуда инфа 8-O ???
MaxiMuz
Спасибо за ответы !

Опять я возвращаюсь к своим любимым регистровым глобальным переменным.
Вначале программы обычно происходит инициализация переменных. В моей программе три регистровых глобальных переменных. Одна из них обьявленна как битовая область, и с ней проблем нет. Две других как беззнаковый байт (см. программу). В месте задания им начальных значений, компилятор игнорировал эти операции, чтобы я не делал. Все уровни оптимизации , кроме -O0 исключали эти команды. Но, после перемещения блока инициализации в отдельную подпрограмму , компилятор всеже их заметил, и правильно откомпилировал! У меня возникла мысль, что какието ключи не позволяют оптимизатору игнорировать команды в подпрограммах.
Хотел бы услышать ваши предположения.

Код
#include <avr/io.h>
#include <avr/interrupt.h>  // задает макросы sei() , cli()
#include <inttypes.h>

//=====================================================================
// определение регистров:
volatile register uint8_t FLCnt asm("r16"); //фоновый счетчик длительности переключения свдиода
volatile register uint8_t DBKCnt asm("r18"); //фоновый счетчик для отсчета периода блокировки опроса кнопок
volatile register struct
{
  uint8_t bDbRKey : 1;
  uint8_t bFLed : 1;
} RFGP asm ("r17");

//;----------------------------------------------------------------------------------------------------------------------------------------
//=== Определение макросов
#define sbi(p,b) (p |= (1<<b)) //Установить бит
//;----------------------------------------------------------------------------------------------------------------------------------------
//Определение портов:
/* направление для порта В*/
#define DIRB 0b00010001
/* Pull-ups для порта В*/
#define PUPB 0b00000101

//;----------------------------------------------------------------------------------------------------------------------------------------
//Определение контактов
#define Control1 PB4 /* линия управления VT (1 - откр; 0 - закр ) */
#define Btn1 PB2 /* линия кнопки (на общ.пров) */
#define Led PB0 /* линия светодиода ("1" - вкл. ч/з резистор на общ.) */

//;----------------------------------------------------------------------------------------------------------------------------------------
/* длительность запрета на опрос клавиш t=(101-1)*0,1=10 sec */
#define Vl_DBKCnt 101

/* t=5*0,1=0,5 sec */
#define Vl_FLCnt 5

// Маска кнопок:"Btn"
#define  KeyMask (1<<Btn1)|(1<<1)


//=====================================================================
//=====================================================================
SIGNAL ( SIG_OUTPUT_COMPARE0A)
{//______ Моргание светодиода t=5*0,1=0,5 sec
if (!(--FLCnt))
{//__ Переключение свдиода
  FLCnt=Vl_FLCnt;// перезагрузка ф.счетчика              !!!!
  ~(RFGP.bFLed);
  sbi (PINB,Led);// ________ Переключение свдиода !
}

//______ Формирование запрета на опрос клавиш t=(101-1)*0,1=10 sec
if (RFGP.bDbRKey==1)
{ //__ проверка на первый запуск
   if (DBKCnt == 0 )
    DBKCnt=Vl_DBKCnt;
   --DBKCnt;//__ продолжение счета
   if (DBKCnt==0) // checking of =0
   { //__ сброс флага
     RFGP.bDbRKey=0;
     DBKCnt=Vl_DBKCnt;
   }
}
}

//=============================================================================
//=============================================================================
int main (void)
{
//_________________________ ИНИЦИАЛИЗАЦИЯ _____________________________
uint8_t temp;

PORTB=PUPB; //иницализация порта B
DDRB=DIRB; // задание направления для порта B
TIMSK0=(1<<OCIE0A); /* установка разр. прер-ия по совпадению т/сч.0 с регистром OCR0A */
OCR0A=234; //загрузка регистра совпадения OCR0A коэф. деления
TCCR0A= (1<<WGM01); //установка режима СТС - обнуление Т/С0 при совпадении с регистром OCR0A
TCCR0B=(1<<CS02)|(1<<CS00); // <---- конфигурация и запуск сч-ка в реж. СТС с предделителем ckl/1024
RFGP.bDbRKey=0;
RFGP.bFLed=0;
[color="#ff0000"]DBKCnt=Vl_DBKCnt; //задание начальных значений для счетчиков
FLCnt=(uint8_t)Vl_FLCnt;[/color]

sei (); // Разрешение общего прерывания
//____________________ ЦИКЛ РАБОЧЕЙ ПРОГРАММЫ ______________________
while (1)
{ //____ Считывание кнопки
  temp=~(PINB);
  temp &= KeyMask;
  if (temp)
  {  //______ Обработка нажатия кн."Btn"
   if ( RFGP.bDbRKey==0)//____ проверка на запрет считывания кнопок
   { RFGP.bDbRKey=1; // sbr RFGP,(1<<bDbRKey)
     sbi (PINB,Control1); // ________ Переключение линии Control1 _______________!!!!!
   }
  }
}
}


Сергей Борщ
QUOTE (MaxiMuz @ Nov 25 2011, 11:40) *
Хотел бы услышать ваши предположения.
Прочитайте еще раз про яйцо и микроволновку. Мне лень считать, сколько раз вам писали, что регистровую переменную нельзя делать volatile, о чем прямым текстом пишут создатели компилятора. Вы же с упорством пьяного продолжаете писать register volatile и пенять на компилятор.
MaxiMuz
Цитата(Сергей Борщ @ Nov 25 2011, 12:27) *
Прочитайте еще раз про яйцо и микроволновку. Мне лень считать, сколько раз вам писали, что регистровую переменную нельзя делать volatile, о чем прямым текстом пишут создатели компилятора. Вы же с упорством пьяного продолжаете писать register volatile и пенять на компилятор.

Извиняюсь что возмутил вас употребением и применением volatile !
Кстати сказать, из ответ на вопрос о использовании Volatile к регистровым глобальным переменным я со своим плохим знанием английского понял что компилятор уже считает регистровые переменные как volatile. Другими словами использование квалификатора бессмысленно.
Я убрал в обьявлении переменных FLCnt, DBKCnt volatile, результат тотже !
Вопрос остается открытым
AHTOXA
Цитата(MaxiMuz @ Nov 25 2011, 18:07) *
Вопрос остается открытым

Какой?
ReAl
Цитата(AHTOXA @ Nov 25 2011, 16:16) *
Какой?

Почему регистровые переменную, вне зависимости от наличя слова volatile, компилятор, предупредивший о своём отношении к таким объявлениям, не рассматривает как volatile и выкидывает из main() их инициализацию, так как в этой же main() к переменным больше обращений нет, а про обращение к ним из прерываний компилятор «не знает».
AHTOXA
По-моему, на этот вопрос тоже уже ответили, и не раз sm.gif
Я на всякий случай ещё раз повторю ответ: да, в этом моменте компилятор работает криво.
Варианты решения проблемы тоже вроде все предложены:
  1. Не использовать регистровые переменные.
  2. Использовать, но не в качестве volatile-переменных.
  3. Использовать, но для обращения к этим переменным применять подпрограммы на (инлайн-)ассемблере.

ReAl
Провёл маленький эксперимент.
Убрал все эти register... asm()
Начало файла стало выглядеть так:

Код
//=====================================================================
// определение регистров:
volatile uint8_t FLCnt;
volatile uint8_t DBKCnt;

#define DbRKey 0x01
#define FLed   0x02


Обработчик прерывания так:
Код
SIGNAL(SIG_OUTPUT_COMPARE0A)
{        
    uint8_t temp;

    temp = FLCnt;
    if (!(--temp)) {
        temp = Vl_FLCnt;
        //    ~(RFGP.bFLed);  Эта штука ничего осмысленного не делает.
        //    и в выходном коде от неё ничего И НЕ БЫЛО
        PINB |= _BV(Led);
    }
    FLCnt = temp;

    if ( GPIOR0 & DbRKey ) {
        temp = DBKCnt;
        // на мой взгляд странные манипуляции, но оставил как есть
        if (temp == 0)
            temp = Vl_DBKCnt;
        --temp;        
        if (temp == 0){    
            GPIOR0 &= ~DbRKey;
            temp = Vl_DBKCnt;
        }
        DBKCnt = temp;
        }
}

При компиляции для atmega168 количество команд в обработчиках прерывания для старого и нового варианта кода сохранилось.
Все push/pop в начале и в конце обработчика совпадают, общий ход практически тот же. Просто несколько команд mov заменилось на lds, sts.
В итоге длина в байтах для нового варианта увеличилась на 8 байт и стала 66 байт.
Время выполнения увеличилось на 4 такта.
Только время тут «абсолютно», а вот освобождение трёх зафиксированных до этого регистров освободит руки компилятору в других местах и общая длина кода всей программы ещё и уменьшиться может.

За что боролись?

Думаю, сначала нужно научиться просто на С писать, а потом про все эти зафиксированные регистры думать.
MaxiMuz
AHTOXA с вами не поспоришь sm.gif

ReAl По сути , кусок из вашего варианта :
Код
   temp = FLCnt;
    if (!(--temp)) {

подобен коду сформированному с использованием регистровых переменных:
Код
         if (!(--FLCnt))
  2e: 80 2f        mov r24, r16
  30: 81 50        subi r24, 0x01; 1
  32: 08 2f        mov r16, r24
  34: 11 f4        brne .+4

Но даже если закрыть глаза на бессмысленные mov rd,rs , как вы сами признали чуть быстрее и меньше по коду. И в использование возможностей железа по максимуму, даже учитывая корявость компилятора, нет ничего предосудительного sm.gif А абстрагирование от железа приводит к появлению таких опусов как Win7 ! с неоправданно раздутым кодом и тормозным ядром !
А если используется мощн.математический аппарат, то как я себе это представляю, здесь полезно оценить что важнее, быстый доступ к регистровым переменным или быстрое выполнение мат.операций.
Код
if ( GPIOR0 & DbRKey )
кстати в ATtiny13 (под него я пишу код) нет таких регистров вв./выв. которые можно так использовать!

Цитата(ReAl @ Nov 25 2011, 23:32) *
..........................
Думаю, сначала нужно научиться просто на С писать, а потом про все эти зафиксированные регистры думать.

с вами полностью согласен , но гибкое использование железа тоже часть "знания Си"
ReAl
Цитата(MaxiMuz @ Nov 28 2011, 12:59) *
подобен коду сформированному с использованием регистровых переменных:
Код
         if (!(--FLCnt))
  2e: 80 2f        mov r24, r16
  30: 81 50        subi r24, 0x01; 1
  32: 08 2f        mov r16, r24
  34: 11 f4        brne .+4

Но даже если закрыть глаза на бессмысленные mov rd,rs , как вы сами признали чуть быстрее и меньше по коду.
Но всё равно проиграет чисто-асмовой реализации гораздо больше, чем выиграет у «универсальной» реализации («универсальной» в том смысле, что точно так же можно написать для IAR/MSP430 и оно тоже будет оптимально в плане работы с volatile-переменными в памяти).

Цитата(MaxiMuz @ Nov 28 2011, 12:59) *
И в использование возможностей железа по максимуму, даже учитывая корявость компилятора, нет ничего предосудительного sm.gif А абстрагирование от железа приводит к появлению таких опусов как Win7 ! с неоправданно раздутым кодом и тормозным ядром!
А абстрагирование от языка и компилятора к чему приводит?
Сначала нужно навчиться по-максимуму использовать платформенно-независимые возможности языка, а то получается «вперёд к коммунизму минуя феодализм». А уже потом использовать учиться использовать возможности специфического компилятора для доступа к специфическим возможностям железа.
Если ставить телегу впереди лошади, то выходит как у Вас — оно-то вроде и короче, но для того, чтобы оно заработало правильно, приходится делать костыли в виде отдельно вызываемой функции инициализации этих переменных (rcall+ret сожрали 4 байта из выигрыша варианта без принудительно-регистровых переменных). А там, глядишь, и в других местах для нормального обращения к этим переменным придётся городить функции.

Цитата(MaxiMuz @ Nov 28 2011, 12:59) *
Код
if ( GPIOR0 & DbRKey )
кстати в ATtiny13 (под него я пишу код) нет таких регистров вв./выв. которые можно так использовать! с вами полностью согласен , но гибкое использование железа тоже часть "знания Си"
Нет. Это часть знания аппаратуры, а не языка. Эти же GPIOR можно и из ассемблера использовать, и из бейсика и что там ещё для AVR найдётся.
В теме до сих пор упоминался только ATtiny2313, у которого их три, вот я как-то и отнёс всё остальное к нему.
sigmaN
перед main воткните __attribute__ ((noreturn));
http://www.emerson.emory.edu/services/gcc/...Attributes.html
экономит "пару" байт
ReAl
Для этого в AVR-GCC уже давно есть OS_main и OS_task, они что-то там глубже копают.
Если main() сама по себе довольно большая, то OS_main даст больше эффекта, чем noreturn.
...
Начиная с 4.2.2, если верить мне самому в исходниках порта scmRTOS :-)
Код
#if GCC_VERSION >= 40202
#define OS_PROCESS              __attribute__((OS_task))
#else
#define OS_PROCESS              __attribute__((__noreturn__))
#endif
sigmaN
ну или так, да.
а ещё я из исходников библиотеки какие-то инициализации выкидывал, когда тини13а на сях программировал. тоже давало неслабый эффект.
ILYAUL
Цитата(sigmaN @ Nov 29 2011, 02:10) *
ну или так, да.
а ещё я из исходников библиотеки какие-то инициализации выкидывал, когда тини13а на сях программировал. тоже давало неслабый эффект.

ИМХО . Для 13 писать на С , можно только в рамках изучения С , там и asm сложно "развернуться", если задействовать все ее ресурсы.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.