26

Odp: float w CC65

tebe - nie mówiłbyś tak, gdybyś widział ten mój algorytm na FP->ASCII...
za małą głowę mam, żeby to od razu robić w ASM.
natomiast nie wykluczam, że później znajdzie się ktoś kto będzie miał na tyle dużo samozaparcia, aby to przerobić. Operacje są dość proste - ale jest ich cholernie dużo (jak na ten temat), poza tym pewnie samo C robi spory narzut w wydajności i pamięciożerności. Na razie chodzi mi o to, żeby móc normalnie coś zrobić w tym C - bez float-a się nie obędzie.

aktualna (nie-)wydajność (ANTIC on, emulator ON):

fadd     -  370.0 ops/s (FC - 4880)
fsub     -  370.0 ops/s (FC - 4650)
fmult    -  119.0 ops/s (FC -  380)
fdiv     -   75.1 ops/s (FC -  256)
flog2    -   60.2 ops/s
flog10   -   31.5 ops/s
flogn    -   21.4 ops/s
fsqrt    -   13.5 ops/s
fcbrt    -    3.8 ops/s
fexp     -    1.7 ops/s // (!)

fmult2   - 1666.0 ops/s (FC -  380?) ( z = a * 2 )
fmult2ip - 5000.0 ops/s (FC -  380?) ( a = a * 2 )
fmult10  -  200.0 ops/s (FC -  380?) ( z = a * 10 )
fmult10ip-  208.0 ops/s (FC -  380?) ( a = a * 10 )
fdiv2    - 1666.0 ops/s (FC -  256?) ( z = a / 2 )
fdiv2ip  - 6000.0 ops/s (FC -  256?) ( a = a / 2 )

-----
2006-01-17 Wyniki poprawione z 185 na 333 dla +-
2006-01-18 Dodane fdiv
2006-01-25 Optymalizacja i poprawiony benchmark (przedtem fałszywe wyniki dla mult, div)
2006-01-31 Więcej funkcji, fexp -musi- być poprawiony
2006-02-04 Przyspieszone mult, add, sub, sqrt, cbrt

Ostatnio edytowany przez piotrv (2006-02-04 12:59:30)

I'm not so bad, once you get to know me.

27

Odp: float w CC65

Dlaczego nie możesz zmieścić zakresu E-38/E+38 na ośmiu bitach wykładnika? Atari zakres E-98/E+98 zmieściło na pięciu.

Ostatnio edytowany przez drac030 (2006-01-16 03:29:55)

KMK
? HEX$(6670358)

28

Odp: float w CC65

Hihi, tu jest właśnie cały trick. Atari przechowuje wszystko w BCD, ja binarnie. W związku z tym mogę jako tako liczyć w C - patrz mnożenie - niby C a porównywalne z ASM. Jak się to przerobi na ASM, powinno być ze dwa razy szybsze. Na pewno da się coś jeszcze wycisnąć w C - ostatnia przeróbka fmul dała 3x przyspieszenie.

Nie wiem jak były liczone wyniki FASTCHIP na Atariki - jakby ktoś mógł zweryfikować czy z ANTICiem czy bez to byłoby super.

Mój format to float - czyli pojedyncza precyzja. Ale jeśli będzie to C to łatwo będzie dodać obsługę double (dwa razy większa mantysa + większa cecha). Aktualnie nie robie double, bo będzie to po prostu na pewno wolniejsze.

Dlaczego 38? bo 2^128 =ok. E+-38

Wersja double na PC ma zakres ok. E+-308

I'm not so bad, once you get to know me.

29

Odp: float w CC65

Jeśli robisz to binarnie to bądź tak miły i zachowaj kolejność lo/hi.

Wyniki FASTCHIP-a były liczone napisanym do tego programem w asemblerze i przy włączonym obrazie (w GR.0).

PS. A właśnie, to też jest wada C - nie daje dos?ępu do obliczeń dziesiętnych.

Ostatnio edytowany przez drac030 (2006-01-16 11:07:43)

KMK
? HEX$(6670358)

30

Odp: float w CC65

Aktualnie nie rozdrabniam mantysy niżej niż unsigned int, czyli o ile nie stosujesz 32-bitowego procesora to nie będzie problemu.

Co do wydajności, to mam nadzieje, że bardziej skomplikowane operacje wykażą przewagę formatu binarnego. Trochę dziwne, że dodawanie jest tyle razy wolniejsze - ale to pewnie narzut języka to powoduje.

----
Zresztą nawet gdyby się uprzeć i z niewiadomych mi powodów chcieć zrobić mantysę:

man-16-lo | man-16-hi

zamiast aktualnej:

man-16-hi | man-16-lo

to jest to zmiana polegająca na wymianie dwóch elementów struktury - więc jakies 5s pracy wprawnego programisty.

Ostatnio edytowany przez piotrv (2006-01-16 11:55:48)

I'm not so bad, once you get to know me.

31

Odp: float w CC65

Mi idzie (czyli "mnie się rozchodzi") o to, żeby bajty w 16-bitowym słowie były w kolejności low/high, bo to pozwoli na ewentualną przyszłą obróbkę 16-bitowymi rozkazami 65C816. Niestety natywny format Atari jest od starszego do młodszego, dzięki czemu jego wynalazca jest juz przeze mnie obsobaczony do piątego pokolenia wstecz. :>

Ostatnio edytowany przez drac030 (2006-01-16 16:29:01)

KMK
? HEX$(6670358)

32

Odp: float w CC65

Nie można walczyć z naturą...

Tzn. że 6502 wykorzystuje inny porządek niż 65C816?
Ja na poziomie słowa używam tego co jest w cc65 - więc pewnie natywnego dla 6502.

I'm not so bad, once you get to know me.

33

Odp: float w CC65

FP atarowskie wykorzystuje inny porządek niż naturalny dla 6502 (mantysa jest zapisywana od najstarszego bajtu do najmłodszego).

KMK
? HEX$(6670358)

34

Odp: float w CC65

drac030 napisał/a:

FP atarowskie wykorzystuje inny porządek niż naturalny dla 6502 (mantysa jest zapisywana od najstarszego bajtu do najmłodszego).

Mantysa jest w BCD, a 6502 ma operacje BCD tylko 8-bitowe, więc ciężko mówić o jakimś naturalnym porządku bajtów. >;->

piotrv: jeśli chodzi o wykładnik, to przecież może on być wykładnikiem 4 , 16 czy wręcz 256 a nie 2 i od razu będzie większy zakres liczb. Jeśli wybierzesz wykładnik 256 to operacje w rodzaju dodawania powinny dostać kopa (wydajnościowego), nawet jak dołożysz bajt na mantysę żeby nie tracić na dokładności.

https://www.youtube.com/watch?v=jofNR_WkoCE

35

Odp: float w CC65

Ale 65C816 ma 16-bitowe (operacje BCD) i stąd wiadomo, jaki jest ten "naturalny porządek".

KMK
? HEX$(6670358)

36

Odp: float w CC65

Większość algorytmów, które znalazłem operuje na zakresie E+/-38. Myślę, że marudzicie.
Do każdej operacji zapisuje algorytm. Myślę, że pierwsza wersja będzie tak jak jest, w następnych będzie można dodać wersję double lub oprzeć się o standardowe formaty float-a.

Co do znaku w bicie a nie w bajcie, to jest to bardzo rozsądne zapotrzebowanie jeśli chodzi o zajętość pamięci, ale wymaga pakowania / rozpakowywania float-a przed i po użyciu, przez co na pewno zwolni całą bibliotekę. Myślę, że na razie poprzestanę na dodaniu funkcji fpack, funpack pakujących do formatu bez extra bajtu znakowego - do zapisania na dysku lub w pamięci.

I'm not so bad, once you get to know me.

37

Odp: float w CC65

Myślę, że tak bardzo nie zwolni. Wydzielenie znaku z cechy to kilka cykli.

KMK
? HEX$(6670358)

38

Odp: float w CC65

Zajrzałem do kodu asm generowanego z CC65. Koszmar...
Nigdy nie myślałem, że 3 rejestry mogą aż tak ograniczać kompilator (większość kodu to move'y z/do pamięci...).

Ostatnie testy prędkości znacznie odbiegają od tego co powyżej.
(z pamięci)
fmult     - 37 ops/s
fmult10 - 200 ops/s
fmult2   - 1700 ops/s
fmult2ip -  5000 ops/s (być może więcej, koniec skali w benchmarku)

fmult2ip -> mnożenie * 2, argument i wynik w tym samym miejscu
fmult2 w stosunku do fmult2ip wykonuje 3 przesłania pamięciowe więcej - a jaki efekt...

Sama zmiana zmiennych z lokalnych / argumentowych na globalne zmniejszyła kod miejscami o połowe i przyspieszyła np. procedurę fmult dwukrotnie.

Mam już ze 20 funkcji, ale nadal pracuje nad przyspieszaniem tych podstawowych.

I'm not so bad, once you get to know me.

39

Odp: float w CC65

Ilość move'ów jeszcze nie przesądza o tym, że kod jest zły. Może wklej coś, to ocenimy.

KMK
? HEX$(6670358)

40

Odp: float w CC65

Są tam same LDA, JSR, LDY, LDA (xx),Y, JSR... Ale spox, wieczorem wkleje fmult - tak jako ciekawostkę...

------
Poniżej kod fdiv (dzielenie zmiennoprzecinkowe). Jest to na razie rekordzista w zwalnianiu - szczytowa prędkość to 46.7 ops/s Od razu mówię, że nie uważam to za coś finalnego...

Zaktualizowałem też wyniki we wcześniejszej tabeli.

Co do CC65 to nie jest tak źle jak myślałem - po zgrubnej analizie kodu asm zauważyłem, że kompilator ten robi parę optymalizacji - np. skraca warunki, jeśli po sprawdzeniu części wiadomo, że cały warunek już będzie fałszywy, to dalej nie liczy. Niestety jak widać od cholery tu JSRów, więc to takie średnio miłe.

; ---------------------------------------------------------------
; void __near__ fdiv (struct __float*, struct __float*, struct __float*)
; ---------------------------------------------------------------

.segment    "CODE"

.proc    _fdiv: near

.segment    "CODE"

    ldy     #$03
    jsr     ldaxysp
    ldy     #$03
    jsr     ldaxidx
    sta     _dw_z1
    stx     _dw_z1+1
    ldy     #$03
    jsr     ldaxysp
    ldy     #$05
    jsr     ldaxidx
    sta     _dw_z1+2
    stx     _dw_z1+2+1
    jsr     ldax0sp
    ldy     #$03
    jsr     ldaxidx
    sta     _dw_bm
    stx     _dw_bm+1
    jsr     ldax0sp
    ldy     #$05
    jsr     ldaxidx
    sta     _dw_bm+2
    stx     _dw_bm+2+1
    ldy     #$03
    jsr     ldaxysp
    ldy     #$00
    sta     ptr1
    stx     ptr1+1
    lda     (ptr1),y
    jsr     pusha0
    ldy     #$03
    jsr     ldaxysp
    ldy     #$00
    sta     ptr1
    stx     ptr1+1
    ldx     #$00
    lda     (ptr1),y
    ldy     #$80
    jsr     decaxy
    jsr     tossubax
    sta     _r0exp
    lda     _r0exp
    jsr     pusha0
    ldy     #$05
    jsr     ldaxysp
    ldy     #$00
    sta     ptr1
    stx     ptr1+1
    ldx     #$00
    lda     (ptr1),y
    jsr     tosicmp
    bcc     L0533
    beq     L0533
    ldy     #$07
    jsr     pushwysp
    lda     #$FD
    jsr     pusha
    jsr     _fsetspec
    jmp     incsp6
L0533:    lda     _dw_bm
    ora     _dw_bm+1
    jne     L0539
    lda     _dw_bm+2
    ora     _dw_bm+2+1
    jne     L0539
    jsr     ldax0sp
    ldy     #$00
    sta     ptr1
    stx     ptr1+1
    lda     (ptr1),y
    bne     L053F
    ldy     #$03
    jsr     ldaxysp
    ldy     #$03
    jsr     ldaxidx
    cpx     #$00
    bne     L0541
    cmp     #$00
    bne     L0541
    ldy     #$03
    jsr     ldaxysp
    ldy     #$05
    jsr     ldaxidx
    cpx     #$00
    bne     L0541
    cmp     #$00
    bne     L0541
    ldy     #$03
    jsr     ldaxysp
    ldy     #$00
    sta     ptr1
    stx     ptr1+1
    lda     (ptr1),y
    bne     L0541
    ldy     #$07
    jsr     pushwysp
    lda     #$FE
    jsr     pusha
    jsr     _fsetspec
    jmp     incsp6
L0541:    ldy     #$07
    jsr     pushwysp
    lda     #$FF
    jsr     pusha
    jsr     _fsetspec
    ldy     #$07
    jsr     pushwysp
    ldy     #$05
    jsr     ldaxysp
    ldy     #$01
    jsr     ldaidx
    jsr     staspidx
    jmp     incsp6
L053F:    jsr     pushw0sp
    lda     #$FF
    jsr     pusha
    jsr     _fisspec
    tax
    beq     L0551
    ldy     #$05
    jsr     pushwysp
    lda     #$FF
    jsr     pusha
    jsr     _fisspec
    tax
    beq     L0551
    ldy     #$07
    jsr     pushwysp
    lda     #$FE
    jsr     pusha
    jsr     _fsetspec
    jmp     incsp6
L0551:    ldy     #$07
    jsr     pushwysp
    ldy     #$05
    jsr     pushwysp
    jsr     _fcopy
    jmp     incsp6
L0539:    ldy     #$03
    jsr     ldaxysp
    ldy     #$00
    sta     ptr1
    stx     ptr1+1
    lda     (ptr1),y
    beq     L0562
    lda     _dw_z1
    ora     _dw_z1+1
    bne     L0561
    lda     _dw_z1+2
    ora     _dw_z1+2+1
    bne     L0561
L0562:    ldy     #$07
    jsr     pushwysp
    ldy     #$09
    jsr     pushwysp
    ldx     #$00
    txa
    ldy     #$04
    jsr     staxspidx
    ldy     #$02
    jsr     staxspidx
    ldy     #$05
    jsr     ldaxysp
    sta     sreg
    stx     sreg+1
    lda     #$00
    tay
    sta     (sreg),y
    ldy     #$05
    jsr     ldaxysp
    sta     sreg
    stx     sreg+1
    lda     #$01
    tay
    jmp     L0A13
L0561:    tya
    sta     _dw_z+2
    sta     _dw_z+2+1
    sta     _dw_z
    sta     _dw_z+1
    lda     #$20
L0A21:    sta     _loop_cnt
    lda     _loop_cnt
    jeq     L0577
    lda     _loop_cnt
    cmp     #$11
    bcs     L0A26
    lda     _dw_z+2+1
    and     #$80
    beq     L057E
    lda     _dw_z
    ldx     _dw_z+1
    jsr     shlax1
    ora     #$01
    jmp     L0A1D
L057E:    lda     _dw_z
    ldx     _dw_z+1
    jsr     shlax1
L0A1D:    sta     _dw_z
    stx     _dw_z+1
L0A26:    lda     _dw_z+2
    ldx     _dw_z+2+1
    jsr     shlax1
    sta     _dw_z+2
    stx     _dw_z+2+1
    lda     _dw_z1
    ldx     _dw_z1+1
    jsr     pushax
    lda     _dw_bm
    ldx     _dw_bm+1
    jsr     tosicmp
    bcs     L058C
    ldx     #$FF
    jmp     L05A5
L058C:    lda     _dw_z1
    ldx     _dw_z1+1
    jsr     pushax
    lda     _dw_bm
    ldx     _dw_bm+1
    jsr     tosicmp
    bcc     L0592
    bne     L059E
L0592:    lda     _dw_z1+2
    ldx     _dw_z1+2+1
    jsr     pushax
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     tosicmp
    bcs     L0598
    ldx     #$FF
    jmp     L05A5
L0598:    lda     _dw_z1+2
    ldx     _dw_z1+2+1
    jsr     pushax
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     tosicmp
L059E:    ldx     #$00
L05A5:    txa
    bpl     L0588
    lda     _dw_z+2
    ldx     _dw_z+2+1
    jmp     L0A1E
L0588:    lda     _dw_z1+2
    ldx     _dw_z1+2+1
    jsr     pushax
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     tosicmp
    bcs     L05A9
    ldx     #$FF
    txa
    jsr     pushaFF
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     tossubax
    clc
    adc     _dw_z1+2
    pha
    txa
    adc     _dw_z1+2+1
    tax
    pla
    jsr     incax1
    sta     _dw_z1+2
    stx     _dw_z1+2+1
    lda     _dw_z1
    ldx     _dw_z1+1
    jsr     pushax
    lda     _dw_bm
    ldx     _dw_bm+1
    jsr     tossubax
    jsr     decax1
    jmp     L0A1F
L05A9:    lda     _dw_z1+2
    ldx     _dw_z1+2+1
    jsr     pushax
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     tossubax
    sta     _dw_z1+2
    stx     _dw_z1+2+1
    lda     _dw_z1
    ldx     _dw_z1+1
    jsr     pushax
    lda     _dw_bm
    ldx     _dw_bm+1
    jsr     tossubax
L0A1F:    sta     _dw_z1
    stx     _dw_z1+1
    lda     _dw_z+2
    ldx     _dw_z+2+1
    ora     #$01
L0A1E:    sta     _dw_z+2
    stx     _dw_z+2+1
    lda     _loop_cnt
    cmp     #$11
    bcs     L05B6
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     shrax1
    sta     _dw_bm+2
    stx     _dw_bm+2+1
    jmp     L05BA
L05B6:    lda     _dw_bm
    and     #$01
    beq     L05BB
    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     shrax1
    pha
    txa
    ora     #$80
    tax
    pla
    jmp     L0A20
L05BB:    lda     _dw_bm+2
    ldx     _dw_bm+2+1
    jsr     shrax1
L0A20:    sta     _dw_bm+2
    stx     _dw_bm+2+1
    lda     _dw_bm
    ldx     _dw_bm+1
    jsr     shrax1
    sta     _dw_bm
    stx     _dw_bm+1
L05BA:    lda     _loop_cnt
    sec
    sbc     #$01
    jmp     L0A21
L0577:    lda     _dw_z
    ora     _dw_z+1
    bne     L05CB
    lda     _dw_z+2
    ora     _dw_z+2+1
    beq     L05CC
L05CB:    lda     _dw_z+1
    and     #$80
    tax
    lda     #$00
    jsr     bnegax
    beq     L05CC
    lda     _r0exp
    beq     L05CC
    lda     _dw_z+2+1
    and     #$80
    beq     L05D2
    lda     _dw_z
    ldx     _dw_z+1
    jsr     shlax1
    ora     #$01
    jmp     L0A22
L05D2:    lda     _dw_z
    ldx     _dw_z+1
    jsr     shlax1
L0A22:    sta     _dw_z
    stx     _dw_z+1
    lda     _dw_z+2
    ldx     _dw_z+2+1
    jsr     shlax1
    sta     _dw_z+2
    stx     _dw_z+2+1
    lda     _r0exp
    sec
    sbc     #$01
    sta     _r0exp
    jmp     L05CB
L05CC:    ldy     #$05
    jsr     ldaxysp
    sta     ptr1
    stx     ptr1+1
    lda     _dw_z
    ldx     _dw_z+1
    ldy     #$02
    sta     (ptr1),y
    iny
    txa
    sta     (ptr1),y
    ldy     #$05
    jsr     ldaxysp
    sta     ptr1
    stx     ptr1+1
    lda     _dw_z+2
    ldx     _dw_z+2+1
    ldy     #$04
    sta     (ptr1),y
    iny
    txa
    sta     (ptr1),y
    ldy     #$03
    jsr     ldaxysp
    ldy     #$01
    jsr     ldaidx
    jsr     pushax
    ldy     #$03
    jsr     ldaxysp
    ldy     #$01
    jsr     ldaidx
    jsr     tosicmp
    beq     L05E1
    ldy     #$05
    jsr     ldaxysp
    sta     sreg
    stx     sreg+1
    lda     #$00
    jmp     L0A23
L05E1:    ldy     #$05
    jsr     ldaxysp
    sta     sreg
    stx     sreg+1
    lda     #$01
L0A23:    ldy     #$01
    sta     (sreg),y
    lda     _dw_z
    ora     _dw_z+1
    bne     L05E8
    lda     _dw_z+2
    ora     _dw_z+2+1
    beq     L0A1B
L05E8:    lda     _r0exp
L0A1B:    sta     _zexp
    ldy     #$05
    jsr     ldaxysp
    sta     sreg
    stx     sreg+1
    lda     _zexp
    ldy     #$00
L0A13:    sta     (sreg),y
    jmp     incsp6

.endproc

Ostatnio edytowany przez piotrv (2006-01-25 23:02:36)

I'm not so bad, once you get to know me.

41

Odp: float w CC65

Tja, zastapienie samych jsrow makrami przyspieszy sporo...

(nie wiem jak dlugie sa te prock, ktore se to wywoluje, ale moze warto)

42

Odp: float w CC65

Bardziej by się przydał kod w C - asma zainteresowani mogą sobie wygenerować.
"Inlajnowanie" procedur runtime-owych raczej niewiele pomoże - myślę, że problem stanowi nieefektywny kod w C.

W C można przekazywać structy przez wartość, co w przypadku małych structów których nie zamierzamy modyfikować jest zdecydowanie bardziej efektywne (niezależnie od procka i kompilatora). Nie wiem tylko, czy CC65 to obsługuje. Sugerowałem użycie 32-bitowych floatów, bo wtedy można zrobić:
typedef long int float;
typedef float double;
i można przekazywać floaty "normalnie".

https://www.youtube.com/watch?v=jofNR_WkoCE

43

Odp: float w CC65

(po weryfikacji long-a przeedytowane)

W cc65 structy muszą być przekazywane przez pointer.

Rzeczywiście, long w cc65 jest 32-bitowy. Jeśli nawet nie będę przerabiał całego float-a, to na pewno przyda się ta informacja do przechowywania mantysy. Dzięki!

Argumenty funkcji w C są takie:

fdiv(_float result*, _float *a, _float *b);

result = a / b

Źródła udostępnie jak skończę ftoa (->ASCII), co wymaga flog10, co wymaga flog2...
(nie chce -się- zniechęcać).

Ostatnio edytowany przez piotrv (2006-01-26 20:33:30)

I'm not so bad, once you get to know me.

44

Odp: float w CC65

(Przeredagowane po poważniejszych testach)

Wyniki przeróbki mantysy:

Przerobiłem:

typedef struct __dword {
  word word1;
  word word2;
} _dword;

na:

typedef unsigned long _dword;

Po przeróbce - przy tym samym algorytmie, fdiv działa tak:

było:
  fdiv - 46.7 ops/s  (FC -  256)
jest:
  fdiv - 58.8 ops/s (FC -  256)

Niestety nie jest tak różowo, jak mi się początkowo wydawało... No ale i tak lepiej. No i kod znacznie się uprościł.

Ostatnio edytowany przez piotrv (2006-01-26 22:47:06)

I'm not so bad, once you get to know me.

45

Odp: float w CC65

Na miesiąc wyjeżdzam na wakacje, dlatego spakowałem to co już mam i wywiesiłem dla wszystkich zainteresowanych na stronie:

http://republika.pl/piotrek_home/Atari/fmath65.html

Pozdro

I'm not so bad, once you get to know me.