logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Avr-gcc: Optymalizacja pętli for (i<1024) na BREQ - jak obejść?

zagwizdow 06 Gru 2007 16:10 1594 15
REKLAMA
  • #1 4558129
    zagwizdow
    Poziom 17  
    Posty: 177
    Pomógł: 21
    Ocena: 7
    Witam
    Mam taki problem który wyszedł przy jakimś innym błedzie że kompilator
    kompiluje petle for(i=0,i<1024;i++) na kod w ktorym wyjście z pętli jest określone na zasadzie i != 1024.
    Czy bez wyłączenia optymalizacji kodu ( czego nie chciałbym robić) da sie jakos to obejść ?
    Czy da sie jakoś lokalnie wyłączyć optymalizacje ?



    
    unsigned int i = 0; //wczesniej gdzies nbylo
    
    for (i = 0; i <= 1023; )
    {
    	//tu cos tam robilo 	
         if (blad)
     { i = 0xfff0;}
    i++;
    }
    	


    rozne wersje zapisu tej samej petli :
    for (i = 0; i < 1024; i++) {;}
    for (i = 0; i < 1024) {i++;}

    dawaly ten sam kod wyjscia z petli
    Cytat:

    1692 .L134:
    1693 0a92 0894 sec
    1694 0a94 811C adc r8,__zero_reg__
    1695 0a96 911C adc r9,__zero_reg__
    1696 .LBE25:
    1697 .LM239:
    1698 0a98 F0E0 ldi r31,lo8(1024)
    1699 0a9a 8F16 cp r8,r31
    1700 0a9c F4E0 ldi r31,hi8(1024)
    1701 0a9e 9F06 cpc r9,r31
    1702 0aa0 01F0 breq .+2
    1703 0aa2 00C0 rjmp .L125
    1704 .L133:

    w r8,r9 licznik petli,
    wyjscie przez zapis wiekszej wartosci do i ( i= 0xfff0) kompilator zrealizował jako skok do etykiety .L133.
  • REKLAMA
  • Pomocny post
    #2 4558447
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    Ale w czym problem?
    Jeżeli i nigdzie więcej nie jest używane, to kod jest funkcjonalnie równoważny.
    Nie da się lokalnie sterować opcjami optymalizcji (chyba, żeby wyłączyć kod do oddzielnie kompilowanego pliku), ale można czasem wpłynąć na to, co robi optymalizator. Np. można zadeklarować i jako volatile. Tylko trzeba dokładnie wiedzieć, czego się potrzebuje i po co.
  • REKLAMA
  • #3 4559298
    zagwizdow
    Poziom 17  
    Posty: 177
    Pomógł: 21
    Ocena: 7
    popatrze z tym volatile czy cos mu sie zmieni tylko ze to znowu nie tak zbytnio rozwiazanie zeby pchac indeks w volatile.
    a wystarczylo by zeby zamiast znacznika zera porownywal przeniesienie i bylo dobrze .

    wyszlo mi to akurat przy innym bledzie ktory jakos nadpisuje ta zmienna i leci petla dookola zanim znowy sie nie przewinie do 1024. przeszkadzac nie przeszkadza ale zmniejsza zaufanie do kompilatora :)
  • REKLAMA
  • Pomocny post
    #4 4560449
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 134
    Jeżeli wewnątrz pętli nie istnieje żaden fragment kodu który może zmienić wartość zmiennej i, to kompilator ma pełne prawo wygenerować taki kod.
    Sprawdź to z rzeczywistym kodem, gdzie faktycznie może nastąpić zmiana wartości zmiennej i. Odnosi się to również do zmiany wywołanej pośrednio, na przykład przez zmianę wartości zmiennaj blad. Kompilator śledzi operacje na zmiennych i jeżeli okaże się, że gdzieś na początku jakaś zmienna została zainicjowana wartościa stałą i dalej nie będzie zmieniana, to wszystkie zmienne zależne wyłącznie od niej, lub również od innych zmiennych jej podobnych, zostaną potraktowane jak stałe, co może spowodować usunięcie ich z programu. Konstrukcje warunkowe bazujące na tych zmiennych zostaną pozbawione kodu, który i tak nigdy by się nie wykonał itd.

    Konstrukcja której użyłeś jest dość często stosowana, tak że nie martwiłbym się o błąd kompilatora, bo coś takiego zostało by dosć szybko zgłoszone i poprawione.
  • Pomocny post
    #5 4560573
    trol.six
    Poziom 31  
    Posty: 1650
    Pomógł: 151
    Ocena: 381
    Jaką wersje kompilatora używasz? na jaki procek? Z jaką optymalizacją?

    Mi kompiluje poprawnie, tak sprawdza warunek:
     2dc:	d0 f3       	brcs	.-12     	; 0x2d2 <main+0x6c>


    Jeżeli zmienna u ciebie i, jest zmieniana w pętli programu, to jeżeli masz breq to wygląda na błąd kompilatora, albo masz przeoptymalizowane flagi. I kompilator nie interesuje sie co sie dzieje ze zmienną w kodzie pętli for. Choć to troche dziwne by było.
  • #6 4560793
    zagwizdow
    Poziom 17  
    Posty: 177
    Pomógł: 21
    Ocena: 7
    optymalizacje mam ustawiona na 's'
    zabardzo sie tam nie wglebialem nigdy w to ,ale to posprawdzam.
    nadpisywanie to osobny blad ktory na razie szukam.


    kompilator
    -------- begin --------
    avr-gcc (GCC) 4.1.2 (WinAVR 20070525)


    Cytat:

    # MCU name
    MCU = atmega128
    OPT = s

    CFLAGS = -g -O$(OPT) \
    -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
    -Wall -Wstrict-prototypes \
    -Wa,-adhlns=$(<:.c=.lst) \
    $(patsubst %,-I%,$(EXTRAINCDIRS))



    po dodaniu volatile jakos lepiej poszlo ale widze ze wczesniej zmienna w rejestrach siedziala a teraz gdzies w ramie ja umiescil.
    Cytat:

    1760 0b34 8981 ldd r24,Y+1
    1761 0b36 9A81 ldd r25,Y+2
    1762 0b38 0196 adiw r24,1
    1763 0b3a 9A83 std Y+2,r25
    1764 0b3c 8983 std Y+1,r24
    1765 .L127:
    1766 0b3e 8981 ldd r24,Y+1
    1767 0b40 9A81 ldd r25,Y+2
    1768 0b42 8050 subi r24,lo8(1024)
    1769 0b44 9440 sbci r25,hi8(1024)
    1770 0b46 00F4 brsh .+2
    1771 0b48 00C0 rjmp .L128


    ---------------------------

    powoli sprawdzam optymalizacje
    dla 0 bylo tak jak dla volatile przy s
    dla 1 jest znowy breq

    Cytat:
    1809 .L142:
    1810 .LBE11:
    1811 .LM242:
    1812 0b9a 0894 sec
    1813 0b9c 611C adc r6,__zero_reg__
    1814 0b9e 711C adc r7,__zero_reg__
    1815 0ba0 80E0 ldi r24,lo8(1024)
    1816 0ba2 6816 cp r6,r24
    1817 0ba4 84E0 ldi r24,hi8(1024)
    1818 0ba6 7806 cpc r7,r24
    1819 0ba8 01F0 breq .+2
    1820 0baa 00C0 rjmp .L133
    1821 .L141:



    2 i 3 daja podobny kod z breq

    to jeszcze poszukam opcji do optymalizacji .

    dzieki za pomoc.
  • Pomocny post
    #7 4561186
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    zagwizdow napisał:

    po dodaniu volatile jakos lepiej poszlo ale widze ze wczesniej zmienna w rejestrach siedziala a teraz gdzies w ramie ja umiescil.


    To jest dość oczywista konsekwencja użycia tego kwalifikatora - efektywnie zabrania on jakichkolwiek optymalizacji zmiennej i pozostaje ona na stosie.

    Wydaje mi się, że walczysz z niewłaściwym problemem - szukasz dziury w całym. Pisałem ja, opisał Ci to szczegółowo kolega shg, to są zupełnie poprawne optymalizacje - wygenerowany kod jest funkcjonalnie równoważny, czyli poprawny. Prawdopodobieństwo błędu kompilatora w takim prostym przypadku jest może 0.00001% albo i mniej :wink:

    Może lepiej napisz, na czym faktycznie polega Twój problem?
  • REKLAMA
  • Pomocny post
    #8 4561308
    trol.six
    Poziom 31  
    Posty: 1650
    Pomógł: 151
    Ocena: 381
    Czyli panowie szelus i shg, chcecie powiedzieć ze poniższy kod jest nieprawidłowy?

    
    for (i=0; i<=20; )
    {
    
    if (jakis_warunek) i++;
    
    if (jakis_inny_warunek) i++;
    
    }
    
  • Pomocny post
    #9 4561459
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    Nie, co niby w nim nieprawidłowego* ?

    Chodzi o to, że nie można zakładać, że kompilator wygeneruje identyczny schemat pętli (i warunku wyjścia, np. to nieszczęsne breq), po zmianie programu i dodaniu różnych rzeczy wewnątrz pętli.
    Optymalizacja powoduje, że wygenrowany kod jest funkcjonalnie równoważny, ale nie koniecznie wykonuje dokładnie takie operacje jak kod źródłowy. Funkcjonalnie równoważny oznacza w tym przypadku, że efekt działania kodu będzie identyczny.

    Np. właśnie sprawdziłem, że avr-gcc w wersji 3.4.3 dla pętli:
    
    int i;
    for (i=0; i < 1024; ++i)
    {
        jakas_funkcja();
    }
    

    rozwinął pętlę w ten sposób, że załadował do licznika pętli 1023, po czym w pętli dekrementował licznik i sprawdzał (jako warunek wyjścia) czy nie stał się ujemny. Jest to jak najbardziej poprawne zachowanie - w jednym i w drugim przypadku jakas_funkcja() będzie wywołana 1024 razy.

    ---------------------------------
    *) Oczywiście zakładam, że intencja programisty jest taka, że w przypadku, gdy nie zachodzi ani jakis_warunek ani jakis_inny_warunek pętla będzie sie wykonywać w nieskończoność.
  • Pomocny post
    #10 4561494
    trol.six
    Poziom 31  
    Posty: 1650
    Pomógł: 151
    Ocena: 381
    No właśnie, czyli jeżeli zagwizdow, skompilował to, i dostał jako warunek nie nieujemny a równy, bo breq to jest równość. To znaczy że kompilator źle to zrobił.

    Ja używam 3.4.4 , niestety nie mam teraz czasu na sprawdzanie na innym, może wieczorkiem jak znajde czas.

    Oczywiście jeżeli inkrementacja jest stopniowa, to breq będzie OK, no bo może zagwizdow coś tajemniczego jeszcze namieszał o czym nie mamy tutaj pojęcia. Co jest całkiem możliwe.

    Dodane:
    No właśnie, zagwizdow, a co nie działa ci to? Przecież nie masz pdwójnych, jak ja w przykładzie powyżej niuansów, więc co? Nie działa?
    Nie wiem czemu ale myślałem że masz fragment kodu, i przeskakuje ci pętle. lol.

    A już wiem czemu:
    Cytat:
    //tu cos tam robilo


    W takim razie jeżeli tam coś jest faktycznie to pętla wykonana sie nieprawidłowa ilość razy, jednak bez tego wynik kompilacji jest poprawany. Ale nieuraczyłeś nas co tam jest wiec teraz nie wiadomo.

    Takie fragmentaryczne obrazki nic nie mówią tak napradwe o końcowym kodzie.
  • #11 4561526
    zagwizdow
    Poziom 17  
    Posty: 177
    Pomógł: 21
    Ocena: 7
    Nie twierdze ze kod jest niepoprawny bo roznowaznie robi to samo. Znalazlem to przy okazji szukania innego bledu i ujawnia to pewną ułomność takiej kompilacji bo w przypadku bledów modyfikujacych indeks prowadzi to zapetlenia petli. Zawsze w petlach w wiekszosci wstawiam warunek '<' nie dlatego ze nie umiem dac '!=' tylko ze tego nie chcialem ale widac ze tu akurat to dla kompilatora nic nie zmienia.

    A co do optymalizacji to rozkaz wyjscia przy == (breq ) i przy '<' (brsh)nie rozni sie czasem ani dlugoscia.
  • Pomocny post
    #12 4561531
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    Dla takiej (uproszczonej) pętli, jak kolega zagwizdow pokazał w pierwszym poście:
    Cytat:

    for (i = 0; i < 1024; i++) {;}
    for (i = 0; i < 1024) {i++;}

    BREQ wyjście_z_pętli jest jak najbardziej OK. Niestety kolega zagwizdow serwuje nam tu fragmenty wybiórczo, a ja nie mam po ręką wersji 4.1.2 żeby to sprawdzić, więc możemy sobie gdybać.
  • Pomocny post
    #13 4561556
    trol.six
    Poziom 31  
    Posty: 1650
    Pomógł: 151
    Ocena: 381
    zagwizdow napisał:
    Zawsze w petlach w wiekszosci wstawiam warunek '<' nie dlatego ze nie umiem dac '!=' tylko ze tego nie chcialem ale widac ze tu akurat to dla kompilatora nic nie zmienia.

    A co do optymalizacji to rozkaz wyjscia przy == (breq ) i przy '<' (brsh)nie rozni sie czasem ani dlugoscia.

    Tak, jak pisze w asemblerze, to też staram sie uzywać brsc zamiast breq, bo po prostu czasem nie wiem, albo nie pamiętam, co sie po drodze może stac z moją zmienną. Jednak kompilator sobie to dosć szybko sprawdza.
  • Pomocny post
    #14 4561563
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    zagwizdow napisał:
    Nie twierdze ze kod jest niepoprawny bo roznowaznie robi to samo. Znalazlem to przy okazji szukania innego bledu i ujawnia to pewną ułomność takiej kompilacji bo w przypadku bledów modyfikujacych indeks prowadzi to zapetlenia petli. Zawsze w petlach w wiekszosci wstawiam warunek '<' nie dlatego ze nie umiem dac '!=' tylko ze tego nie chcialem ale widac ze tu akurat to dla kompilatora nic nie zmienia.


    Jestem niemal zupełnie przekonany, że błąd jest gdzieś indziej.
    Jeżeli kompilator zmienił tak warunek to z jakiegoś powodu kompilator "nie zauważył', że jest coś dodawane do licznika pętli. Jest bardzo małe prawdopodobieństwo, że jest to błąd w kompilatorze i raczej obstawiałbym to, że program nie specyfikuje tego, co się programiście wydaje, że specyfikuje. Innymi słowy, że warunek jest źle sformułowany.
    Może pokażesz dokładnie ten warunek pt. blad? A jeśli to flaga, to również fragment kodu, który ją ustawia.

    I jeszcze jedno. Jaki masz ustawiony poziom ostrzeżeń (-W)? Kompilator nie krzyczy gdzieś w stylu "statement has no effect" albo, że warunek jest zawsze true/false?
  • Pomocny post
    #15 4561597
    trol.six
    Poziom 31  
    Posty: 1650
    Pomógł: 151
    Ocena: 381
    Chyba jedynym przypadkiem gdzie kompilator nie potrafi sobie policzyć do równości, są warunki od zmiennych volatile. Ale też podwójne.

    volatile char a = 0;
    
    for( i=0; i<=20; i++ )
    {
    
    if ( PIND == 20) i++;
    if ( a == 20) i++;
    
    }


    wtedy byłoby źle, jakby sprawdzał ==, w każdym innym przypadku, wzrost następuje pojedynczo, i całkiem spokojnie można stosować breq
  • #16 4561625
    zagwizdow
    Poziom 17  
    Posty: 177
    Pomógł: 21
    Ocena: 7
    Widze ze zrobile sie zamieszanie .
    Nie szukalem tu ze petla nie dziala , tylko myslalem ze ktos moze wie jak prosto zmusic kompilator do sprawdzania warunku '<' ale jak sie nie da to trudno.

    Dzieki wszystkim za pomoc.

Podsumowanie tematu

✨ Dyskusja dotyczy optymalizacji pętli for w kompilatorze avr-gcc (wersja 4.1.2, WinAVR 20070525) dla mikrokontrolerów AVR, gdzie warunek wyjścia z pętli jest generowany jako porównanie równości (BREQ) zamiast oczekiwanego porównania mniejszości (np. BRSH). Autor zauważa, że kompilator optymalizuje pętlę for(i=0; i
Wygenerowane przez model językowy.
REKLAMA