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

AVR-GCC - Optymalizacja dostępu do struktury przez GCC?

robiw 11 Cze 2014 20:06 2829 25
  • #1 13699033
    robiw
    Poziom 26  
    Witam Kolegów,
    Wiem, mógłbym to sprawdzić, ale może ktoś zna odpowiedź bez sprawdzania. "Zwykłe" zmienne, które używane są w pętli głównej jak i w przerwaniu należy zaopatrzyć w specyfikator volatile... z wiadomych względów. Ja to jest w przypadku pól struktur? Jeśli struktura używana jest zarówno w pętli głównej jak i w przerwaniu to musi być volatile, czy tak jak w przypadku tablic nie potrzeba tego robić? A co z tablicami struktur? robiw

    PS.
    Pytam bo mam funkcje, do której przekazuję przez wartość np. pole struktury a struktura ta (jej pola) może podlegać zmianie w ISR. Gdy daję jej polom specyfikator volatile to GCC "krzyczy", że parametr wywołania funkcji nie jest volatile a przekazuję mu element volatile. Z drugiej strony nie chcę nadawać argumentowi wywołania funkcji specyfikatora volatile bo nie wszystkie argumenty, które jej przekazuje sa volatile...
  • #2 13699213
    BlueDraco
    Specjalista - Mikrokontrolery
    A czym niby różni się struktura czy tablica od innej zmiennej? Atrybut volatile działa w każdym przypadku tak samo.

    Zdecyduj, czy potrzebujesz atrybutu volatile dla argumentu funkcji. Jeśli tak - zadeklaruj argument funkcji jako volatile. Jeśli nie - zrzutuj typ przy wywołaniu funkcji.
  • #3 13699229
    robiw
    Poziom 26  
    Dla tablicy, jak rozumiem, GCC nie zastosuje optymalizacji, więc używana i w main i w ISR nie musi być volatile bo i tak każdorazowo będzie pobierana dana z odpowiedniej komórki RAM. W funkcji nie potrzebuję volatile. Nie mam pewności, czy rzutowanie zapewni odpowiedni dostęp do zmiennej...robiw
  • #4 13699589
    gaskoin
    Poziom 38  
    Zależy w jakim przypadku. Mówisz tak ogólnie, że nie da się odpowiedzieć na Twoje pytania. Zarzuć przykładowym kodem. Sama tablica nie musi być ulotna, ale dane w niej już mogą. Zależy, czy zmieniasz dane w tej tablicy, czy zmieniasz tablice... Generalnie w przypadku wątpliwości zajrzeć do asemblera. Zależy to też od wersji GCC - przed Gcc 4.x dla volatile w szczególnych przypadkach optymalizator źle się zachowywał, ale to Cię raczej nie dotyczy.
  • #5 13699600
    BlueDraco
    Specjalista - Mikrokontrolery
    Dość beztroskie robisz założenia. Fakt, na ogół Twoja wróżba się sprawdzi, ale to chyba nie jest poważne założenie do jakiegokolwiek projektu. Nie jest prawdą, że "każdorazowo będzie pobierana dana z odpowiedniej komórki RAM" - jeśli ten sam element wektora będziesz np. testował w pętli, to nie ma powodu, by kompilator nie zassał go z pamięci tylko raz.
    Nie wiem, co to jest "odpowiedni dostęp do zmiennej", o którym piszesz.
  • #6 13700001
    robiw
    Poziom 26  
    OK,
    Powiedzmy, że mamy strukturę:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Pola tej struktury aktualizowane są w ISR oraz sprawdzane są w main jak i przekazywane do funkcji takich jak np:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Również w main mogą być testowane czy nawet zapisywane dane do tej struktury. Mam świadomość, iż muszę przemyśleć założenia...robiw
  • #7 13700034
    Konto nie istnieje
    Konto nie istnieje  
  • #8 13700200
    robiw
    Poziom 26  
    OK. Przeanalizowałem kod pod kątem volatile. W tej chwili specyfikator ten posiadają te pola struktury, które mogą zmieniać swój stan w ISR i które to odczytywane są w main, pozostałe pola pozostawiłem bez volatile. Niestety, jedno z pól, a mianowicie pole tablicy Tab[8] opatrzone tym specyfikatorem daje ostrzeżenie przy wbudowanej funkcji:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    o treści "passing argument 2 of 'memcmp' discards qualifiers from pointer target type", więc juz nie wiem, czy tablicy tej nadawać tenże specyfikator.

    BlueDraco napisał:
    ...Nie jest prawdą, że "każdorazowo będzie pobierana dana z odpowiedniej komórki RAM" - jeśli ten sam element wektora będziesz np. testował w pętli, to nie ma powodu, by kompilator nie zassał go z pamięci tylko raz...


    Jest powód moim zdaniem, jeśli elementy tej tablicy mogą być modyfikowane w ISR. Z drugiej strony, na znanym skądinąd blogu, czytałem, iż nie ma potrzeby stosowania volatile do tablic, gdyż nie jest optymalizowany do nich dostęp tak jak ma to miejsce w przypadku "zwykłych", nietablicowych zmiennych, których wartość, jeśli nie są volatile, może być przechowywana w rejestrze...

    robiw
  • #9 13700245
    tmf
    VIP Zasłużony dla elektroda
    Owszem, do elementów tablic dostęp też może być optymalizowany. Który to blog? Jaki kompilator miałby powód aby w kółko odczytywać ten sam element tablicy?
    Co do memcmp - spójrz na prototyp tej funkcji - nie zawiera volatile, prawda? A więc w sposób niejawny zrzucasz modyfikator, stąd też masz ostrzeżenie. Swoją drogą, jeśli ta tablica jest modyfikowana w ISR, które może być wywołane w czasie działania memcmp to musisz zadbać także o atomowość operacji memcmp, w przeciwnym przypadku, czasami otrzymasz sieczkę.
  • #10 13700295
    robiw
    Poziom 26  
    tmf napisał:

    Co do memcmp - spójrz na prototyp tej funkcji - nie zawiera volatile, prawda? A więc w sposób niejawny zrzucasz modyfikator, stąd też masz ostrzeżenie.

    Prawda, to wiem, ale jak to obejść? Nie chcę buforować zawartości tej tablicy do tymczasowej tablicy, nie volatile.
    tmf napisał:

    Swoją drogą, jeśli ta tablica jest modyfikowana w ISR, które może być wywołane w czasie działania memcmp to musisz zadbać także o atomowość operacji memcmp, w przeciwnym przypadku, czasami otrzymasz sieczkę.


    Nie muszę, funkcji tych (memcpy i memcmp) używam właśnie w tej ISR... robiw
  • #12 13700322
    robiw
    Poziom 26  
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Działa. Rozumiem, że w tym przypadku, gdy funkcja używana jest w ISR to tylko kwestia zapisu, zaś w innym przypadku (gdy byłaby używana w main) należałoby zastosować:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    robiw
  • #14 13700351
    robiw
    Poziom 26  
    Dzięki. Człowiek całe życie się uczy... Jakoś mi weszło do głowy, że dla tablic volatile nie jest niezbędne...bo optymalizacja dostępu nie będzie w tym przypadku wykonana. Podążając tym tokiem myślenia, dla bardziej skomplikowanych typów zmiennych jak struktury (choć w sumie to zwykłe zebrane razem zmienne) czy tablice struktur (tutaj już pewności nie miałem), można byłoby się spodziewać, że jest podobnie...tymczasem rozumiem, że należałoby przyjąć ogólną zasadę (od której są wyjątki):

    zmienna dowolnego typu modyfikowana (i nieważne czy zapis czy odczyt) w ISR -> volatile? robiw
  • #15 13700386
    tmf
    VIP Zasłużony dla elektroda
    Tak. Zauważ, że jeśli kompilator sam od siebie nie optymalizuje dostępu to volatile jest zupełnie nieszkodliwe - dobrze to widać przy -O0, kiedy wyłączymy optymalizację i wszystkie zmienne zachowują się tak jakby miały volatile (dla gcc). Natomiast volatile zaczyna "działać" jeśli kompilator jednak jakiś dostęp chciałby zoptymalizować. Na AVR może się wydawać, że dostępy do tablic lub struktur nie są opytmalizowane, ze względu na sposób adresacji. Np. dostęp do struktur często realizowany jest poprzez rejestr indeksowy, co prawda AVR ma 3 takie rejestry, ale do wykorzystania w celu adresacji struktury jest tylko jeden (jeden nie ma pełnej funkcjonalności, a Y jest wykorzystywany do indeksowania stosu). Stąd często kompilator przeładowuje rejestr indeksowy i nie potrafi pewnych elementów zoptymalizować, nie znaczy to jednak, że nie próbuje i "niestety" czasami mu się to udaje. Jego wysiłki można jawnie wspomóc stosując atrybuty const i pure (atrybuty nie modyfikatory).
    BTW, na jakim blogu widziałeś te rewelacje?
  • #16 13700399
    robiw
    Poziom 26  
    Co do blogu to było to tak dawno, że nie pamiętam dokładnie - tak dawno, że utrwaliłem sobie taką informację, choć nie mówię na 100%. Tutaj jest np. o argumencie sprintf: http://forum.atnel.pl/post34515.html#p34515. Nie znaczy to jednak, że widziałem to tam...robiw

    A jednak tutaj... i mówi to sam Mirek:

    mirekk36 napisał:

    "chodzi o to, że dostęp do tablicy musi być i tak zawsze realizowany w asemblerze w oparciu o pary rejestrów indeksowych i nie da się tego zoptymalizować jak dostępu do pojedynczej zmiennej, którą można wrzucić w pojedynczy rejestr i już. "


    http://forum.atnel.pl/post18205.html#p18205

    Kolejny cytat:
    mirekk36 napisał:

    "Rozwiązanie jest proste, nie trzeba żadnego rzutowania, nie trzeba pisać własnej funkcji po prostu pozbądź się specyfikatora volatile dla tablic, bo dla tablic nie trzeba."


    Stąd pomyślałem, że dla tablic struktur także nie trzeba... robiw

    Dodano po 45 [minuty]:

    ... teraz pytanie, który z Was ma rację? Mógłbyś podać jakieś linki do źródeł, z których pozyskałeś taka wiedzę? Z góry dziękuję... robiw
  • #17 13700578
    BlueDraco
    Specjalista - Mikrokontrolery
    Mistrz MK jak zwykle popłynął. Dlaczego mnie to nie dziwi...
    Sam pomysł, by wykonywać operacje na elementach wektora w czasie, gdy mogą one być modyfikowane, wygląda podejrzanie. Blokowanie przerwań na czas tych operacji - to tylko maskowanie błędu koncepcji. Jeśli wiesz, że operacje na wektorze będą wykonywane w czasie, gdy procedura przerwania go nie ruszy - możesz spokojnie zrzutować typ wskaźnika usuwając volatile. Dotyczy to tego konkretnego przypadku - wywołania procedury operującej na wektorze. Gdyby to nie była procedura - nie byłoby już tak łatwo.

    Wypadałoby w tym momencie zasugerować zapytanie MK, skąd wziął pomysł, że optymalizacja dostępów nie dotyczy tablic czy struktur. Skoro standard na ten temat milczy, nie rozróżniając znaczenia volatile w zależności od typu, do którego modyfikator jest stosowany, to wszelkie teorie nt. innego traktowania tablic należy traktować jako pomysły własne ich autora, niepoparte niczym poza głębokim przekonaniem tego, kto je wymyślił.

    Jeśli jednak jesteś zarejestrowany na forum Atnel i chesz zachować ten status - nie zadawaj tego pytania na tamtym forum - zapewne zostaniesz usunięty, jak wszyscy, którzy poddają publicznie w wątpliwość tezy MK.
  • #18 13700599
    robiw
    Poziom 26  
    Kolego, już zostałem usunięty z tego forum. W zasadzie to nie forum a ołtarzyk Autora...szkoda, szczerze żałuję i współczuję braku dystansu, acz dziwię się zapatrzeniu forumowiczów. Nawet Sun...został odstawiony na bocznicę... Jeszcze większa szkoda :-(. Sugerowanie zapytania do MK odradzam... choć ciekaw jestem odpowiedzi... Rozumiem, że jesteś pewien swojej opinii w temacie tablic? Znam mechanizm dostępu do elementów poprzez indeksowanie, toteż kupiłem temat bez problemu...robiw
  • #19 13700646
    Freddie Chopin
    Specjalista - Mikrokontrolery
    robiw napisał:
    Rozumiem, że jesteś pewien swojej opinii w temacie tablic?

    Napisz taki kod:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Na 99% przy optymalizacji będzie taki sam problem jak ze zwykłymi ("nie-tablicowymi") flagami.

    Kwestia volatile jest bardzo skomplikowana, reguła "zawsze używaj volatile" jest pewnym uproszczeniem, ponieważ czasami można go nie używać i będzie dobrze. Zwykle volatile jest potrzebne przy czymś co sprawdzane jest w pętli, za to jeśli jednokrotnie przekazywane jest do jakiejś funkcji przez wartość, to zwykle jest zbędne.

    4\/3!!
  • #20 13700673
    grko
    Poziom 33  
    Wszystko zależy od kontekstu w jakim używa się zmienne volatile. Wezmy najprostszy przypadek:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiście wiadomo, że to może nie zadziałać tak jak chcemy gdy optymalizacja jest właczona. Można to rozwiązać na kilka sposobów.

    1) Robimy tablice volatile:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    2) Robimy indeks volatile:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiście rozwiązanie nr 2 jest lepsze ale może to nie zadziałać w takim przypadku:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #21 13700702
    robiw
    Poziom 26  
    Przychodzi mi jednak do głowy szczególny przypadek. Powiedzmy, że ISR wyłącznie dodaje nowe elementy do tablicy struktur (wypełnia je danymi) i ustawia flagę dla pętli głównej, że pojawił się nowy element do wykorzystania przez nią. Pętla główną widząc ta flagę przekazuje do pewnej funkcji korzystającej z tych danych wskaźnik do nowo wypełnionego elementu tj. wskaźnik do elementu tablicy struktur. W takim wypadku wydaje mi się, iż funkcja ta nie musi wykonywać operacji atomowych bo struktura do której wskaźnik jej przekazano nie ulegnie zmianie. Co najwyżej ISR doda nowy element w czasie wykonywania operacji na tejże strukturze, ale nie zmieni jej samej. Oczywiście można by całe ciało tej funkcji zrobić atomowym, ale jeśli wykonuje się ona sporo czasu to byłby to błąd. Można też na wstępie utworzyć lokalna kopie tej struktury. Widzisz tu jakieś zagrożenia? robiw

    Dodano po 2 [minuty]:

    Oczywiście w tym miejscu odnoszę się do atomowosci operacji na przekazanym elemencie nie zaś do jego ulotnosci, która pozostawiam bez zmian...
  • #22 13700941
    tmf
    VIP Zasłużony dla elektroda
    W przedstawionym przez ciebie przykładzie istotnie można zapomnieć o atomowości i volatile. Jest to jeden z wyjątków o których pisze kol. Freddie Chopin. Jednak zawsze warto dokładnie przemyśleć mechanizm dostępu do danych. Z drugiej strony - być może atomowy w takiej sytuacji będzie musiał być dostęp do samego wskaźnika. Jeśli będzie on na AVR 2-bajtowy, to samo przepisanie będzie musiało odbyć się atomowo. Po tej operacji funkcja będzie dysponować lokalnym wskaźnikiem i dostęp do samej tablicy już atomowy być nie musi.
  • #23 13701015
    robiw
    Poziom 26  
    Nie bardzo jednak widzę potrzebę atomowości pozyskiwania wskaźnika. Poza tym, skoro przekazuje ten argument w wywołaniu funkcji to jak mam zapewnić atomowość przekazania i właściwie po co? Wróćmy jeszcze raz:

    - ISR dodaje w dowolnej chwili nowe elementy tablicy struktur ustawiając flagę dla pętli głównej, że jest co najmniej jeden, nowy element. Prosta funkcja w pętli głównej "leci" po indeksach tablicy i wyszukuje pierwszą, nowo wypełnioną strukturę zapamiętując jej index. Po tym, wywołuje funkcje operująca na tych nowych danych i do funkcji przekazuje wskaźnik do tego elementu np.: funkcja(&tablica_struktur[ten_znaleziony_index]) a sama funkcja korzysta z tych danych (tylko czyta) - gdzie jest tu miejsce na przekazanie złego wskaźnika? robiw

    Dodano po 4 [minuty]:

    tmf napisał:
    W przedstawionym przez ciebie przykładzie istotnie można zapomnieć o atomowości i volatile


    Jak to, o volatile zapomnieć nie można bo przecież ISR modyfikuje ta tablicę a main z niej czyta...chyba, że miałeś na myśli volatile jako wskaźnik wywołania funkcji korzystającej z tych danych...robiw

    Dodano po 32 [minuty]:

    Podobna, szczególna sytuacja dotyczy, moim zdaniem następującego przypadku. Mamy funkcję, która przyjmuje np. następujący argument funkcja(uint8_t argument) i przekazujemy jej element tablicy struktur, który jest volatile. GCC będzie krzyczał, ale czy naprawdę niezbędne jest w tym wypadku deklarowanie tego argumentu funkcji jako volatile? Nie wystarczy wykonać rzutowania tego argumentu typu (uint8_t)? Przecież funkcja ta musi pobrać przekazany jej argument? Jak to widzicie? A jeśli argumentem funkcji jest wskaźnik do uint8_t to także nie wystarczy elementowi, który jest volatile i przekazywany jest jako argument wykonać rzutowania typu (uint8_t *)? robiw
  • #24 13701945
    tmf
    VIP Zasłużony dla elektroda
    Zakładając, że realizujesz np. bufor do którego można tylko dodawać nowe elementy z jednego końca i usuwać z drugiego. Potrzebujesz dwa wskaźniki - początku i końca bufora. W takiej sytuacji sam bufor nie musi być volotile - w ISR tylko dodajesz element, a w programie tylko pobierasz. Lecz w obu miejscach używasz wskaźników, które są modyfikowane i w ISR i w prog. głównym. Wskaźnik jest 16-bitowy, jak pobrać jego wartość w programie, jeśli w czasie pobierania jego wartość może się zmienić? Musisz zapewnić atomowe przepisanie jego wartości na wskaźnik tymczasowy.
    Co do twojego drugiego przykładu - jeśli funkcja przyjmuje po prostu wartość to volatile nie jest potrzebne i nie ma sensu. Kompilator przy przekazaniu wartości też nie będzie protestował bo nie ma czegoś takiego jak volatile wartość. volatile ma sens tylko przy odwołaniu do pamięci, a jeśli przekazujesz wartość do funkcji to takiego odwołania nie ma - coś takiego jak void func(volatile int) nie ma sensu.
  • #25 13702186
    Freddie Chopin
    Specjalista - Mikrokontrolery
    robiw napisał:
    Mamy funkcję, która przyjmuje np. następujący argument funkcja(uint8_t argument) i przekazujemy jej element tablicy struktur, który jest volatile. GCC będzie krzyczał

    Może weź najpierw to sprawdź, bo teraz już trochę "popłynąłeś".

    4\/3!!
  • #26 13702790
    gaskoin
    Poziom 38  
    Funkcje mają parametry, a to co do nich przekazujemy (konkretne wartości) to są argumenty.

    Nie żebym się czepiał, ale fajnie jest wiedzieć o czym ktoś do Ciebie mówi.

    Btw tworzysz wspaniałe opisy słowno-muzyczne, ale ja trochę ich nie kumam :) Jednak super by było gdybyś wspomagał swoje słowa jakimiś przykładami kodu. Np z tego:

    robiw napisał:
    Mamy funkcję, która przyjmuje np. następujący argument funkcja(uint8_t argument) i przekazujemy jej element tablicy struktur, który jest volatile. GCC będzie krzyczał, ale czy naprawdę niezbędne jest w tym wypadku deklarowanie tego argumentu funkcji jako volatile? Nie wystarczy wykonać rzutowania tego argumentu typu (uint8_t)? Przecież funkcja ta musi pobrać przekazany jej argument? Jak to widzicie? A jeśli argumentem funkcji jest wskaźnik do uint8_t to także nie wystarczy elementowi, który jest volatile i przekazywany jest jako argument wykonać rzutowania typu (uint8_t *)? robiw
    Nie zrozumiałem nic :P Tylko tmf chyba zrozumiał, ale on w końcu jest wybitnym i wszechstronnym naukowcem.
REKLAMA