Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Europejski lider sprzedaży techniki i elektroniki.
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

XMega AVR - Jak zadeklarować indeksowaną zmienną bitową w C

jp_elek 16 Gru 2016 01:17 2661 106
  • #61 16 Gru 2016 01:17
    Piotrus_999
    Poziom 40  

    tmf napisał:
    Poza tym twój przykład kodu z x86 jasno pokazuje, że efekt jest zależny od implementacji.
    To jest o wersji gdzie x nie jest volatile.

    w przypadku volatile arm i x86 generują kod identycznie : tu przykład z arm-a
    Kod: c
    Zaloguj się, aby zobaczyć kod


    W przypadku volatile standard mówi wyraznie że musi być czytany przed każdym użyciem i zapisywany po każdej zmianie np:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    albo avr:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Dodano po 17 [minuty]:

    tmf napisał:
    Wracając do tematu - zauważ, że tu wielokrotny odczyt tej samej zmiennej w ramach jednego wyrażenia kompletnie nie ma sensu. A wręcz prowadzi do błedów - w ramach wyrażenia x to x i nie powinno być tak, że z magicznych przyczyn to x się może zmieniać w trakcie jego ewaluacji.
    Specyfika volatile (często zresztą nie rozumiana) - jak chcesz użyć wartości z peirwszego odczytu wystarczy tylko zdjąc atrubut volatile (np przez użycie zmiennej lokalnej).

    Dodano po 43 [minuty]:

    tmf napisał:
    że jeśli mamy operacje o takim samym priorytecie to kolejność ich ewaluacji jest dowolna
    Nie jest. Są ewaluowane zgodnie z tym co zapisano w standardzie czyli +- */ z lewej do prawej.

  • #62 16 Gru 2016 08:17
    Freddie Chopin
    Specjalista - Mikrokontrolery

    tmf napisał:
    A wręcz prowadzi do błedów - w ramach wyrażenia x to x i nie powinno być tak, że z magicznych przyczyn to x się może zmieniać w trakcie jego ewaluacji.

    Załóżmy, że ADC_DR to rejestr danych ADC, zadeklarowany oczywiście jako "volatile cośtam".

    accumulator = ADC_DR + ADC_DR + ADC_DR + ADC_DR;
    average = accumulator / 4;

    Czy dobrze rozumiem, że wg Ciebie rejestr powinien być odczytany raz i pomnożony przez 4? Szukacie dziury w całym, a przecież volatile jest jasno określone i nadzwyczaj proste - ma zostać odczytane przed _KAŻDYM_ użyciem i zapisane po _KAŻDEJ_ zmianie, nie może być buforowane w rejestrach. Tylko tyle i aż tyle.

    Jeśli w wyrażeniu dodawania x nie może się zmienić, to wystarczyło go zadeklarować bez volatile. Skoro programista powiedział kompilatorowi - "słuchaj, ta zmienna może się naprawdę zmienić w każdym takcie, uwierz mi, wiem co robię, nawet jeśli wydaje Ci się inaczej" - to jedyne co kompilator może zrobić, to posłuchać. Zwłaszcza że rozwiązania tego "problemu" są trywialne i wystarczy odczytać sobie do dodatkowej lokalnej zmiennej i już użyć jej milion razy.

    EDIT: Żeby nie być gołosłownym

    Cytat:
    5.1.2.3 Program execution
    [...]
    2 Accessing a volatile object, modifying an object, modifying a file, or calling a function
    that does any of those operations are all side effects, 12) which are changes in the state of
    the execution environment. Evaluation of an expression in general includes both value
    computations and initiation of side effects. Value computation for an lvalue expression
    includes determining the identity of the designated object.
    [...]
    4 In the abstract machine, all expressions are evaluated as specified by the semantics. An
    actual implementation need not evaluate part of an expression if it can deduce that its
    value is not used and that no needed side effects are produced (including any caused by
    calling a function or accessing a volatile object).
    [...]
    6 The least requirements on a conforming implementation are:
    — Accesses to volatile objects are evaluated strictly according to the rules of the abstract
    machine.
    [...]


    Cytat:
    6.7.3 Type qualifiers
    [...]
    7 An object that has volatile-qualified type may be modified in ways unknown to the
    implementation or have other unknown side effects. Therefore any expression referring
    to such an object shall be evaluated strictly according to the rules of the abstract machine,
    as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the
    object shall agree with that prescribed by the abstract machine, except as modified by the
    unknown factors mentioned previously. 134) What constitutes an access to an object that
    has volatile-qualified type is implementation-defined.


    Przykład z wielokrotnym dodawaniem x podpada co najmniej pod "side effects", wiec nie ma się co doszukiwać błędu w kompilatorze.

  • #63 16 Gru 2016 08:50
    Piotrus_999
    Poziom 40  

    Freddy a co ja napisałem w poprzednim poście? Powtórzyłes moje argumenty. Nie szukam dziury a chodziło o wytłumaczenie początkującemu o co chodzi z volatile i dlaczego generowany jest taki kod jak jest. Avr-gcc za to nie optymalizuje nie volatile dodawan - i to zauważyłem. (inne to robią)

  • #64 16 Gru 2016 09:17
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Piotrus_999 napisał:
    Freddy a co ja napisałem w poprzednim poście?

    Napisałeś też, że zgłosiłeś ponownie buga do GCC, choć widzę że nie ma on akurat wiele wspólnego ze sprawą volatile.

  • #65 16 Gru 2016 09:28
    Piotrus_999
    Poziom 40  

    Freddie Chopin napisał:
    wspólnego ze sprawą volatile
    No właśnie. Chodzi o nie volatile. Volatile pojawił się przy okazji komentarza kol. Tmf. A moja uwaga o nie zoptymalizowaniu nie volatile jako ciekawostka.

  • #66 16 Gru 2016 10:39
    tmf
    Moderator Mikrokontrolery Projektowanie

    Piotrus_999 napisał:

    tmf napisał:
    że jeśli mamy operacje o takim samym priorytecie to kolejność ich ewaluacji jest dowolna
    Nie jest. Są ewaluowane zgodnie z tym co zapisano w standardzie czyli +- */ z lewej do prawej.


    Mylisz się, w standardzie nigdzie nie jest określona klejność ewaluacji dla operastorów o równym priorytecie. Takie zachowanie jest niezdefiniowane - zresztą zacytowałem ci stosowny fragment. Oczywisty przykład:
    a[i++]=b[i++]; - zachowanie nieokreślone.
    b=a[i++] + b[i++]; - zachowanie nieokreślone.
    W obu przypadkach zachowanie jest zależne od implementacji, kompilator ma absolutną dowolność ewaluacji powyższych wyrażeń.

    Dodano po 6 [minuty]:

    Freddie Chopin napisał:
    tmf napisał:
    A wręcz prowadzi do błedów - w ramach wyrażenia x to x i nie powinno być tak, że z magicznych przyczyn to x się może zmieniać w trakcie jego ewaluacji.

    Załóżmy, że ADC_DR to rejestr danych ADC, zadeklarowany oczywiście jako "volatile cośtam".

    accumulator = ADC_DR + ADC_DR + ADC_DR + ADC_DR;
    average = accumulator / 4;

    Czy dobrze rozumiem, że wg Ciebie rejestr powinien być odczytany raz i pomnożony przez 4? Szukacie dziury w całym, a przecież volatile jest jasno określone i nadzwyczaj proste - ma zostać odczytane przed _KAŻDYM_ użyciem i zapisane po _KAŻDEJ_ zmianie, nie może być buforowane w rejestrach. Tylko tyle i aż tyle.

    Jeśli w wyrażeniu dodawania x nie może się zmienić, to wystarczyło go zadeklarować bez volatile. Skoro programista powiedział kompilatorowi - "słuchaj, ta zmienna może się naprawdę zmienić w każdym takcie, uwierz mi, wiem co robię, nawet jeśli wydaje Ci się inaczej" - to jedyne co kompilator może zrobić, to posłuchać. Zwłaszcza że rozwiązania tego "problemu" są trywialne i wystarczy odczytać sobie do dodatkowej lokalnej zmiennej i już użyć jej milion razy.

    EDIT: Żeby nie być gołosłownym

    Cytat:
    5.1.2.3 Program execution
    [...]
    2 Accessing a volatile object, modifying an object, modifying a file, or calling a function
    that does any of those operations are all side effects, 12) which are changes in the state of
    the execution environment. Evaluation of an expression in general includes both value
    computations and initiation of side effects. Value computation for an lvalue expression
    includes determining the identity of the designated object.
    [...]
    4 In the abstract machine, all expressions are evaluated as specified by the semantics. An
    actual implementation need not evaluate part of an expression if it can deduce that its
    value is not used and that no needed side effects are produced (including any caused by




    calling a function or accessing a volatile object).
    [...]
    6 The least requirements on a conforming implementation are:
    — Accesses to volatile objects are evaluated strictly according to the rules of the abstract
    machine.
    [...]


    Cytat:
    6.7.3 Type qualifiers
    [...]
    7 An object that has volatile-qualified type may be modified in ways unknown to the
    implementation or have other unknown side effects. Therefore any expression referring
    to such an object shall be evaluated strictly according to the rules of the abstract machine,
    as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the
    object shall agree with that prescribed by the abstract machine, except as modified by the
    unknown factors mentioned previously. 134) What constitutes an access to an object that
    has volatile-qualified type is implementation-defined.


    Przykład z wielokrotnym dodawaniem x podpada co najmniej pod "side effects", wiec nie ma się co doszukiwać błędu w kompilatorze.


    Kluczem do zrozumienia powyższego są sequence points. W ramach wyrażenia a=b+c+d+e; nie ma punktów sekwencyjnych i zgodnie ze standardem kompilator może zacząc od czegokolwiek. Punkty sekwencyjne wprowadziłbyś, gdyby to wyrażenie zapisać:
    a=b;
    a+=c;
    a+=d;
    a+=e;
    Tu kompilator nie ma możliwości zmian i musi ewaluować wyrażenia w podanej kolejności, chyba, że optymalizator uzna, że kolejność nie ma znaczenia (dla volatile nie może zmienić kolejności). Nigdzie nie pisałem, że podane zachowanie kompilatora polegające na czterokrotnym odczycie zmiennej volatile w ramach wyrażenia jest błędne - pisałem, że IMHO jest to przykład braku optymalizacji, a równie poprawny byłby jednokrotny odczyt, ze względu właśnie na brak punktu sekwencyjnego.
    W twoim przykładzie akumulacja wyników z ADC tylko przypadkiem zadziała dobrze. Załóżmy, że kolejne odczyty mają różne wagi, czyli zapiszemy accu=ADC/4 + ADC/2 + ADC/1 - wynik tej operacji jest zależny od kolejności ewaluacji wyrażenia, a ta jest wg standardu dowolna!

    Dodano po 9 [minuty]:

    Tu cały artykuł na ten temat z odnośnikami do standardu języka:
    https://www.securecoding.cert.org/confluence/...d+on+the+order+of+evaluation+for+side+effects

  • #67 16 Gru 2016 10:46
    Piotrus_999
    Poziom 40  

    tmf napisał:
    a[i++]=b[i++]; - zachowanie nieokreślone.
    b=a[i++] + b[i++]; - zachowanie nieokreślone.
    To nie najlepszy przykład - łorninga nie dostałeś?.

    Raczej

    Kod: armasm
    Zaloguj się, aby zobaczyć kod


    Z lewej do prawej jak nic.

  • #68 16 Gru 2016 11:02
    Freddie Chopin
    Specjalista - Mikrokontrolery

    tmf napisał:
    IMHO jest to przykład braku optymalizacji, a równie poprawny byłby jednokrotny odczyt, ze względu właśnie na brak punktu sekwencyjnego.

    Mylisz się. Zobacz ten fragment w cytatach

    "An object that has volatile-qualified type may be modified in ways unknown to the
    implementation or have other unknown side effects. Therefore any expression referring
    to such an object shall be evaluated strictly according to the rules of the abstract machine,
    as described in 5.1.2.3."

    Owe mistyczne "rules of the abstract machine" to system w którym żadne optymalizacje nie są włączone i nie ma rejestrów procesora, a więc po prostu każdy fragment kodu z odczytem owego "x" ma spowodować odczyt. Głównym powodem jest owe "side effects".

    tmf napisał:
    W twoim przykładzie akumulacja wyników z ADC tylko przypadkiem zadziała dobrze. Załóżmy, że kolejne odczyty mają różne wagi, czyli zapiszemy accu=ADC/4 + ADC/2 + ADC/1 - wynik tej operacji jest zależny od kolejności ewaluacji wyrażenia, a ta jest wg standardu dowolna!

    Wprowadzasz komplikację która jest bez znaczenia dla dyskusji. Chodziło o to, żeby były cztery fizyczne odczyty. Czym - z punktu widzenia kompilatora - różni się "#define ADC_DR *(volatile cośtam*)(0x1234567)" od "volatile uint8_t x;"? Niczym. Skąd ma niby wiedzieć, że jedno ma odczytać tyle razy ile jest zapisane w kodzie, a drugie może odczytać raz i pomnożyć przez ile mu akurat potrzeba?

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Teraz też wg Ciebie nie ma fizycznej możliwości, żeby x mogło się jednak zmienić?

    Ta dyskusja to naprawdę szukanie dziury w całym - volatile to volatile. Jak wiesz że się nie zmieni, to po co dawać volatile? Jeśli nie zmieni się akurat w tym momencie, to skopiuj do zmiennej. Ja tam wolę, żeby taki bezsensowny kod (przykład czysto akademicki, nigdy nie zobaczymy czegoś takiego w rzeczywistym kodzie) był "niezoptymalizowany" (ale wciąż 100% poprawny), niż żeby kompilator musiał podejmować decyzje kiedy może sobie zoptymalizować dostęp do sprzętowych rejestrów, bo jak jest jakieś pole do decyzji, to znaczy że jest pole do ewentualnych błędów lub problemów.

    Dodano po 2 [minuty]:

    Piotrus_999 napisał:
    Z lewej do prawej jak nic.

    No i co z tego? "Implementation defined" nie znaczy, że absolutnie nie może być z lewej do prawej. Po prostu nie musi być. Równie dobrze może być zupełnie odwrotnie. Na innym kompilatorze, innej archutekturze, innej wersji tego samego kompilatora, przy innych opcjach kompilacji albo w innej funkcji.

  • #69 16 Gru 2016 11:06
    tmf
    Moderator Mikrokontrolery Projektowanie

    @Piotrus_999 Przeczytałeś artykuł do którego dałem link? Masz tam wprost odniesienia do standardu i napisane, że nie ma czegoś takiego w standardzie jak kolejność ewaluacji od lewej do prawej lub odwrotnie. To co obserwujesz to efekt konkretnej implementacji - jakoś kompilator musi działać w sytuacjach niejednoznacznych. W przykładzie:
    c=a[i++] + b[i++];
    kompilator też jakoś musi to wyrażenie zaimplementować, ale ma tu dowolność, bo standard nic nie mówi jak to zaimplementować. Czli każdy kompilator może wygenerować inny kod i raz to będzie:
    c=a[i++]
    c+=b[i++]
    a raz:
    c=b[i++]
    c+=a[i++]
    co oczywiście generuje zupełnie inny wynik. Podobnie jest z volatile - ten modyfikator nie wprowadza punktu sekwencyjnego do wyrażenia (lista sytuacji w których wstawiany jest punkt sekwencyjny w standardzie jest listą zamkniętą, enumeratywną, nie jest na niej wymienone volatile), a więc ewaluacja takich wyrażeń jest dowolna. Jedyną rzeczą jaką może zrobić rozsądny programista to unikać takich sytuacji, wprowadzając jasne punkty sekwencyjne - w tym wypadku dzieląc wyrażenie na podwyrażenia rozdzielone średnikiem (wprowadzającym punkt sekwencyjny). Koniec.
    Chcesz dyskutować - podaj konkretne stwierdzenie ze standardu.

  • #70 16 Gru 2016 11:10
    Freddie Chopin
    Specjalista - Mikrokontrolery

    tmf napisał:
    Podobnie jest z volatile - ten modyfikator nie wprowadza punktu sekwencyjnego do wyrażenia (lista sytuacji w których wstawiany jest punkt sekwencyjny w standardzie jest listą zamkniętą, enumeratywną, nie jest na niej wymienone volatile), a więc ewaluacja takich wyrażeń jest dowolna. Jedyną rzeczą jaką może zrobić rozsądny programista to unikać takich sytuacji, wprowadzając jasne punkty sekwencyjne - w tym wypadku dzieląc wyrażenie na podwyrażenia rozdzielone średnikiem (wprowadzającym punkt sekwencyjny). Koniec.

    Przecież na razie to nikt poza Tobą nie pisze nic o punktach sekwencyjnych. Ja ciągle powtarzam, że chodzi o "side effects".

  • #71 16 Gru 2016 11:16
    tmf
    Moderator Mikrokontrolery Projektowanie

    @Freddie Chopin Na poziomie maszyny abstrakcyjnej istotna jest kolejność pomiędzy punktami sekwencyjnymi. I tylko taka kolejność jest gwarantowana. Tłumaczenie do obrębie punktu może być dowolne i równie poprawny jest jednokrotny, jak i czterokrotny odczyt zmiennej. Podczas dalszego przekształcania wyrażenia informacja o tym, że miałeś modyfikator volatile znika. Przypuszczam, a nawet jestem pewien, że twórcy gcc z tego powodu właśnie poszli na łatwiznę i jeśli masz volatile to kasują optymalizację. Jak pisałem, nie jest to błąd kompilatora, po prostu jedna z możliwych implementacji niepoprawnego wyrażenia, w którym mamy wielokrotne odwołanie do tej samej zmiennej volatile.
    IMHO jedynie cąłkiem bezpieczną i poprawną metodą zapisu byłoby rozdzielenie tego dostępu, tak aby wprowadzić punkty sekwencyjne, albo przypisanie tej zmiennej inne zmiennej i potem wykorzystanie tej drugiej (już bez volatile) w wyrażeniu.

    Dodano po 1 [minuty]:

    Freddie Chopin napisał:
    tmf napisał:
    Podobnie jest z volatile - ten modyfikator nie wprowadza punktu sekwencyjnego do wyrażenia (lista sytuacji w których wstawiany jest punkt sekwencyjny w standardzie jest listą zamkniętą, enumeratywną, nie jest na niej wymienone volatile), a więc ewaluacja takich wyrażeń jest dowolna. Jedyną rzeczą jaką może zrobić rozsądny programista to unikać takich sytuacji, wprowadzając jasne punkty sekwencyjne - w tym wypadku dzieląc wyrażenie na podwyrażenia rozdzielone średnikiem (wprowadzającym punkt sekwencyjny). Koniec.

    Przecież na razie to nikt poza Tobą nie pisze nic o punktach sekwencyjnych. Ja ciągle powtarzam, że chodzi o "side effects".


    O, to może doszliśmy do porozumienia. Jest side effect implementation specific. Czy tak powinniśmy programować? NIE.

  • #72 16 Gru 2016 11:33
    Piotrus_999
    Poziom 40  

    tmf napisał:
    po prostu jedna z możliwych implementacji niepoprawnego wyrażenia, w którym mamy wielokrotne odwołanie do tej samej zmiennej volatile.
    A w czym jest ono niepoprawne?

    mozesz np chcieć podnieść do kwadratu zmienna volatile.

    volatile są jak przestrzenie nieeuklidesowe :)
    przy volatile niekoniecznie to jest prawdziwe x+x+x == x*3

  • #73 16 Gru 2016 11:39
    94075
    Usunięty  
  • #74 16 Gru 2016 11:45
    tmf
    Moderator Mikrokontrolery Projektowanie

    @Piotrus_999 Nie mąć. Piszemy o standardzie języka. Niepoprawne jest to, że wykorzystujemy właściwość zależną od implementacji, w efekcie ten sam kod da inne efekty na innym kompilatorze, ba, nawet jeśli kiedyś się twórcom gcc coś odwidzi i postanowią zmienić tą implementację. Nawet twój przykład z x^2 dobrze ilustruje problem - jak kompilator to zaimplementuje - jako funkcje kwadrat(x), czy jako x*x? I dlaczego dla volatile wynik będzie różny w obu przypadkach?

    Dodano po 2 [minuty]:

    albertb napisał:
    @tmf
    Dla mnie z faktu, że 4 opracowania wartości x są w dowolnej kolejności wyciągnięcie wniosku, że można je zastąpić jednym (przed optymalizacją) jest nieuprawnione.
    Ot i całe clou dyskusji.

    Albert


    Albert - to nie tak. Chodzi o to, że kompilator ma dowolność implementacji tego zapisu - to może być x+x+x+x, lub 4*x. Obie implementacje są równie poprawne. Jeśli uważasz, że tak nie jest, to podaj proszę źródło.

  • #75 16 Gru 2016 11:48
    Piotrus_999
    Poziom 40  

    tmf napisał:
    wet twój przykład z x^2 dobrze ilustruje problem - jak kompilator to zaimplementuje - jako funkcje kwadrat(x), czy jako x*x?


    Szczęśliwie w C nie ma operatora potęgowania więc X^2 kompilator jakoś zXOR-uje. Jak zrobione będzie podnoszenie do kwadratu to zależy jak programista napisze.

    tmf napisał:
    jak kompilator to zaimplementuje - jako funkcje kwadrat(x), czy jako x*x? I dlaczego dla volatile wynik będzie różny w obu przypadkach?
    To jest oczywiste i może sie okazać że będzie różny dla volatile jeżeli x będzie przekazany przez parametr, a x zmieni się pomiedzy operacjami). Volatile musi być po prostu tyle razy czytany ile razy jest wykorzystywany, a zapisywany po każdej zmianie. W przypadku kiedy jest przekazywany do funkcji jako parametr odczytany będzie tylko raz. (lub tyle razy ile potrzeba jeżeli przez volatile wskaźnik jako parametr funkcji)
    Nie ma to nic wspólnego z implementacją. Jest to właśnie standard języka.

  • #76 16 Gru 2016 12:01
    94075
    Usunięty  
  • #77 16 Gru 2016 12:56
    Piotrus_999
    Poziom 40  

    [quote="Freddie Chopin"]No i co z tego? "Implementation defined" nie znaczy, że absolutnie nie może być z lewej do prawej. Po prostu nie musi być. Równie dobrze może być zupełnie odwrotnie. Na innym kompilatorze, innej archutekturze, innej wersji tego samego kompilatora, przy innych opcjach kompilacji albo w innej funkcji.

    Cytat:
    Są w C pewne generalne zasady:
    Kod: armasm
    Zaloguj się, aby zobaczyć kod

  • #78 16 Gru 2016 14:35
    jp_elek
    Poziom 9  

    jeśli wolno na chwilę wtrącić coś bardziej podstawowego ,

    ( wszystkie wersje kompilują się niezależnie od -Ox ,efekty różne , podobnie ostrzeżenia)

    otóż po stworzeniu trzech prostych funkcji z "always_online" i umieszczeniu ich w IO_cfg.h ,

    wyrzuca ostrzeżenia niespójne , bo gdyby nie były używane to raczej powinien robić awanturę że tych funkcji "nie zna" , jednocześnie będąc zmuszony je wielokrotnie używać/implementować w instrukcjach z pliku z funkcją main.
    XMega AVR - Jak zadeklarować indeksowaną zmienną bitową w C

    po umieszczeniu natomiast tychże funkcji w pliku z 'main' produkuje się takimi ostrzeżeniami :

    XMega AVR - Jak zadeklarować indeksowaną zmienną bitową w C

    Istota mojego pytania sprowadza się do dwu kwestii:

    a.) Czy jest jakiś sposób aby to to ( "C") - wstawiło bezwarunkowo kod funkcji czy też nie ma takiego sposobu. ( pytanie oczywiście z podtekstem jeśli mam analizować -w przypadku realnej - nieco bardziej złożonej funkcji inline, to zdecydowanie wolę analizować własny kod asemblerowy niż niespodziankę z komp. C, usianą kilku tuzinami skoków )

    b.) Czy wymuszenie sekwencji instrukcji przez prosty zapis w kolejnych liniach zakończonych średnikiem , jest wystarczającym bezwarunkowym wymogiem zachowania kolejności działań ( mówimy o instrukcjach gdzie argumentami są volatile ) ?

    Ostrzeżenia może mnie nie przerażają, ale są prostą drogą do potknięcia się.
    J.P.

  • #79 16 Gru 2016 14:46
    Piotrus_999
    Poziom 40  

    dodaj inline i bedzie - takie małe niedopatrzenie wtedy nie będzie plakał. Po prostu funkcja nie inline dostała atrybut inline

    Dodano po 3 [minuty]:

    jp_elek napisał:

    b.) Czy wymuszenie sekwencji instrukcji przez prosty zapis w kolejnych liniach zakończonych średnikiem , jest wystarczającym bezwarunkowym wymogiem zachowania kolejności działań ( mówimy o instrukcjach gdzie argumentami są volatile ) ?

    Nie martw się - dostaniesz właściwy kod bez wzgledu na to co napiszesz. Kompilator zrobi tak jak miało być.

  • #80 16 Gru 2016 14:48
    jp_elek
    Poziom 9  

    @ Piotrus_999
    hej, pomogło , sam bym nie wpadł na "masło maślane " w rodzaju :

    static inline bool __attribute__((always_inline)) Y_isON(uint8_t Y_No)

  • #81 16 Gru 2016 14:55
    tmf
    Moderator Mikrokontrolery Projektowanie

    albertb napisał:
    tmf napisał:
    Chodzi o to, że kompilator ma dowolność implementacji tego zapisu - to może być x+x+x+x, lub 4*x. Obie implementacje są równie poprawne. Jeśli uważasz, że tak nie jest, to podaj proszę źródło.

    Moim zdaniem nie są, pod względem side effects. I wynika to dla mnie z tego co przytoczył Freddie w poście #62
    Natomiast wydaje mi się, że tak naprawdę to Ty powinieneś dowieść, że są równoprawne, jeśli chcesz dalszą dyskusję na tym fakcie opierać.
    Albert


    Zakładając, że x nie jest volatile zgadzamy się chyba, że x+x+x+x można zamienić do 4*x. To raczej oczywiste i jest podstawą działania optymalizatora. Zgadzamy się też co do tego, że kolejność ewaluacji powyższego wyrażenia jest absolutnie dowolna.
    Pozostaje więc tylko pytanie co jeśli x posiada modyfikator volatile. Jasnym jest, że czterokrotne odwołanie się do x może dać inny rezultat niż jednokrotne i w efekcie te dwa zapisy nie są równoważne. To sprecyzujmy problem - czy dodanie volatile nakazuje, aby w ramach wyrażenia było tyle odwołań do zmiennej ile razy ona wystąpiła? Czyli inaczej mówiac, czy nakazuje to wyłączenie optymalizacji takiego wyrażenia?
    IMHO nie i później postaram się znaleźć stosowne fragmenty standardu (a z pobieżnego przeszukania widzę, że standard tego nie precyzuje, czyli mamy dowolność do implementacji). Czyli czy czy volatile powoduje, że x+x+x+x zostaje przetłumaczone na:
    x=x+x; x+=x; x+=x;
    IMHO to byłoby bez sensu (chociaż najwyrażniej twórcy gcc ten wariant zastosowali). A to dlatego, że volatile nie do tego zostało stworzone aby wpływać na kolejność ewaluacji wyrażenia. Ale lećmy po standardzie - punkt 6.7.3.7:
    Cytat:
    7 An object that has volatile-qualified type may be modified in ways unknown to the
    implementation or have other unknown side effects. Therefore any expression referring
    to such an object shall be evaluated strictly according to the rules of the abstract machine,
    as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the
    object shall agree with that prescribed by the abstract machine, except as modified by the
    unknown factors mentioned previously.134) What constitutes an access to an object that
    has volatile-qualified type is implementation-defined.


    Czyli widzimy w miarę jasno, że standaard nie definiuje czym jest dostęp do takiego obiektu, w efekcie wydaje się, że wyrażenie x+x to to samo co 2*x. Trochę rozjaśnia sprawę przypis do tego punktu:
    Cytat:
    A volatile declaration may be used to describe an object corresponding to a memory-mapped
    input/output port or an object accessed by an asynchronously interrupting function. Actions on
    objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as
    permitted by the rules for evaluating expressions.


    Czyli nie możemy wyrzucić odwołań do zmiennej volatile (to oczywiste), nie możemy zmienić ich kolejności (to też oczywiste), ale to dotyczy dostępu przedzielonego punktami sekwencyjnymi. Nie dotyczy to wystąpienia odwołania w ramach jednego wyrażenia (pomiędzy punktami sekwencyjnymi), gdyż mamy wyrażnie "except as permitted by the rules for evaluating expressions" - a te zasady wyraźnie mówią, że x+x to to samo co 2x. Czyli zasady ewaluacji wyrażeń są nadrzędne w stosunku do volatile.
    Dla przykłądu w F.9.2 Expression transformations mamy np. że x-x może być zastąpione zerem, a więc reguły ewaluacji wyrażeń dopuszczają usunięcie zmiennej, także tej volatile skoro są nadrzędne. Czyli jasno widać, że nie powinniśmy w jednym wyrażeniu wielokrotnie używać zmiennej volatile, chyba, że lubimy nieokreślone zachowania.

  • #82 16 Gru 2016 15:02
    Freddie Chopin
    Specjalista - Mikrokontrolery

    jp_elek napisał:
    jeśli wolno na chwilę wtrącić coś bardziej podstawowego ,

    Ja zadam jeszcze bardziej podstawowe pytanie. Te całe magiczne optymalizacje związane z próbami wymuszenia na kompilatorze inline'owania funkcji - one mają jakiś normalny cel? Czy Twój program działa zbyt wolno do REALNEGO zadania jakie przed nim postawiłeś? Może nie wyrabia się w rzeczywistej aplikacji? Zajmuje za dużo pamięci flash/RAM? Jeśli którekolwiek z powyższych jest pasuje do Twojej sytuacji, to w ogóle sprawdziłeś, czy aby na pewno kod o _TEJ_ _SAMEJ_ funkcjonalności wydziargany w assemblerze byłby szybszy/mniejszy/lepszy/...? Bo jeśli Twoim celem jest zmuszenie kompilatora żeby wygenerował jakiś "kod idealny", który sobie zamarzyłeś, to można życzyć tylko powodzenia i pójść swoja drogą.

    Poza tym, czemu niektórzy mają jakąś durną manię z tymi obrazkami? Zamiast do opisu problemu wrzucić _CAŁY_ ten plik .h w którym jest problem oraz _KOMUNIKATY_ kompilatora, skopiowane z odpowiedniego okienka, to się bawisz w te obrazki nikomu do niczego nie potrzebne, bo i tak nie widać na nich najważniejszego. Tylko czekać jak za parę lat w takich tematach ludzie będą potrafili wrzucić tylko filmik z sobą w roli głównej nagrany komórką...

    jp_elek napisał:
    a.) Czy jest jakiś sposób aby to to ( "C") - wstawiło bezwarunkowo kod funkcji czy też nie ma takiego sposobu. ( pytanie oczywiście z podtekstem jeśli mam analizować -w przypadku realnej - nieco bardziej złożonej funkcji inline, to zdecydowanie wolę analizować własny kod asemblerowy niż niespodziankę z komp. C)

    A po co? Jak nie wstawi tego kodu to nagle całość przestanie działać?

    jp_elek napisał:
    b.) Czy wymuszenie sekwencji instrukcji przez prosty zapis w kolejnych liniach zakończonych średnikiem , jest wystarczającym bezwarunkowym wymogiem zachowania kolejności działań ( mówimy o instrukcjach gdzie argumentami są volatile ) ?

    W jakim celu chciałbyś upewniać się o kolejności działań w instrukcjach gdzie argumenty są volatile?

    Bez obrazy, ale przez 3 strony doskonale pokazałeś, że Twoja wiedza o C jest w najlepszym wypadku fragmentaryczna i wyjątkowo nierówna, a zadajesz pytania jakbyś pisał co najmniej system operacyjny. Przecież te dwa powyższe pytania w ogóle są niezbyt sensowne, gdy są wyrwane z kontekstu. No chyba że zamierzasz prowadzić wykłady z programowania na poziomie akademickim, to wtedy może faktycznie warto takie rzeczy wiedzieć, ale bardziej na zasadzie ciekawostki / anegdotki, bo przydatność w rzeczywistości jest bliska zeru. Ot tak, żeby przyszpanować na forum. Wygląda to wiec na typową "chorobę assemblera" - rozwiązywanie problemu pt. "mój wyjątkowo słaby kod w C kompilator przetłumaczył tak, że można by zaoszczędzić jeszcze 0.00001% flasha i przyspieszyć jego działanie o 0.005234% - co robić? jak żyć? chyba wrócę do assemblera!"

    Problem optymalizacji i szybkości kodu na mikrokontrolery jest wydumany do granic niemożliwości. W rzeczywistych zastosowaniach jest zupełnie bez znaczenia. Na układzie który ma kilkanaście MHz można zrobić tyle rzeczy na raz, że głowa pęka i ciężko to potem ogarnąć, a układ przez _ZNACZĄCĄ_ większość czasu i tak się nudzi. I nie jest to jakaś bajka tylko rzeczywistość - przede mną leży układ działający z częstotliwością 24MHz, realizujący 8 wątków, mierzy kilkadziesiąt różnych wielkości z różnych źródeł, analizuje je, loguje, przetwarza, zabezpiecza zewnętrzny układ przed awarią (na podstawie tych właśnie wartości), komunikuje się przez Modbus i normalnego UARTa, itd. Spokojnie ze 20000 linii kodu (pliki mają 52 tysiące linii, ale część to komentarze i nagłówki czy puste linie), nie liczę kodu RTOSa (dosyć durnego i słabego), driverów które wchodzą w skład tego RTOSa itd. Rozmiar całego firmware'u to 160kB i nie ma tam żadnych obrazków czy czegoś takiego. Rzeczywiste obciążenie układu - 14%. Gdybym przez ten tydzień jaki upłynął od założenia tego tematu - podobnie jak Ty to właśnie robisz - dziargał coś usilnie, żeby urwać kilka taktów tu i tam, to obciążenie to spadłoby _MOŻE_ do 13.99999%. Taka jest rzeczywistość. Jeśli chcesz, to możesz próbować napisać taki program w assemblerze - co prawda od dobrych 20-30 lat już nikt takich programów nie pisze, no ale może akurat będziesz prekursorem nowego trendu. Przy tym zakresie funkcjonalności zajmie Ci to z 15 lat i może nawet będzie działać. Finalnie okaże się że zajmuje 10% mniej pamięci i zamiast 14% może obciąża układ tylko w 13%. A może raczej będzie większe i obciąży układ w 100%, w efekcie do niczego nie będzie się nadawać - no chyba że jako przykład "jak nie zabierać się za programowanie". Za to jestem pewny, że kod będzie super optymalny i żadnego nadmiarowego rozkazu tam nie będzie!

    Taki jest właśnie los assemblera. Już wkrótce rok 2017, czas nie działa na waszą korzyść...

  • #83 16 Gru 2016 15:04
    jp_elek
    Poziom 9  

    może już trochę marudzę , ale jeszcze raz do pytania o bezwarunkową ( inline'izację),
    pytanie o tyle istotne iż w tej chwili na placu boju zostało już prawie kilka "XMeg" i zajętość Flash jest coraz częściej mało "bolesna", a szybkość wykonania kodu nadal nęci jako zaleta ...
    J.P.

    o.. Freddie Chopin mnie uprzedził , cóż nie mam nic na swoje usprawiedliwienie , może poza tym iż jeśli coś robię to chcę mieć pewność iż jest to to co zamierzałem, nic więcej .

  • #84 16 Gru 2016 15:13
    Freddie Chopin
    Specjalista - Mikrokontrolery

    tmf napisał:
    IMHO to byłoby bez sensu (chociaż najwyrażniej twórcy gcc ten wariant zastosowali)

    Zaczyna to wyglądać na najeżdżanie na GCC (; Znajdź może choć JEDEN kompilator, który tego tak nie robi, bo coś mi się wydaje, że raczej to nie "twórcy GCC poszli na łatwiznę", tylko po prostu wszyscy tak robią. Zapewne tak robią, bo "tak ma być".
    Dodano po 1 [minuty]:
    jp_elek napisał:
    o.. Freddie Chopin mnie uprzedził , cóż nie mam nic na swoje usprawiedliwienie , może poza tym iż jeśli coś robię to chcę mieć pewność iż jest to to co zamierzałem, nic więcej .

    Jaką więc założyłeś prędkość wykonywania, jaką osiągnąłeś i skąd pomysł, że da się szybciej?

    Sprawa wygląda tak. Masz jakiś kawałek kodu, jego wykonanie zajmuje X taktów rdzenia. Możesz usilnie wymuszać na kompilatorze jakieś optymalizacje typu always_inline czy robić tam wstawki assemblera, rzutować typy na jakieś inne czy robić inne dziwne i mało przenośne rzeczy. W efekcie może, podkreślam - _MOŻE_, będzie działało w czasie 0.97 * X taktów. Gdyby ten sam czas poświęcić na analizę ALGORYTMU, to mogłoby się okazać, że zastosowanie czegoś mądrzejszego i bardziej skomplikowanego, zredukuje czas wykonywania do np. 0.35 * X. Sam sobie teraz odpowiedz co ma większy sens. Już posty na początku Ci powinny pokazać o co chodzi w prawdziwej optymalizacji - przyszedł michalko, wrzucił Ci kod z lepszym algorytmem i działał on kilkukrotnie szybciej niż ten Twój, w którym takimi mikrooptymalizacjami byś zaoszczędził może te 5 czy 10%. Niemniej jednak algorytmy to coś co ciężko dostrzec z poziomu assemblera, bo umysł zajęty jest wtedy w 100% pierdołami typu "do którego rejestru teraz przenieść ten bajt".

  • #85 16 Gru 2016 15:38
    jp_elek
    Poziom 9  

    @ Freddie Chopin ,

    Widać, chyba nie potrafię się wyrażać w sposób zrozumiały dla Ciebie ,
    wszystkie moje pytania w podstawowym znaczeniu dotyczą poprawnej formy zapisu takiej czy innej funkcji/wyrażenia , o ile możliwe też dostępnych wariantów.
    Kwestię przydatności staram się pomijać , dlatego pytania sprowadzam do banalnych "funkcyjek" / wyrażeń by zrozumieć mechanizm , a nie dosłowne rozwiązanie (gotowca) - który przecież jest użyteczny gdy staje się "gwarantowanym" działającym odniesieniem.

    Jeszcze jedna prozaiczna przyczyna , od dziesiątek lat jestem "naprawiaczem" różnych dziwactw , jeśli nie odkryjesz co autor miał na myśli to niewiele zrobisz poza wysłaniem klienta po nowe - widać jakoś to "przykleja się " z czasem jako "zboczenie" może zły nawyk .
    Pozdrawiam J.P.

  • #86 16 Gru 2016 15:45
    Piotrus_999
    Poziom 40  

    Freddie Chopin napisał:
    Zaczyna to wyglądać na najeżdżanie na GCC (; Znajdź może choć JEDEN kompilator, który tego tak nie robi, bo coś mi się wydaje, że raczej to nie "twórcy GCC poszli na łatwiznę", tylko po prostu wszyscy tak robią. Zapewne tak robią, bo "tak ma być".
    Dokładnie oprócz standardu jest jeszcze zdrowy rozsadek. To co masz w standardzie mówi tylko że operacje masz wykonywać _ZAWSZE_ na tej zmiennej a nie na jej reprezentacji np. w rejestrach. A od architektury komputera zależy czy są instrukcje operujace na pamięci czy nie. W RISC takowych nie ma tak źe trzeba za każdym razem czytać lub pisać. I tyle.

  • #87 16 Gru 2016 15:49
    Freddie Chopin
    Specjalista - Mikrokontrolery

    jp_elek napisał:
    wszystkie moje pytania w podstawowym znaczeniu dotyczą poprawnej formy zapisu takiej czy innej funkcji/wyrażenia , o ile możliwe też dostępnych wariantów.
    Kwestię przydatności staram się pomijać , dlatego pytania sprowadzam do banalnych "funkcyjek" / wyrażeń by zrozumieć mechanizm , a nie dosłowne rozwiązanie (gotowca) - który przecież jest użyteczny gdy staje się "gwarantowanym" działającym odniesieniem.

    To czy funkcja jest czy nie jest inline (oraz to czy wymusisz takie inline'owanie atrybutami kompilatora) nie ma znaczenia dla działania kodu. To jak poprawnie definiować funkcje inline jest przedmiotem każdej książki o podstawach C. Mechanizm funkcji inline jest taki sam jak zwykłych. Słowo kluczowe "inline" ma marginalne znaczenie dla kompilatora, w dzisiejszych czasach kompilatory mogą je równie dobrze zignorować i same zadecydować czy przeprowadzić inline'owanie czy nie, co robią bardzo często dla funkcji których definicja jest widoczna, a które owego "inline" wcale nie mają.

    jp_elek napisał:
    jeśli nie odkryjesz co autor miał na myśli to niewiele zrobisz

    Skupiając się na mało istotnych szczegółach typu wymuszanie inline'owania, narzekanie na to że wygenerowany kod nie jest identyczny jak ten który byś (rzekomo) napisał sam czy debaty o kolejności dostępu do zmiennych volatile z pewnością nie pomogą Ci w zrozumieniu "co autor miał na myśli". To nie ten poziom szczegółowości. Aby zrozumieć "co autor miał na myśli" trzeba widzieć algorytmy i wzorce, a nie takie detale. No i oczywiście bardzo dobrze znać język programowania - z definicji nie da się zrozumieć (na poziomie idei) czegoś czego nie rozumiesz (na poziomie zapisu).

  • #88 16 Gru 2016 15:50
    Piotrus_999
    Poziom 40  

    jp_elek napisał:
    Widać, chyba nie potrafię się wyrażać w sposób zrozumiały dla Ciebie ,
    wszystkie moje pytania w podstawowym znaczeniu dotyczą poprawnej formy zapisu takiej czy innej funkcji/wyrażenia , o ile możliwe też dostępnych wariantów.
    Freddy Ci napisał - te mikrooptymalizacje mają sens jak Twój algorytm jest OK. Dodajmy - aby one były skuteczne naprawdę musisz poznać bardzo dobrze język i nabrać sporej praktyki - tak żebyć pisząc widział w wyobrażni jak to będzie dziłać na wyższym i niższym poziomie.

    To jest niestety bardzo zły nawyk z asemblera - gdzie nie widzisz algorytmu tylko jakies flagi i skoki.

  • #89 16 Gru 2016 15:56
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Żeby znów nie być gołosłownym:

    Cytat:
    6 A function declared with an inline function specifier is an inline function. Making a
    function an inline function suggests that calls to the function be as fast as possible. 138)
    The extent to which such suggestions are effective is implementation-defined. 139)
    [...]
    139) For example, an implementation might never perform inline substitution, or might only perform inline
    substitutions to calls in the scope of an inline declaration.

  • #90 16 Gru 2016 16:07
    Piotrus_999
    Poziom 40  

    No ale to jest gcc i atrybut __attribute__((always_inline)) i attribute__((noinline)) wymuszają odpowiednie zachowanie. Nie są juz sugestią tylko imperatywem

TME logo Szukaj w ofercie
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME
TME Logo