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

Narzut kompilatora dla __memx przy użyciu tablicy w pamięci Flash powyżej 64kB

robiw 13 Mar 2017 07:50 1092 15
  • #1 16342119
    robiw
    Poziom 26  
    Witam,
    Za namową Kolegów zmieniłem podejście do umieszczania danych w pamięci Flash i zamiast makr pgm_read_xxx i atrybutu PROGMEM zacząłem stosować __memx, które jest wygodne zwłaszcza, gdy przekraczamy barierę 64kB. Zadeklarowałem tablicę, jak niżej:

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


    i prostą, przykładową funkcję, jak niżej:

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


    W wyniku kompilacji kompilator wygenerował dość pokaźny kod wynikowy (Os), gdzie, ku mojemu zaskoczeniu, odkładane są rejestry, jak dla funkcji obsługi przerwań:

    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod


    W momencie, gdy nieco bardziej rozbuduję powyższą funkcję (np. wywołuję w jej ciele inne, bardzo proste funkcje) tych rejestrów odkładanych jest nawet kilkanaście. To dość wysoka cena wykorzystania __memx. Czy to normalne? robiw
  • #3 16342185
    robiw
    Poziom 26  
    Nie przekraczam, próbowałem w celach testowych, zadeklarowałem jedną tablicę ok. 6kB. Dla __flash nie sprawdzałem...robiw
  • #4 16342206
    tmf
    VIP Zasłużony dla elektroda
    @robiw Te rejestry są odkładane na stos nie z powodu __memx, wynika to z ABI kompilatora. W ABI zakładane jest, że stan pewnych rejestrów nie może być zmieniany przez funkcję i stąd są one odkładane na stos. Kompilator może to potem zoptymalizować, ale nie zobaczysz tego w tak trywialnym przykładzie. Zauważ, że sprawa dotyczy funkcji TFTdrawPicture a nie odwołań poprzez __memx.
  • #5 16342216
    robiw
    Poziom 26  
    Sprawdziłem...dla __flash funkcja jest znacznie krótsza i nie odkłada rejestrów. Niemniej jednak, tym razem, zauważyłem, że w kodzie asemblera innej funkcji mam odkładanie rejestrów i "ret", baaardzo prostej funkcji, niezwiązanej z odczytem pamięci Flash czy przerwaniami. Kiedy kompilator wykonuje tego typu "manewr"? robiw

    Dodano po 2 [minuty]:

    tmf napisał:
    Zauważ, że sprawa dotyczy funkcji TFTdrawPicture a nie odwołań poprzez __memx.


    OK, dzięki, niemniej jednak w ciele funkcji TFTdrawPicture dokonuje się odczyt pamięci Flash. Tak, czy inaczej, narzut, patrząc na skompilowany kod, jest dość duży dla tak trywialnego przykładu... robiw
  • #7 16342294
    michalko12
    Specjalista - Mikrokontrolery
    robiw napisał:
    W momencie, gdy nieco bardziej rozbuduję powyższą funkcję (np. wywołuję w jej ciele inne, bardzo proste funkcje) tych rejestrów odkładanych jest nawet kilkanaście. To dość wysoka cena wykorzystania __memx. Czy to normalne? robiw

    No niestety, __memx to już arytmetyka na 24b wskaźnikach, a to dla AVR jest już te parę taktów więcej przy dodawaniu i odejmowaniu. Kompilator wykorzystuje większą ilość rejestrów i musi więcej odłożyć ich na stos.
    Chcesz efektywności na operacjach >16b, korzystaj z procesorów 32b. Można tutaj jeszcze pokombinować przechodząc w samej funkcji z __memx na __flashN w celu przyśpieszenia tej funkcji, ale to nie będzie duży zysk i gra raczej nie warta świeczki. Gdybyś używał pgm get_far_address() wcale to lepiej by nie wyglądało, a wręcz mogłoby być jeszcze gorzej, bo te makro zwraca 32b wartość. Możesz wrócić do wersji z PGM_XXX i porównać.
  • #9 16342333
    michalko12
    Specjalista - Mikrokontrolery
    ASMnauka_ napisał:
    Owszem, jeżeli jest to konieczne (brak wolnych\nie wykorzystanych rejestrów)
    O wiele lepszym rozwiązaniem jest skopiowanie do wolnych rejestrów.

    Dla kompilatora C pojęcie "wolne rejestry" nie istnieje. Wolne one są tylko wtedy gdy ich zawartość zostanie odłożona na stos. Autor wątku nie posługuje się assemblerem więc po co mieszasz? Ja nie znając bascoma nie wypowiadam się w takowych wątkach.
  • #10 16342348
    Konto nie istnieje
    Konto nie istnieje  
  • #11 16342354
    ASMnauka_
    Poziom 15  
    michalko12 napisał:
    Dla kompilatora C pojęcie "wolne rejestry" nie istnieje.
    I to jest jeden z wielu mankamentów kompilatora.
    michalko12 napisał:
    Autor wątku nie posługuje się assemblerem więc po co mieszasz?
    Zgadza się, lecz sam wiesz, iż kompilator nie jest doskonały.
    Nie zapanujesz nad nim :(
    Wstawki ASM są stosowane bardzo często.
    Ja podałem jedynie przykład.
    Inna kwestią jest stosowanie dwóch pętli przy odczycie.
    Jest to zbędne.
    michalko12 napisał:
    Ja nie znając bascoma nie wypowiadam się w takowych wątkach.

    Nie jest to kod Bascom-a ale ASM.
  • #13 16342382
    michalko12
    Specjalista - Mikrokontrolery
    robiw napisał:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Czy to jest właściwy kod tej funkcji? Jeśli tak to dlaczego do 8b portu ładowane są 16b dane? Jaki format ma tablica Picture[]?
  • #14 16342483
    robiw
    Poziom 26  
    To przykładowy kod. Funkcja wysyłała dane do wyświetlacza, ale dla uproszczenia kodu wynikowego usunąłem to i dałem PORTB by kompilator w ogóle tego nie usunął. robiw

    Dodano po 1 [minuty]:

    Piotrus_999 napisał:
    Kolego @robiw. W Twoim poprzednim wątku napisałem Ci o tym (ostrzegając przed ceną) z przykładami : https://www.elektroda.pl/rtvforum/topic3314557.html#16329257 ale jak widać szkoda się dla Ciebie męczyć bo i tak nie czytasz postów.

    A tak już poza konkursem najlepiej porzucić te 8bitowce w cholerę. To tak jak byś się upierał w PC-ie że koniecznie musisz miec 386


    Czytam, czytam, dziękuję. Po prostu teraz sprawdziłem to praktycznie... robiw
  • #15 16342506
    michalko12
    Specjalista - Mikrokontrolery
    robiw napisał:
    To przykładowy kod. Funkcja wysyłała dane do wyświetlacza, ale dla uproszczenia kodu wynikowego usunąłem to i dałem PORTB by kompilator w ogóle tego nie usunął. robiw

    No to nie ma o czym dyskutować. W przeciwnym razie można byłoby pokusić się o pewne optymalizacje zmniejszające kod samej funkcji.
  • #16 16342903
    tmf
    VIP Zasłużony dla elektroda
    robiw napisał:
    Sprawdziłem...dla __flash funkcja jest znacznie krótsza i nie odkłada rejestrów. Niemniej jednak, tym razem, zauważyłem, że w kodzie asemblera innej funkcji mam odkładanie rejestrów i "ret", baaardzo prostej funkcji, niezwiązanej z odczytem pamięci Flash czy przerwaniami. Kiedy kompilator wykonuje tego typu "manewr"? robiw

    Dodano po 2 [minuty]:

    tmf napisał:
    Zauważ, że sprawa dotyczy funkcji TFTdrawPicture a nie odwołań poprzez __memx.


    OK, dzięki, niemniej jednak w ciele funkcji TFTdrawPicture dokonuje się odczyt pamięci Flash. Tak, czy inaczej, narzut, patrząc na skompilowany kod, jest dość duży dla tak trywialnego przykładu... robiw


    Jak już ci koledzy napisali, 24-bitowa arytmetyka wymaga na AVR większej liczby operacji niż 16-bitowa, stąd widzisz tego efekt. Jak wiesz, lista rozkazów AVR nie jest w pełni ortogonalna, a więc pewne operacje, m.in. dostępu do pamięci przez wskaźnik są realizowane tylko przy pomocy wybranych rejestrów. Więc jest duża szansa, że trzeba je zachować, bo już są wykorzystywane. W dodatku, jak pisałem, ABI kompilatora zakłada, że pewne rejestry nie są niszczone przez funkcję, więc są zachowywane bo tak. I tu popełniasz kolejny błąd - nie można tak analizować funkcji. Bo optymalizator (i kompilator) tworzą funkcje dopasowane do sposobu ich wywoływania. I to, że jeden wariant wygląda tak, to nie znaczy, że w miejscu w którym wywołasz funkcję tak zostanie ona skompilowana. Może się okazać, że zostanie ona znacznie zoptymalizowana, bo kompilator zauważy, że Z jest wolny i nie ma sensu go zachowywać na stosie. I jeszcze raz, bo może to nie jest jasne - obserwowane zachowywanie rejestrów na stosie, to nie efekt __memx, lecz otoczenia funkcji w połączeniu ze stosowaniem __memx.
REKLAMA