atarionline.pl Optymalizacje w CC65 - Forum Atarum

Jeśli chcesz wziąć udział w dyskusjach na forum - zaloguj się. Jeżeli nie masz loginu - poproś o członkostwo.

  • :
  • :

Vanilla 1.1.4 jest produktem Lussumo. Więcej informacji: Dokumentacja, Forum.

    • 1: CommentAuthorilmenit
    • CommentTime23 Apr 2020 zmieniony
     
    Napisałem obiecanego w jednym wątku małego artka o optymalizacjach w CC65:
    ->link<-
    Miłego czytania i czekam na komentarze i pytania :)
    • 2: CommentAuthorzbyti
    • CommentTime23 Apr 2020 zmieniony
     
    WoW! To zabieram się za lekturę! :]

    EDIT: dla mnie ten artykuł wygrywa dzisiaj internety! ;)
    • 3:
       
      CommentAuthorbocianu
    • CommentTime23 Apr 2020
     
    Kawał dobrej roboty.
    Dzięki!
    • 4:
       
      CommentAuthorKaz
    • CommentTime23 Apr 2020
     
    Pięknie panie Ilmenit, ja też poczytam.
    • 5: CommentAuthorzbyti
    • CommentTime23 Apr 2020
     
    Przeczytane od dechy do dechy! Napisane znakomicie bardzo przejrzystym i jasnym stylem!

    Błędów w tekście nie znalazłem, w kodzie nie szukałem ;)

    Teraz zabieram się za czytanie kodu benchmarków, może parę machnę w Action! dla porównania?

    @ilmenit ten art. to pełna profeska i mam nadzieję, że w społeczności 8-bit odbije się głośnym echem i stanie przyczynkiem do pisania podobnych materiałów albo dyskusji na forach.
    • 6: CommentAuthorilmenit
    • CommentTime23 Apr 2020
     
    @zbyti - dzięki za miłe słowa :)
    • 7:
       
      CommentAuthorpirx
    • CommentTime23 Apr 2020
     
    kudos!
    • 8:
       
      CommentAuthorjhusak
    • CommentTime23 Apr 2020
     
    Po optymalizacjach CC65 jest prawie idealny (tzn dość dobrze przekłada się na optymalny kod 6502) - mi osobiście brakuje actionowego przekazywania parametrów przez rejestry AXY. Chyba, że można jakoś to obejść? Bo inlainy można obejść definami.
    • 9: CommentAuthorilmenit
    • CommentTime23 Apr 2020
     
    W CC65 jest fastcall, ktory przekazuje parametry przez rejestry A/X: ->link<-
    • 10: CommentAuthortr1x
    • CommentTime23 Apr 2020
     
    Czy poniższej zasadny też należy się trzymać?

    "7. Make the size of your array elements one of 1, 2, 4, 8

    When indexing into an array, the compiler has to calculate the byte offset into the array, which is the index multiplied by the size of one element. When doing the multiplication, the compiler will do a strength reduction, that is, replace the multiplication by a shift if possible. For the values 2, 4 and 8, there are even more specialized subroutines available. So, array access is fastest when using one of these sizes."

    [https://cc65.github.io/doc/coding.html]
    • 11: CommentAuthorilmenit
    • CommentTime23 Apr 2020 zmieniony
     
    EDIT: przeczytałem jeszcze raz, więc kasuję poprzedni wpis.

    W tekście nie chodzi o wielkość tablicy, ale wielkość elementu tablicy, która powinna być 1,2,4,8.
    Zatem chodzi o sizeof(my_type) w kodzie:
    my_type table[TABLE_SIZE];

    ale dla szybkiego kodu taką "tablica struktur" jak pisałem warto przerobić na "strukturę tablic".
    • 12: CommentAuthormariuszw
    • CommentTime24 Apr 2020
     
    @Ilmenit

    Świetny artykuł! Jasny, klarowny, bardzo merytoryczny. Zawiera pewnie wszystko, co trzeba wiedzieć w temacie C na 6502. Dzięki, że Ci się chciało, bo jak widzę sporo pracy w to poszło.

    Mam nadzieję, że uda Ci się odczarować C na 6502 - bo łatka "to się do niczego nie nadaje, bo za wolne i za duży kod generuje" bardzo przylgnęła.
    • 13: CommentAuthorilmenit
    • CommentTime24 Apr 2020
     
    @mariuszw - dzięki! Główny problem z pisaniem w dla 6502 jest taki, że ludzie próbują pisać tak, jak by pisali na "duże" platformy - dynamicznie alokują pamięć, używają głównie wskaźników, intów lub skomplikowanych struktur. To wszystko są "najlepsze praktyki" współcześnie. Ale tu trzeba prościej, bo niezależnie od tego jak dobry będzie kompilator, nie zoptymalizuje on użycia wskaźników, które do zaadresowania pamięci muszą być 16bitowe, czy skomplikowanych struktur danych. Mam nadzieję, że udało mi się to pokazać.
    • 14: CommentAuthorantrykot
    • CommentTime24 Apr 2020
     
    By the definition of the C language, enums are "ints"

    Nieprawda.
    • 15: CommentAuthorilmenit
    • CommentTime24 Apr 2020 zmieniony
     
    A nie jest to tak?

    ->link<-

    6.7.2.2 Enumeration specifiers
    Constraints
    The expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an int.

    Juz widze:
    Each enumerated type shall be compatible with char,asigned integer type, or an unsigned integer type. The choice of type is implementation-defined,110) but shall be capable of representing the values of all the members of the enumeration. The enumerated type is incomplete until after the } that terminates the list of enumerator declarations.
    110) An implementation may delay the choice of which integer type until all enumeration constants have been seen.
    • 16:
       
      CommentAuthorbocianu
    • CommentTime24 Apr 2020
     
    Przeczytałem do końca. Świetny artykuł. Powinna to być lektura obowiązkowa, dla każdego kto się łapie za CC65. Gdybym tylko wiedział to wszystko dużo wcześniej, jak zaczynałem. Być może powstałoby więcej gier w CC65 ;)

    Główny problem z pisaniem w dla 6502 jest taki, że ludzie próbują pisać tak, jak by pisali na "duże" platformy - dynamicznie alokują pamięć, używają głównie wskaźników, intów lub skomplikowanych struktur.


    No niestety tak jest, wiem to po sobie. A dlaczego tak jest? BO MOŻNA. Skoro można to czemu nie, i dopiero po jakimś czasie orientujemy się, że nagle się nam kończy pamięć, albo wszystko działa dużo wolniej niż powinno. A w nagrodę pozostaje nam piękny czytelny i nowoczesny kod, który i tak musimy zaorać :D

    Dzięki ilmenit jeszcze raz, za kawał dobrej roboty.
    • 17: CommentAuthorzbyti
    • CommentTime24 Apr 2020
     

    bocianu:

    w nagrodę pozostaje nam piękny czytelny i nowoczesny kod, który i tak musimy zaorać :D

    Jak ten Pan to pięknie ujął! :D
    • 18: CommentAuthorilmenit
    • CommentTime24 Apr 2020
     
    @antrykot - poprawione w artku, że jest to implementation specific, dzięki.
    • 19: CommentAuthorastrofor
    • CommentTime24 Apr 2020 zmieniony
     
    W dużych platformach raczej się nie alokuje pamięci, nawet na językach nieco niższego poziomu stara się od tego odchodzić. Nawet słynny macowy objective-c stara się to zautomatyzować. Przynajmniej mam wrażenie że jest taki trend.
    Edit: Dobra z tym objc to się rozpędziłęm, ale wiekszość języków jak może to używa garbage collectorów.
    • 20: CommentAuthorilmenit
    • CommentTime24 Apr 2020
     
    @astrofor - tu się nie zgodzę. Alokuje się dynamicznie na stercie. To, że są wrzucane na stos jakieś wrappery to później dealokujące lub lecą garbage collectory to inna sprawa :)
    • 21: CommentAuthorastrofor
    • CommentTime24 Apr 2020 zmieniony
     
    @ilmenit : miałem na myśli że użytkownika (programisty) to jest przezroczyste.
    Edit: zresztą nie będe się z Tobą kłócił, bo mam znikome pojęcie o kompilatorach.
    Tylko z doświadczenia zawodowego mówię, że alloca ostatnio używałem na studiach.
    • 22:
       
      CommentAuthorpirx
    • CommentTime25 Apr 2020
     
    explicite. pod spodem mallocuje aż miło.
    • 23: CommentAuthortr1x
    • CommentTime25 Apr 2020 zmieniony
     
    Czy aktualny jest punkt "11. Use initialized local variables
    Initialization of local variables when declaring them gives shorter and faster code.
    [...]
    If you mix uninitialized and initialized variables, you force the compiler to allocate space for the uninitialized variables each time, it parses an initialized one. So do this:
    int i, j;
    int a = 3;
    int b = 0;

    instead of
    int i;
    int a = 3;
    int j;
    int b = 0;

    The latter will work, but will create larger and slower code."?
    [ źródło: ->link<- ]

    Oto kod wygenerowany przez cc65 2.17 na stronie ->link<- z parametrami -Osir -Cl:
    void main(void)
    {
    int i, j;
    int a = 3;
    int b = 0;
    }

    .proc   _main: near

    L0004:
    L0006:

    ldx #$00
    lda #$03
    sta L0004
    stx L0004+1
    txa
    sta L0006
    sta L0006+1
    rts

    --------------------
    void main(void)
    {
    int i;
    int a = 3;
    int j;
    int b = 0;
    }


    .proc   _main: near

    L0003:
    L0006:

    ldx #$00
    lda #$03
    sta L0003
    stx L0003+1
    txa
    sta L0006
    sta L0006+1
    rts
    • 24: CommentAuthortebe
    • CommentTime25 Apr 2020
     
    2.17 ?

    Ilmenit używa 2.18
    • 25:
       
      CommentAuthorKaz
    • CommentTime25 Apr 2020
     
    Sebastian - używaj do kodu znaczników BBcode [ code ] i [ /code], będzie czytelniej.
    • 26: CommentAuthorilmenit
    • CommentTime25 Apr 2020 zmieniony
     
    Punkt 11 głównie może zwiększać wydajność, gdy nie używasz przełącznika -Cl. Gdy używasz, zmienne te nie są deklarowane na stosie ale jako "zmienne statyczne". Te zmienne jednak wciąż przy wejściu do funkcji są inicjalizowane wartością (w przeciwieństwie do użycia keyworda 'static') i to się dzieje w kodzie:
    #include <stdio.h>

    void f()
    {
    static unsigned char s = 0;
    unsigned char n = 0;
    printf("\nStatic one:%d ",s++);
    printf("Non-static:%d",n++);
    }

    int main()
    {
    unsigned char i;
    for (i=0;i<5;++i)
    f();
    for(;;);
    }


    wypisze:
    Static one:0  Non-static:0
    Static one:1 Non-static:0
    Static one:2 Non-static:0
    Static one:3 Non-static:0
    Static one:4 Non-static:0

    jest tak, aby zmienne zachowywały się "standardowo" mimo przeniesienia zmiennych ze stosu.
    Przy braku -Cl jest jak w komentarzu powyżej, czyli kompilator miesza odkładanie na stos z inicjalizacją zmiennych co generuje wolniejszy kod.
    • 27: CommentAuthortr1x
    • CommentTime25 Apr 2020
     
    @ilmenit dziękuję za wyjaśnienie.