atarionline.pl ACTION!, CC65, a może ATALAN ? - 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: CommentAuthoremka
      • CommentTime5 Sep 2010 21:09 zmieniony
       
      Powstałe zamieszanie wokół nowych języków programowania skłoniło mnie do skromnej próby przetestowania ich możliwości. Ze względu moją dawną fascynację liczbami pierwszymi jako pierwszy test użyłem sita Eratostenesa, programu napisanego dawno temu w ACTION!'ie a obecnie przepisanego na CC65. W stosunku do oryginalnej wersji użyłem tablicy bitowej i pominąłem sprawdzanie liczb parzystych, dzięki temu tablica mieszcząca 65535 liczb zajmuje 4096 bajtów. Całkowity czas wykonania programu wyniósł dla CC65 10.6s a dla ACTION'a 11,6s. Mimo szczerych chęci nie udało mi się przepisać tego programu na ATALAN'a, ale kilka prostszych testów daje nadzieję na nowego króla wydajności.
      Co do samego algorytmu to ograniczeniem ACTION'a jest maksymalna wielkość zmiennych WORD, a ograniczeniem CC65 jest pamięć komputera, maksymalne wykorzystanie pamięci ATARI 65XE pozwala zbudować tablicę mieszczącą 500k słów. Szukając innego algorytmu znalazłem to ->link<- jest to też sita Eratostenesa, ale w jakiś sposób udało się ominąć ograniczenie pamięci małego atari, znalezienie liczby 65537, (pierwszej poza granicą 16b) zajmuje mu 2sekundy (pięć razy szybciej niż moje sito), a liczby 16777213 ok. 10 min. Na razie nie potrafię zrozumieć algorytmu zwłaszcza że jest on wstawką maszynową do BASIC'a. Czy ktoś zna może ten algorytm albo potrafi go zrozumieć na podstawie kodu źródłowego ->link<- ? Dobrze by było przetestować te trzy języki trudniejszym algorytmem.
      • 2:
         
        CommentAuthorKaz
      • CommentTime5 Sep 2010 22:09
       
      Z ta liczba nowych jezykow to nie przesadzajmy, zaledwie Atalan i LogForth sa nowe :)

      LogForth:
      ->link<-

      TDC swego czasu mamil nas wydajnoscia Action! ;)
      ->link<-

      No i oprocz liczenia warto by chyba pokazac predkosc generowania grafiki, to wazniejsze w praktyce:
      ->link<-

      A jeszcze warto, przy przekonywaniu ludzi do Atalana czy CC65, porownac z Basiciem, szczegolnie popularnym TBXL:
      (link wcielo)

      Acha, no i kiedys Cosi mowil, ze bedzie przekonywal do volksFortha :)
      ->link<-
      • 3: CommentAuthoremka
      • CommentTime5 Sep 2010 22:09
       
      Faktycznie języki nie są nowe ale implementacja na ATARI jest brakującym ogniwem którego brakowało w latach 80'tych. FORTH może być alternatywą dla BASICA nawet turbo, a do napisania gry potrzebny jest pomysł (50% powodzenia) i wizja (dodatkowe 30%). Cała reszta przy pomocy takich narzędzi jak CC65 to formalność (język może być inny).
      • 4:
         
        CommentAuthorCosi
      • CommentTime6 Sep 2010 14:09
       
      Łatwiej się przekonuje ludzi do Atalana ;-) W zasadzie nawet nie muszę przekonywać, inni to robią lepiej.
      Żeby nie było - sam z Fortha korzystam, mam nawet kilka różnych implementacji na wszystkich moich komputerach. Jest na przykład świetny jako kalkulator :D
      • 5: CommentAuthorszary
      • CommentTime6 Sep 2010 22:09
       
      Hmmm...coś kiedyś było na temat możliwości wykonania carta z action. Czy wiadomo cokolwiek na ten temat?
      • 6:
         
        CommentAuthortdc
      • CommentTime11 Sep 2010 21:09
       
      Tdc mamił kiedyś i jeszcze będzie mamił ;)


      Odnośnie tych językowych dysput to Action! jest bezkonkurencyjny bo jest programem, z którego można było korzystać w tamtych czasach i jest programem pure Atari. Nowe języki pisane na inne platformy nie nadają się tu do porównywania. Optymalizacja kodu również była wtedy bardzo ograniczona bo miała się zmieścić tak w RAM aby jeszcze zostało trochę miejsca na kod w nim powstający (na innych platformach ograniczenia pamięci nie ma więc nie ma i problemów z świetną optymalizacją kodu wyn.). Poza tym jak porównywać programy których powstanie różni 20 lat ?

      Do tego warto dodać, że Action! powstał nie do testów np. liczb pierwszych tylko do gier i ew. dem. Dlatego testy tego języka raczej należy pod tym kątem konstruować (to nie jest uwaga do emka tylko ogólna do dyskusji). I tu na pewno wyjdzie wiele jego wad, jednak mimo to nie było drugiego tak wydajnego (i wygodnego) języka nawet na innych platformach.

      emka:

      Całkowity czas wykonania programu wyniósł dla CC65 10.6s a dla ACTION'a 11,6s.

      A możemy prosić o źródła ?

      emka:

      Co do samego algorytmu to ograniczeniem ACTION'a jest maksymalna wielkość zmiennych WORD, a ograniczeniem CC65 jest pamięć komputera,

      Zapewniam Ciebie że Action! ma znacznie większe problemy z pamięcią - z czym niestety ciągle walczę w swoich grach. Z jednej strony chcę aby gry chodziły na 64 kb z drugiej jest to bardzo trudne i pracochłonne.
      • 7: CommentAuthoremka
      • CommentTime11 Sep 2010 23:09 zmieniony
       
      Nie wiem czy komuś może to się przydać, tablica licz pierwszych w zakresie 65536 może służyć do szybkiego odnajdywania liczb w zakresie 4gigabajty. Obecnie próbuję zrozumieć pewien algorytm powstały na małym atari, nie potrzebuje on tak dużej tablicy prawdopodobnie raz znalezione liczby pierwsze nie muszą być dalej przechowywane, wystarczy że są ich wielokrotności.
      Dla ciekawych dołączam źródła tych testów. Algorytm jest zwykłym sitem dobrze wszystkim znany i w takiej formie może służyć tylko do testów.

      ACTION!

      BYTE RTCLOK2=19,RTCLOK3=20
      BYTE ARRAY FLAGS(8192)
      CARD COUNT,I,K,KROK
      BYTE ARRAY MASK(8)=[1 2 4 8 16 32 64 128]
      BYTE ARRAY MASKX(8)=[$FE $FD $FB $F7 $EF $DF $BF $7F]

      PROC CLRX(CARD X)
      BYTE Y
      Y=X&7
      X=X RSH 3
      FLAGS(X)==& MASKX(Y)
      RETURN

      BYTE FUNC TESX(CARD X)
      BYTE Y
      Y=X & 7
      X=X RSH 3
      RETURN(FLAGS(X) & MASK(Y))

      PROC SIEVE()
      RTCLOK2=0
      RTCLOK3=0
      COUNT=1

      FOR I=0 TO $1FFF
      DO
      FLAGS(I)=$AA
      OD
      FOR I=3 TO 255 STEP 2
      DO
      IF TESX(I) THEN
      ; PRINTF("%U ",I)
      K=I*I
      KROK=I+I
      WHILE (K > KROK)
      DO
      CLRX(K)
      K==+KROK
      OD
      FI
      OD

      FOR I=3 TO $FFFD STEP 2
      DO
      IF TESX(I) THEN
      COUNT==+1
      ; PRINTF("%U ",I)
      FI
      OD

      I=RTCLOK2*256+RTCLOK3
      PRINTF("%E %U LICZB PIERWSZYCH W CZASIE",COUNT)
      PRINTF("%E %U RAMEK",I)
      RETURN



      CC65

      // cl65 -t atari -O sito.c -o sito.xex

      #include <stdio.h>
      #define RTCLOK2 *(unsigned char*)19
      #define RTCLOK3 *(unsigned char*)20

      unsigned int i,k,count,krok;
      char mask[8]={1,2,4,8,16,32,64,128};
      char maskx[8]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
      char flags[8192];

      void main(void)
      {
      RTCLOK2=0;
      RTCLOK3=0;
      count=1;

      for(i=0;i<8192;++i)
      flags[i]=0xaa;

      for(i=3;i<256;i+=2)
      {
      if (flags[i>>3] & mask[i&7])
      {
      // printf("%u ",i);
      k=i*i;
      krok=i+i;
      while(k>=krok)
      {
      flags[k>>3] &= maskx[k&7];
      k+=krok;
      }
      }
      }

      for(i=3;i>2;i+=2)
      {
      if (flags[i>>3] & mask[i&7])
      {
      ++count;
      // printf("%u ",i);
      }
      }

      i=(RTCLOK2<<8)+RTCLOK3;
      printf("%e %u liczb pierwszych w czasie\n ",count);
      printf("%e %u ramek\n",i);
      for (;;);
      }


      Wydaje mi się że oba testy napisane zostały w sposób maksymalnie wykorzystujący możliwości obu języków, ale pewnie tylko mi się wydaje. Bardzo chciałem napisać taki test w ATALANIE ale na razie nie dałem rady.

      PS.
      Częstotliwość występowania liczb pierwszych maleje w miarę zwiększania się ich wartości, Największe liczby mają nazwy pochodzące od ośrodków które je odkryły. Czy nikt nigdy nie chciał mieć gwiazdy nazwanej swoim imieniem lub chociaż liczby pierwszej.
      • 8:
         
        CommentAuthortdc
      • CommentTime12 Sep 2010 05:09 zmieniony
       
      Dzięki za źródło, chciałem je aby ktoś mógł zoptymalizować ten kod.

      emka:

      Wydaje mi się że oba testy napisane zostały w sposób maksymalnie wykorzystujący możliwości obu języków, ale pewnie tylko mi się wydaje.


      Zapewniam Ciebie, że kod w Action! kompletnie nie jest zoptymalizowany. Wywoływanie procedur świadczy bardziej o pseudokodzie dla każdego komputera - warto by było to zoptymalizować pod względem architektury Atari oraz Action!
      Przykładowo żadnych procedur, ich parametrów oraz zwracania danych (to również na np. pececie jest niezwykle powolne a nawet bardziej niż na Atari).
      Do tego skacząc w pętlach do procedur dajesz przewagę dla kodu w C, gdzie takich skoków itp. nie ma.

      W Action! są bardzo powolne mnożenia. Widzę że wykorzystujesz RSH i "&", ten pierwszy jest nieprawdopodobnie powolny, & jest wystarczająco szybki do typowych zastosowań. Natomiast RSH to warto sobie przepisać na asm bo nawet jeśli nie masz zielonego pojęcia o asm to i tak to będzie dziesiątki razy szybsze ;)

      Ok, nie mam czasu aby dalej omawiać zagadnienie, życzę powodzenia w dalszym optymalizowaniu oraz znalezieniu następnej liczby pierwszej ;)
      • 9: CommentAuthoremka
      • CommentTime12 Sep 2010 11:09 zmieniony
       
      No i zaczęło się !
      To są tylko liczby i nie ma sensu optymalizowanie kodu pod architekturę ATARI , co do procedur i funkcji to można je włączyć w główną funkcje tak jak w CC65, wystarczy zmienić linie
      31  IF TESX(I) THEN
      na IF (FLAGS(I RSH 3) & MASK(I & 7))THEN

      37 CLRX(K)
      na FLAGS(K RSH 3)==& MASKX(K&7)

      47 IF TESX(I) THEN
      na IF (FLAGS(I RSH 3) & MASK(I & 7))THEN

      i procedurę clrx() oraz funkcję tesx() można usunąć,
      ale ACTION! jest kompilatorem jednoprzebiegowym(chyba) i radzi sobie z tym gorzej niż z funkcjami.
      RSH jest wolne ale i tak jest to najszybsze dzielenie ( w tym przypadku przez 8 i wykonuje się bez schodzenia do assemblera). Należy również rozważyć to że zagnieżdżenie RSH wywoła procedurę biblioteczną która jest dopiero wolna.(straty przewyższają zyski)

      PS.
      Temat traktuję edukacyjnie jak na razie test wykonuje się szybciej na CC65 chyba że ktoś potrafi zoptymalizować ACTIONA! bez schodzenia do assemblera. Przyrost prędkości większy niż 20% będzie dla mnie wynikiem wartym zachodu.
      • 10:
         
        CommentAuthortdc
      • CommentTime12 Sep 2010 15:09
       

      emka:

      No i zaczęło się !

      Spokojnie ;)
      Tutaj Ciebie nikt nie będzie wyzywał od lamerów, "kłamczuszków" i trolli oraz nie dostaniesz bana tylko dlatego, że chcesz coś pokodować ;)

      emka:

      nie ma sensu optymalizowanie kodu pod architekturę ATARI

      Ma, skoro np. CPU nie ma mnożenia to nie należy się spodziewać zadowalających rezultatów po takim kodzie.

      Do tego w tym kodzie w C stosujesz kilka ciekawych tricków, w momencie gdy w kodzie Action! nie ma żadnej optymalizacji. Więc jak to porównywać ?
      Action! i C to są dwa różne języki i to co tu zaproponowałeś w C już jest mocno zoptymalizowane, natomiast w Action! robi się to zupełnie inaczej. Więc warto choć trochę to zoptymalizować.

      Dodatkowo w innej podobnej dyskusji okazało się, że zoptymalizowany kod w C pod CC65 jest szybszy od kodu w Action! jednak jeśli nie jest zoptymalizowany to jest od Action! wolniejszy. Wyniki te zostały potwierdzone Twoim przykładem.

      emka:

      co do procedur i funkcji to można je włączyć w główną funkcje tak jak w CC65 (...)
      jak na razie test wykonuje się szybciej na CC65

      No i jakie są teraz wyniki szybkości ?

      emka:

      RSH jest wolne ale i tak jest to najszybsze dzielenie

      W tym języku tak, ale skoro optymalizujesz to w C to w Action! musisz tym bardziej bo przecież jak wspominałem 6502 nie ma mnożenia i dzielenia.

      emka:

      dzielenie ( w tym przypadku przez 8

      To koniecznie trzeba poprawić bo jest jedną z najwolniejszych operacji w tym kodzie.

      emka:

      ależy również rozważyć to że zagnieżdżenie RSH wywoła procedurę biblioteczną która jest dopiero wolna

      RSH zawsze wywołuje procedurę biblioteczną, ale nie w tym jest problem. Problem jest w tym, że w Action! procedura ta jest kompletnie skopana, strata wydajności jest taka jakbyśmy się przesiedli do Bascia XL, dlatego należy unikać tych dwóch operacji bitowych w głównych pętlach.

      emka:

      Temat traktuję edukacyjnie

      I bardzo dobrze;)
      Skoro uczę programowania od lat 90. to w całej rozciągłości popieram edukowanie poprzez przykłady;)

      emka:

      chyba że ktoś potrafi zoptymalizować ACTIONA! bez schodzenia do assemblera

      Myślę że parę osób tu potrafi.
      Ja obecnie zoptymalizowałem główne procedury rysujące w TOMCAT około 3 krotnie również bez schodzenia do asm. Było to potrzebne po wprowadzeniu planów obiektów bo przy tak dużej ilości przeciwników, pocisków itp. Atari + Action! już się nie wyrabiały.
      • 11: CommentAuthoremka
      • CommentTime12 Sep 2010 18:09 zmieniony
       

      tdc:

      No i jakie są teraz wyniki szybkości ?

      wykonanie programu w ACTION! wydłużyło się do 13s

      tdc:

      RSH zawsze wywołuje procedurę biblioteczną

      Nie zawsze, akurat w procedurze clrx() i funkcji tesx()
      x = x rsh 3	 zostało zamienione na 

      lsr x+1
      ror x
      lsr x+1
      ror x
      lsr x+1
      ror x

      wystarczy w monitorze ACTION!'a zapytać o adres clrx (? clrx) i podejrzeć ją z monitora w emulatorze.
      nie bardzo wiem co tu optymalizować nawet w maszynie, jeżeli nawet załadujemy połowę zmiennej x do akumulatora to i tak potem trzeba ją gdzieś zapisać.

      tdc:

      skoro optymalizujesz to w C to w Action! musisz tym bardziej


      oba programy są identyczne w 99% i nieoptymalizowane
      jedyna optymalizacja w CC65 to opcja -O w kompilatorze,bez tej opcji CC65 jest faktycznie 2 razy wolniejszy.
      Bardzo chciałbym wiedzieć jak zoptymalizować kod w ACTION!ie, np nie wiem dlaczego każda funkcja zaczyna się od jmp na następną pozycję. Czy jest jakiś podręcznik optymalizacji do ACTION!a?
      • 12: CommentAuthorilmenit
      • CommentTime13 Sep 2010 12:09
       
      By jeszcze przyspieszyć można przenieść zmienne do main() i dać je jako "register".

      void main(void)
      {
      register unsigned int i,k,count,krok;

      A następnie kompilować:
      cl65 -Cl -t atari -Osir sito.c -o sito.xex
      • 13: CommentAuthorBartoszP
      • CommentTime13 Sep 2010 13:09
       
      Słowo kluczowe register jest ogólnie nieprzydatne w architekturach z małą ilością rejestrów. Skoro zadeklarujemy 4 zmienne jako register to i tak kompilator sobie z tym nie poradzi inaczej jak robiąc zwykłe zmienne. Sens ma jedynie pokazywanie zmiennej sterującej pętlą.
      Tak ogólnie to lepiej zmienne lokalne, w naszym przypadku k i krok, deklarować lokalnie w bloku gdzie są używane i wtedy podpowiedzieć register.
      Zmienne globalne i tak są zawsze rzeczywistymi komórkami pamięci.
      • 14: CommentAuthorilmenit
      • CommentTime13 Sep 2010 16:09
       
      W CC65 słowo kluczowe register umieszcza zmienną na stronie zerowej, więc jest bardzo przydatne.
      • 15: CommentAuthoremka
      • CommentTime13 Sep 2010 16:09 zmieniony
       
      Dotychczas zmienne na stronie zerowej umieszczałem przy pomocy definicji

      #define i *(unsigned int*)0xf0
      #define k *(unsigned int*)0xf2
      #define krok *(unsigned int*)0xf4
      #define count *(unsigned int*)0xf6

      Nareszcie zrozumiałem jak działa register w CC65. Do tej pory myślałem że kompilator próbuje użyć rejestrów sprzętowych. Używania zmiennych lokalnych unikałem jak ognia, a samo dopisanie register nic nie zmienia.

      Dzięki

      Ile zmiennych CC65 pozwala umieścić na stronie zerowej?
      • 16: CommentAuthorilmenit
      • CommentTime13 Sep 2010 16:09 zmieniony
       
      Domyślnie 6 bajtów. Nie wiem, czy da się tę liczbę prosto zmienić, nie patrzyłem w źródła odnośnie tego:
      ->link<-

      Polecam też przeczytać odpowiedź Ullricha von Bassewitz na moje pytanie o struktury:
      ->link<-

      Jeszcze kilka informacji ze starego wątku na AtariOnline.

      Zmienne zeropage możemy też deklarować samemu. Jeden z dawnych branchy CC65 miał słowo zstatic do definiowania zmiennych zero-page. Niestety to nie zostało użyte i w CC65 trzeba takie zmienne definiować w ASMie i użyć #pragma zpsym(), jak poniżej:

      //// test.c
      #include
      #define a 0x9C68
      extern unsigned char i;
      #pragma zpsym ("i");
      void main(void)
      {
      for (i=1;i;++i)
      ((unsigned char*)a)[i] = 0x01;
      }

      //// zero.asm
      .export _i
      .segment "ZEROPAGE"
      _i: .byte 0

      Daje to kod wynikowy (_i jest na zero-page, więc zaoszczędzamy cykl):

      lda #$01
      85 sta _i
      A5 L0003: lda _i
      F0 beq L0004
      A2 ldx #$9C
      A9 lda #$68
      18 clc
      65 adc _i
      90 bcc L000E
      E8 inx
      85 L000E: sta sreg
      86 stx sreg+1
      A9 lda #$01
      A0 ldy #$00
      91 sta (sreg),y
      E6 inc _i
      4C jmp L0003
      60 L0004: rts


      CC65 nie potrafi optymalizować kodu, gdy wskaźnik zdefiniujesz na stałe miejsce w pamięci. Spójrz na kod wygenerowany w poniższym przykładzie (cl65 -Osi -Cl -l test.c).

      unsigned char a[255];

      void main(void)
      {
      unsigned char i;
      for (i=1;i;++i)
      a[i] = 0x02;
      }

      lda #$01
      sta L0004
      L0005: lda L0004
      beq L0006
      ldy L0004
      lda #$02
      sta _a,y
      inc L0004
      jmp L0005
      L0006: rts
      • 17: CommentAuthorilmenit
      • CommentTime13 Sep 2010 17:09 zmieniony
       
      [Post był nie do tego wątku]
      • 18: CommentAuthorBartoszP
      • CommentTime13 Sep 2010 21:09
       
      @ilmenit
      Wiedziałem, że gdzieś jest haczyk, pisałem tak "w ciemno".
      Tak czy inaczej "zero page" jest gorsze od rejestru ale lepsze niż komórka pamięci poza "zero page"
      • 19: CommentAuthoremka
      • CommentTime14 Sep 2010 07:09
       

      ilmenit:

      CC65 nie potrafi optymalizować kodu, gdy wskaźnik zdefiniujesz na stałe miejsce w pamięci

      To jest jeszcze do przeżycia, gorzej jest jeżeli adresujemy tablicę, zmienną która kiedyś była int a potem została maskowana do char. Kompilator traktuje wtedy tablicę mniejszą niż 256 jako długą 65536 i odwołuje się do niej przez wektor na stronie zerowej.

      unsigned char a[255];
      unsigned int j;
      unsigned char i;

      void main(void)
      {
      for (i=1;i;++i)
      a[j & 0xff] = 0x02;
      }

      otrzymamy taki kod

      lda #$01
      sta _i
      L0003: lda _i
      beq L0004
      lda _j
      clc
      adc #<(_a)
      sta ptr1
      lda #$00
      adc #>(_a)
      sta ptr1+1
      lda #$02
      ldy #$00
      sta (ptr1),y
      inc _i
      jmp L0003
      L0004: rts


      Kompilator do adresowania tablicy użył tylko młodszego bajtu zmiennej j, ale tablicę potraktował jako długą (użyłem zmiennych globalnych, kompilator nadaje im wtedy nazwy użyte w programie co ułatwia analizę).

      Pewnie pytam o rzeczy oczywiste ale, czy można wymusić traktowanie tablicy mniejszej niż 256 bajtów jako krótkiej?
      • 20:
         
        CommentAuthortdc
      • CommentTime14 Sep 2010 08:09 zmieniony
       

      emka:

      wykonanie programu w ACTION! wydłużyło się do 13s


      Jest to jasny dowód na to że Action! jest językiem do gier i dem a nie do obliczeń (co z resztą jest oczywiste skoro to język bez operacji zmiennoprzecinkowych).

      Jednak wydłużenie kodu nie wynika z tego, że zrezygnowaliśmy ze skoków do procedur - to dalej trzeba optymalizować.

      Co do czasu trwania skoków do procedur to starałem się to ukazać tutaj:
      ->link<-

      emka:

      Nie zawsze, akurat w procedurze clrx() i funkcji tesx()


      I tu mnie zaskoczyłeś, nie spodziewałem się, że tak mały kompilator uwzględnia takie rzeczy.
      Ze 2 lata temu chciałem sprawdzić ile dotów 3D potrafi Action! animować na ramkę, wtedy wywalenie takich obliczeń przyspieszyło to naprawdę kilkukrotnie - uznałem to za ustalone.
      Jak widać teraz trzeba to sprawdzić, czyli w których sytuacjach jak jest.

      emka:

      oba programy są identyczne w 99% i nieoptymalizowane


      Nie. Np. w C zastosowałeś niewielki trick "for(i=0;i<8192;++i)"

      Z moich wczesnych testów, wynika (na MS Visual C++ i Borland na blaszaku), że taka pętla wykonuje się nieco szybciej od wersji podręcznikowej.
      Natomiast w Action! nie ma żadnej tego typu optymalizacji pętli.

      Pętla w Action! działa dużo wolniej od asm i w tej materii manewr jest naprawdę znikomy, a mógłby być lepszy, podejrzewam że CC65 ma szybsze pętle o ile stosuje się takie tricki (a była już kiedyś o tym mowa).

      emka:

      nie bardzo wiem co tu optymalizować nawet w maszynie


      Jest wiele rzeczy, ale większość będzie się odwoływać do asm lub do znacznych zmian kodu i algorytmu.

      W dodatku popracuj nad zlikwidowaniem tych procedur, szybko powinieneś dojść do sytuacji tej którą najbardziej się spodziewamy, czyli takiej w której to jednak działa szybciej ;)

      emka:

      Czy jest jakiś podręcznik optymalizacji do ACTION!a?


      Poza tym niepełnym w mojej głowie to chyba nie ;)

      Swego czasu wraz z Konopem szukaliśmy jakichkolwiek informacji i wszędzie można znaleźć jedynie różne wersje firmowego manuala, który dobrze opisuje język, ale nic nie ujawnia jeśli chodzi o szczegóły funkcjonowania kompilatora, a o optymalizacji to można zapomnieć.

      Z tego co widzę, to na całym świecie jest mało osób, które choćby w małym stopniu znają Action! lub cokolwiek w nim robiły. Dlatego szanse, że istnieje jakiś fajny podręcznik oceniam na niemal zerowe.


      Moją radą w optymalizowaniu kodu w Action! jest to aby traktować go jako asembler, a nie myśleć w takich kategoriach jak w C/C++. Wtedy da się osiągnąć różne cuda, które mieliśmy okazję widzieć tu i tam w historii Atari.


      ilmenit:

      By jeszcze przyspieszyć można przenieść zmienne do main() i dać je jako "register".

      W Action! można zmienne umieścić na stronie 0, ale być może kompilator wcale nie uwzględnia takiej ewentualności i nic to nie da.
      • 21: CommentAuthorilmenit
      • CommentTime14 Sep 2010 10:09
       
      @emka
      W C typy rzutuje się inaczej :)

      a[ (unsigned char) j ] = 0x02;


      Generuje:

      .proc	_main: near

      .segment "CODE"

      ;
      ; for (i=1;i;++i)
      ;
      lda #$01
      sta _i
      L0004: lda _i
      beq L0005
      ;
      ; a[ (unsigned char) j ] = 0x02;
      ;
      ldy _j
      lda #$02
      sta _a,y
      ;
      ; for (i=1;i;++i)
      ;
      inc _i
      jmp L0004
      ;
      ; }
      ;
      L0005: rts
      • 22: CommentAuthorxxl
      • CommentTime14 Sep 2010 11:09
       
      nie chce zeby ten post byl odebrany jako atak, zainteresowalem sie tym programikiem, napisanie go w action! w taki sposob zeby byl szybszy od c lub atalana jest niemozliwe.
      pytanie czy c kontroluje rejestry i znaczniki procesora? wiem ze atalan 'zaczyna' ale jak jest w c?
      w powyzszym przykladzie gdyby kontrola znacznikow byla przeprowadzona to linia lda _i bylaby niepotrzebna, przy kontroli rejestrow ta petla programu bylaby ponad 2x szybsza, moze sa jakies sztuczki ktore pozwalaja w c wyciagnac np. rej.y na zewnatrz petli. pytam bez zlosliwosci.
      • 23: CommentAuthoremka
      • CommentTime14 Sep 2010 13:09 zmieniony
       

      tdc:

      Jest to jasny dowód na to że Action! jest językiem do gier i dem a nie do obliczeń

      Każdy patrzy na komputer inaczej, dla mnie pamięć ekranu i PMG to tablice a rejestry sprzętowe to struktury, narysowanie punktu na ekranie to ustawienie bitu w tablicy. Język im lepiej zarządza tablicami tym szybciej rysuje. (Takie myślenie prawdopodobnie zbliża mnie do asemblera i oddala od współczesnych komputerów).
      Oczywiście że ACTION! pozwala na deklarowanie zmiennych na stronie zerowej(program dostaje wtedy lekkiego kopa), należy tylko znaleźć komórki nieużywane przez system. Jeżeli tego nie użyłem to dlatego że nie wiedziałem jak to zrobić w CC65, a programy miały być maksymalnie podobne

      illmenit:

      W C typy rzutuje się inaczej
      a[ (unsigned char) j ] = 0x02;

      Ale to proste (jak się już to wie)

      Za wcześnie się ucieszyłem, niestety nie chce działać dla mniejszych tablic np. 128 bajtowych
      a[ (unsigned char) j & 0x7f] = 0x02;

      xxl:

      gdyby kontrola znacznikow byla przeprowadzona to linia lda _i bylaby niepotrzebna

      Nikt mądrzejszy nie chciał się wypowiedzieć więc wypadło na mnie, myślę że sam już odpowiedziałeś sobie na to pytanie. Masz rację, ale jak zaczniemy dalej optymalizować to ostatni jmp też jest niepotrzebny, wystarczy bne na instrukcję ldy _j można optymalizować dalej, z punktu widzenia działania programu to w ostatnim przykładzie wskaźnik tablicy wewnątrz pętli nie zmienił się ani razu i inteligentny (bardzo) kompilator powinien zoptymalizować pętlę do zera i wykonać ją tylko jeden raz (to by było przyspieszenie). Cały przykład miał na celu pokazanie jak CC65 zarządza tablicami i tyle.CC65 rozwija się, może kiedyś wykryje nic nierobiące pętle (ale się wymądrzam wybacz).
      • 24: CommentAuthorrudla
      • CommentTime15 Sep 2010 01:09 zmieniony
       
      Here is Atalan (this version is in code repository, but not on atalan.kutululu.org, yet) implementation of bit sieve:

      out rtclock1@20:byte
      out rtclock2@19:byte

      count:0..8191

      const
      mask:array(0..7) = 1,2,4,8,16,32,64,128
      maskx:array(0..7) = %1111'1110,%1111'1101,%1111'1011,%1111'0111,%1110'1111,%1101'1111,%1011'1111,%0111'1111

      flags:array(count)

      rtclock1 = 0
      rtclock2 = 0

      for i:0..$1fff flags(i)=$aa

      for i:3..255 step 2 where (flags(i/8) and mask(i mod 8) <> 0)
      for k:i*i..8192 step 2*i
      flags(k/8) = flags(k/8) and maskx(k mod 8)

      count = 1
      for k:3..8192 step 2 where (flags(k/8) and mask(k mod 8) <> 0)
      inc count

      t = rtclock2 * 256 + rtclock1

      "[count] prime numbers in [t] ticks"


      It finds 1028 primes in 64 ticks (1.28 seconds).

      Generated assembler code is attached.
      • 25: CommentAuthoremka
      • CommentTime15 Sep 2010 22:09 zmieniony
       
      Bardzo mi się podoba przykład rudla. Głównym powodem użycia tablicy bitowej było to, że w tablicy o rozmiarze 8192 można przechować 65536 liczb, ten program wypełnia ją tylko do 8192, będzie co prawda ok. 5-6 razy wolniejszy ale i tak wygra test, żeby to zmienić wystarczy tylko bardzo niewielka przeróbka. Na moim dość leciwym 32b sprzęcie kompilator zachowuje się bardzo kapryśnie. Instalowałem go na dwóch różnych komputerach i na każdym zachowywał się inaczej. Na żadnym nie udało się go skompilować.
      Potrzebna obszerniejsza dokumentacja.
      rudla tak trzymać
      • 26: CommentAuthorrudla
      • CommentTime15 Sep 2010 23:09
       
      I was trying to write it according to the examples given at the beginning of this topic.

      I didn't get the end condition of the loop:

      k=i*i;
      krok=i+i;
      while(k>=krok)
      {
      flags[k>>3] &= maskx[k&7];
      k+=krok;
      }

      Is that some trick with overflow?
      • 27: CommentAuthorrudla
      • CommentTime15 Sep 2010 23:09
       
      Aaah. I get it. Of course, loops to $ffff get often stuck :-) so you are using the overflow trick.
      • 28: CommentAuthoremka
      • CommentTime15 Sep 2010 23:09 zmieniony
       

      rudla:

      Is that some trick with overflow?


      yes it is.

      Mój angielski jest bardzo szkolny ale wiem że czytasz po polsku.

      W powyższym przykładzie koniec pętli następuje po przekroczeniu zakresu tablicy
      jeżeli zaczynamy liczyć od 3 a krok jest 2 to warunek k < 3 wystąpi dopiero po przekroczeniu zakresu zmiennej k

      jeżeli zaczynamy liczyć od i*i a krok jest i+i to warunek k < i*i wystąpi dopiero po przekroczeniu zakresu zmiennej k

      i*i = i+i tylko dla 2

      poprawienie ortografii przesunęło post o jeden do dołu
      • 29: CommentAuthoremka
      • CommentTime18 Sep 2010 00:09 zmieniony
       
      Obecny stan kompilatora (15.09.2010) i mojej wiedzy o ATALAN'ie prawie pozwala na napisanie pełnego testu sita. Moja wersja wymagała tylko jednej poprawki w kodzie wynikowym instrukcji porównania, dla zmiennych typu word. np. (k > krok) (oficjalnie jeszcze nieobsługiwanej).Czas wykonania testu spadł poniżej 10s i jest na razie najszybszy.
      • 30: CommentAuthorrudla
      • CommentTime28 Sep 2010 18:09 zmieniony
       
      I finished improved version of common subexpression elimination.

      New version of Atalan compiles the sieve (added as another example to web site at ->link<- ) without errors and computation time is 7,4s (370 ticks). I also added support for square root, to make the logic of the program more obvious :-)

      Also no tricks with overflow, compiler takes care about all the tricks necessary.
      • 31: CommentAuthoremka
      • CommentTime29 Sep 2010 01:09
       
      Atalan jest coraz lepszy, (w teście sita jest najszybszy!), ale do zrobienia pozostało jeszcze wiele,
      jak dla mnie jedną z ważniejszych rzeczy do zrobienia jest 16 bitowe porównanie np.

      if (a > b)

      generuje kod

      lda b
      cmp a
      lda b+1
      cmp a+1
      jcs _lbl2

      według mnie przedostatnia instrukcja powinna być

      sbc a+1 aby doliczyć C z porównania młodszych bajtów.

      cmp zeruje C przed porównaniem,
      sbc dolicza C do wyniku.

      Wszystkie porównania są zapisane w pliku m6502.atl, za powyższy przykład odpowiada procedura

      rule ifle %A,%B:card, %C:card   = instr
      let _a, %C(0) ; lda %B
      sub _void, _a, %B(0) ; cmp %C
      let _a, %C(1) ; lda %B+1
      sub _void, _a, %B(1) ; cmp %C+1
      ifeq %A, _C, 1 ; jcs %A


      nie udało mi się podmienić cmp na sbc ale po zamianie wersji krótkiej na szybką, porównanie zadziałało

      rule ifle %A,%B:card, %C:card   = instr
      let _a, %B(1) ; lda %B+1
      sub _void, _a, %C(1) ; cmp #>%C
      ifeq %A, _C, 0 ; bcc %1
      ifeq _lab1, _Z, 0 ; beq _lab1
      let _a, %B(0) ; lda %B
      sub _void, _a, %C(0) ; cmp #<%C
      ifeq %A, _C, 0 ; bcc %A
      label _lab1


      Podobnie jest z pozostałymi porównaniami.
      Myślę że nie jest to trudny do usunięcia błąd, a Atalan to naprawdę wielki projekt. Bałem się że nie zostanie ukończony, powstanie nowej wersji świadczy że Rudla nie poddaje się tak łatwo.
      Trzymam kciuki za Niego.
      • 32: CommentAuthorrudla
      • CommentTime29 Sep 2010 09:09 zmieniony
       
      Yes, I found this problem too. It's almost always the optimizations, that cause the trouble. In this case, I added some rudimentary data flow analysis support and optimizer suddenly started removing sub instructions, as their result was not used.

      Oh, and there was also my error, when I incorrectly 'solved' the issue by replacing _a for _void :-)

      This is caused by the fact, that compiler does not properly handle flags yet. This is one of the next tasks in development, as it causes also some other problems and would allow next optimizations.

      Yes, Atlan is big project and the TODO list does not seem to get shorter :-)
      It is the interest of community in Atalan, it is what keeps me motivated to continue the development.
      Thank you for that.
      • 33: CommentAuthorrudla
      • CommentTime3 Oct 2010 16:10
       
      Improving optimization some more, Atalan now computes bit_sieve in 6.7 seconds (336 ticks).
      • 34: CommentAuthoremka
      • CommentTime3 Oct 2010 22:10
       
      Nie podejrzewałem że uda się zejść poniżej 7s, Atalan to potrafi. Otrzymany kod to prawie czysty język maszynowy którego napisanie wymagałoby od programisty wieloletniego doświadczenia.
      To fantastyczne! Atalan jest najszybszym językiem na 6502, wygrywa test sita, teraz nadchodzi czas aby skracać listę TODO.

      Rudla jesteś wielki