kategoria: ANSI C
[#1] [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?
Moze precyzyjnie:
- mam zmienną liczbową np. int lub byte na której przeprowadzam operacje, np. dodaje punkty itp.
- muszę tą liczbę wypisać na ekranie za pomocą swoich 'kolorowych' cyferek które zrobiłem jako bitmapa.

W przypadku gdybym miał do czynienia z ciągiem gotowych znaków, np. "To jest tekst"
to sprawa jest jasna bo mogę od razu przejść po tablicy i dla każdego znaku wypisać potrzebną literkę.

Ale jak sprawa się ma gdy mamy LICZBĘ?
- mam ją zamienieć na tablicę znaków poprzez sprintf ? czy to nie będzie niewydajne?
- drugi sposób to wyłuskać jednosci, dziesiatki itp, za pomocą dzielenia, modulo - ale to chyba jeszcze gorsze..

Nie ma co tu Ameryki odkrywać, bo a każdej grze mamy "punkty" lub jakieś "liczby",
ale nie wiem jak to sie robi z liczbami...
[#2] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #1

ps. może jakas zoptymalizowana wersja sprintf?
1
[#3] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #2

Wyłuskiwanie w sumie nie jest też złe.

Trzecią opcją jest stablicowanie wszystkich liczb i porobienie gotowych referencji do grafik. O ile starczy Ci pamięci.

Porób testy z dwiema opcjami co szybsze.
1
[#4] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #1

Bardzo dużo używam sprintf (a dokładniej snprintf) i nigdy nie miałem z tym problemów wydajnościowych. Ale ja od wielu lat nie dotykałem nic słabszego niż G4.
Ciężko powiedzieć co tam siedzi w implementacji sprintf, ale ze względu na swoją uniwersalność na pewno nie jest to prosta funkcja.

Gdy zapytamy ChatGPT o to żeby zaproponował coś szybszego od sprintf to poleci (ale bez wielkiego przekonania) coś w tym stylu:

#include <stdio.h>
#include <string.h>

void intToStr(int liczba, char str[]) {
    int i = 0;
    int isNegative = 0;

    // Sprawdzamy, czy liczba jest ujemna
    if (liczba < 0) {
        isNegative = 1;
        liczba = -liczba;  // Zmieniamy znak liczby na dodatni
    }

    // Wyciągamy cyfry
    do {
        str[i++] = (liczba % 10) + '0';  // Pobieramy najmniej znaczącą cyfrę i zamieniamy ją na znak ASCII
        liczba /= 10;                    // Dzielimy liczbę przez 10, aby przesunąć się do kolejnej cyfry
    } while (liczba > 0);

    // Jeśli liczba była ujemna, dodajemy znak minus
    if (isNegative) {
        str[i++] = '-';
    }

    // Dodajemy znak końca stringa
    str[i] = '\0';

    // Odwracamy string, ponieważ cyfry zostały zapisane w odwrotnej kolejności
    int len = strlen(str);
    for (int j = 0; j < len / 2; j++) {
        char temp = str[j];
        str[j] = str[len - j - 1];
        str[len - j - 1] = temp;
    }
}

int main() {
    int liczba = -12345;
    char str[12];
    intToStr(liczba, str);
    printf("Liczba jako string: %s\n", str);
    return 0;
}


Bardzo kusi pozbycie się tego odwracania stringa na końcu.
Dobrze byłoby zmierzyć czas w jaki operacja wykona się np. 10000 razy z użyciem sprintf() i tego jego intToStr(). Wiedzielibyśmy czy gra jest warta świeczki czy nie warto się bić.

Ostatnia aktualizacja: 24.09.2024 21:33:59 przez MDW
1
[wyróżniony] [#5] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@MDW, post #4

Ja używam czegoś takiego i masz tam div10 oraz mod10. Wystarczająco szybkie na moje potrzeby, i potem to sobie rysuję swoim fontem jak każdy inny string.

Jak potrzebujesz żeby to było naprawdę szybkie, to 68k ma instrukcje do liczb w systemie BCD i stamtąd jesteś w stanie dużo łatwiej wyciągnąć pojedyncze cyfry. Nie pamiętam tylko, czy tam nie było takiej historii że zamiast konwertować co rusz z inta na bcd to czy jednak tam nie trzeba całej arytmetyki na tym robić. Musiałbyś doczytać i poszukać za tym jak to inline asmem ogarnąć, bo raczej w gcc nie znajdziesz intrinsiców do tego.

Ostatnia aktualizacja: 24.09.2024 21:52:28 przez teh_KaiN
2
[#6] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@teh_KaiN, post #5

Jeżeli możesz, to po prostu zapisuj od końca wstecz. Zrób założenie że masz 16bajtowy bufor wyjściowy (zmień proto czy coś, żeby było jasne), ustaw kursor na koniec i pisz do tyłu a potem po prostu zwróć początek. Wiadomo że odwracanie kolejności to mniej cykli niż pewnie nawet jedno dzielenie, no ale jak już takie pierdółki optymalizujemy to dlaczego nie.
2
[#7] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@_DiskDoctor_, post #3

na razie w ten sposób zrobiłem, dzielenia chyba nie da się uniknąć, poza tablicowaniem

do glownego gameplay potrzebuje max 3 cyfrowe liczby powiedzmy z zakresu 0-250
(ale za to 6 takich liczb)

void    E_fast_font__render_max_3_digits(int _number, int _color_offset, unsigned char* _dst)
{
    int hundred = _number / 100;
    int ten = (_number % 100) / 10;
    int one = _number % 10;

    _dst -= 4;
    if (one == 1) _dst += 2;

    e_fast_font__render_digit[one](_dst, e_fast_font + _color_offset);

    if (hundred != 0)
    {
        _dst -= 4;
        if (ten == 1) _dst += 2;

        e_fast_font__render_digit[ten](_dst, e_fast_font + _color_offset);

        _dst -= 4;
        if (hundred == 1) _dst += 2;

        e_fast_font__render_digit[hundred](_dst, e_fast_font + _color_offset);
    }
    else if(ten != 0)
    {
        _dst -= 4;
        if (ten == 1) _dst += 2;

        e_fast_font__render_digit[ten](_dst, e_fast_font + _color_offset);
    }
}
1
[#8] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@teh_KaiN, post #5

o rozwiązanie Kiero jest też ciekawe (bez modulo), musze potem porównać obie..
1
[#9] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #8

Moje tez ma modulo tylko zapisuje odwrotnie:)
1
[#10] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@kiero, post #9

a sorki, pomyliłem - miałem na myśli kod który podał tech_Kain
1
[wyróżniony] [#11] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #7

Da sie uniknac dzielenia i tablic, uzywajac odejmowania.
Kiedys dawalem na PPA taka procedure, wiec gdzies tutaj jest.
Ale raczej watpie, zeby sie nadawala na przerobke na C.
Ja przynajmniej sobie nie wyobrazam, zeby kompilator byl w stanie cos takiego zrobic, bo to czysty ASM byl i do tego uzywany dosc specyficznie, zeby max speed byl.
Ale moze sie myle, i sie da, ja sie nie znam na C.
1
[wyróżniony] [#12] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #1

Kiedys, dawno temu zrobilem cos w ten desen, bez dzielenia:

#include <stdio.h>

char* IntToStr(int Liczba);
char  Check(int *LiczbaA, char factor);

int main(void)
{
        int liczba = 234;

        printf("%s\n", IntToStr(liczba));

        return(0);
}

char* IntToStr(int Liczba)
{
        static char str[4];
                
        str[0] = Check(&Liczba, 100);
        str[1] = Check(&Liczba, 10);
        str[2] = Check(&Liczba, 1);
        str[3] = '\0'; 

        return(str);
}

char Check(int *LiczbaA, char factor)
{
        char x = '0' - 1;

        do
        {
                *LiczbaA -= factor;
                x++;
        }
        while(*LiczbaA >= 0);

        *LiczbaA += factor;
        
        return(x);
}
2
[#13] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Phibrizzo, post #12

dzieki potestuje potem
[#14] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Don_Adan, post #11

Nawet znalazlem:
link

Ostatnia aktualizacja: 24.09.2024 23:12:18 przez Don_Adan
1
[#15] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@teh_KaiN, post #5

Jeśli chodzi o wstawki asemblerowe z BCD to są dostępne trzy polecenia: ABCD (dodawanie), SBCD (odejmowanie) i NBCD (negacja).

Odkodowanie BCD nie powinno być trudne. Zwyczajne przesunięcie i maska. W jednym bajcie znajdują się dwie cyfry dziesiętne (górne i dolne 4 bity).

Ja polecam takie rozwiązanie:

Prototyp funkcji w C (przykład dla SAS/C i DICE):

__asm UBYTE addBCD(register __a0 UBYTE *op1, register __a1 UBYTE *op2);

Wstawka w asm (dodaje dwie ośmiocyfrowe liczby w pamięci i zwraca bajt o wartości 0xff gdy jest przeniesienie i 0x00 gdy go nie ma):

CYFRY equ 8

_addBCD:
    adda.w #CYFRY/2,a0
    adda.w #CYFRY/2,a1
    abcd  -(a1),-(a0)
    abcd  -(a1),-(a0)
    abcd  -(a1),-(a0)
    abcd  -(a1),-(a0)
    scs   d0
    rts

Jeśli chcesz skonwertować ciąg cyfr (znaków) na format BCD, by liczyć za pomocą szybkich instrukcji wpisujesz:

#define CYFRY 8

char test[ CYFRY ] = "00012345";

void znaki_do_bcd(char bcd[], char cyfry[])
{
    int i;

    for( i = 0; i < CYFRY / 2; i++)
    {
        bcd[i] = ((cyfry[i * 2] - '\0') << 4) | (cyfry[i * 2 + 1] - '\0');
    }       
}

Jeżeli teraz chcesz dodać punkty to wpisujesz:

addBCD( punkty_bcd, bonus_bcd );

Żeby skonwertować z powrotem na ciąg cyfr (znaków), by wydrukować (narysować) wpisujesz:

char bcd_do_znaki(char cyfry[], char bcd[])
{
    int i;
    for( i = 0; i < CYFRY / 2; i++)
    {
        cyfry[i * 2] = (bcd[i] >> 4) + '\0';
        cyfry[i * 2 + 1] = (bcd[i] & 0xf) + '\0';
    }    
}


Ostatnia aktualizacja: 24.09.2024 23:14:04 przez Hexmage960
1
[#16] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #7

3 cyfrowe liczby powiedzmy z zakresu 0-250


To ja powracam do swojego pomysłu.

Stablicuj grafiki dla liczb 0-250, wrzuć do tablicy indeksowanej takimi samymi liczbami -- problem z głowy, nic nie musisz przeliczać.

Spróbuj.
1
[#17] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Hexmage960, post #15

Moja intuicja podpowiada, że to rozwiązanie z BCD będzie najszybsze. Po to właśnie powstało BCD

Jeśli operacji wyświetlania liczby będzie dużo więcej niż jej zmian, to można rozważyć zapamiętywanie każdej cyfry w osobnym bajcie (w tym przypadku 3 bajty). Wtedy trzeba tylko oprogramować operacje arytmetyczne i porównania na takiej liczbie (dodawania i odejmowanie będą proste).
Jeśli porównań będzie sporo, to można dla nich przechowywać kopię liczby w klasycznym bajcie.
[#18] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #1

Wszystko zależy od lenistwa i jak mocno chcesz się kopać z koniem.

Ogólnie ja używam odejmowania, bo nie muszę za dużo myśleć jak to zrobić za pomocą któregoś tam sprintf czy innego itoa.
Jeśli jesteśmy przy grach:
Jeśli chodzi o życia pohatera to ustawiam sobie że będę wyświetlał maksymalnie 9 żyć, ale wewnętrznie jest to UWORD. Taki lamerski przykładzik
void HudDrawLives(void)
{
	UWORD nr = gm->lives;

	ULONG scrOfs = 26;

	if (nr > 9)
	{
		nr = 9;
	}

	char c = '0' + nr;

	GfxSmallFontItemsAdd(c, scrOfs);
}

W przypadku zbierania jakiś tam rzeczy przez bohatera, to wygląda to tak:
void HudDrawGems(void)
{
	UWORD nr = gm->totalItems;
	ULONG scrOfs = 13;

	char c = '0';
	while (nr > 99)
	{
		c++;
		nr -= 100;
	}
	GfxSmallFontItemsAdd(c, scrOfs++);

	c = '0';

	while (nr > 9)
	{
		c++;
		nr -= 10;
	}
	GfxSmallFontItemsAdd(c, scrOfs++);

	c = '0' + nr;
	GfxSmallFontItemsAdd(c, scrOfs++);
}

Analogicznie można to rozeszerzać metodą znanego kodziarza - Kopiego-Pasty. By było jaśniej to funkcyja GfxSmallFontItemsAdd dodaje do swojej listy rzeczy które pózniej w pętli gry są wrzucane na ekran.
Na tą chwilę to w zupełności mi wystarcza wydajnościowo o ile nie patrze w kod asm wygenerowany przez kompilator.

A jeśli jesteśmy przy punktach w grze i zależy nam na szybkości to najszybciej według mnie to będzie nie używać punktów i cześć (już dzwonili z piekła i pytali się o mój numer :) ).
Ale jeśli już bardzo chcemy to ja bym trzymał je jako longi, a w śródku adresy do cyfr z bitmapy. Jeśli mamy cztery cyfry przeznaczone na punktację, to mamy cztery longi.
Można też trzymać jako string "0010" lub jego wariację, na przykład 0,0,1,0 ale tu trzeba w pętli obliczać adres i ewentualnie odejmować '0'. To że mamy cztery cyfry na punktacje daje nam sporo możliwości, bo zawsze możemy wyświetlać fejkowe dodatkowe na przykład dwa zera. Wtedy najmniejszy bonus dla gracza to 100 punktów (ja tam wolę dostawać 100 punktów w grzez zamiast jednego :) ) ale wewnętrznie to jest 1 punkt. Zostaje sprawa dodawania ale to jest też banał bo tu kłania się podstawówka ( ja na szczęście tam byłem ). W najgorszym przypadku trzeba zmienić 5 adresów (sytuacja 9,9,9,9) , ale największy możliwy wynik idzie dokładnie policzyć bądź oszacować w grze.

A jeśli chodzi o BCD i asm, to warto przejrzeć źródła z Atari ST z gry Toki, link i szuakć pod etykietą affichescore bądź szukać abcd.

Zapomniałem o jednym drobnym szczególe, w przypadku gdy mamy te longi i chcemy mieć higscore to wtedy sobie konwertujemy na inta, a robimy to poza pętlą właściwej gry i akurat tam nie potrzebujemy prędkości.
1
[#19] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@asman, post #18

Ooo, Mcodera znalazles. Dobry byl. Sporo mnie nauczyl.
[#20] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Don_Adan, post #19

Dzięki Wszystkim za rózne pomysły, na ten moment sprawdziłem kilka ale nie wszytkie
- ten pierwszy z dzieleniem i modulo
- sprintf
- Phibrizzo bez dzielenia #12
- i ztablicowanie (ale tylko wąski przedział)

Więc wyszło mi w ten sposób:

- ten pierwszy z dzieleniem i modulo oraz PHibrizzo trwają bardzo podobnie - zauważyłem,
że kompilator optymalizuje dzielenie i modudo do odejmowania i pojawia się mnożenie też.
W uproszczonym teście bez zbędnych rzeczy wersja z dzieleniem była nieco szybsza od Phibrozzo choć niewiele,
pewnie po prostu kompilator po swojemu zoptymalizował dzielenie i modulo.

- sprintf - jest bardzo wolno - wychodziło mi niemal dwukrotnie wolniej w testach niz pozostałe sposoby - wiec na pewno odpada

- ztablicowanie sprawdzialem ale tylko w uproszczeniu w wąskim i bylo kilka punktów szybciej niż dwie pierwsze najszybsze metody.
Pewnie można się było tego spodziewać.

Na ten moment zostanę w takim razie przy wersji z dzieleniem i modulo - gdyż kompilator sensownie to optymalizuje.

Nie wiem jak z pozostałymi metodami, zostawie je na potem. I musze je bardziej zrozumieć.
3
[#21] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #20

kompilator optymalizuje dzielenie i modudo do odejmowania i pojawia się mnożenie też
Dzielenie całkowite przez stałą można zastąpić mnożeniem przez jej przeskalowaną odwrotność. Większość kompilatorów tak optymalizuje.
1
[#22] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #20

Wszystko zalezy jak fontow uzywasz.
Czy to caly zestaw jest, czy tylko same cyfry od 0 do 9
Bo w jednym przypadku 0 to bedzie bodaj $30 a w drugim przypadku 0 to bedzie 0.
No i wyswietlane wartosci.
Jesli to sa tylko wartosci od 000 do 250.
To tablica bedzie najszybsza ale i najwieksza.
Najwolniejsze bedzie dzielenie.
A odejmowanie pomiedzy nimi, zalezy jaka wartosc jest wyswietlana.
Bo ta metoda polega na odejmowaniu , w Twoim przypadku najpierw 100, i liczeniu ile razy sie miesci.
Potem odejmowaniu 10.
Tylko, zeby ona ladnie dzialala to raczej musi byc asembler a nie C.
Bo tu chodzi, zeby bylo jak najmniej branchy/skokow.
W ASM to bedzie 1 branch na petle, w C to beda prawie na pewno 2 branche na petle.
[#23] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Krashan, post #21

Niezbyt. Zależy od typu (czy można podbić żeby zmieścić pośredni wynik) i od instrukcji procesora. Musiałby sprawdzić czy przy np int8/16 kompilator tak zrobi.
[#24] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Don_Adan, post #22

W ASM to bedzie 1 branch na petle, w C to beda prawie na pewno 2 branche na petle.
Będzie jeden, jeżeli przemyśli się organizację pętli i zrobi to tak jak Phibrizzo w poście 12. Wtedy masz taki kod w C:
char* NumToStr(short liczba, char *bufor)
{
	char k = 47;

	do
	{
		liczba -= 100;
		k++;
	}
	while (liczba >= 0);

	liczba += 100;
	bufor[0] = k;
	k = 47;
	
	do
	{
		liczba -= 10;
		k++;
	}
	while (liczba >= 0);
	
	bufor[1] = k;
	bufor[2] = 58 + liczba;
	bufor[3] = 0x00;
	return bufor;
}

Z tego moje stare GCC 2.95.3 produkuje takiego oto asma (liczba w D0, adres bufora w A0):

_NumToStr:
    MOVEQ   #47,d1
.1: ADDQ.B  #1,d1
    ADDI.W  #-100,d0
    BPL.S   .1
    ADDI.W  #100,d0
    MOVE.B  d1,(a0)
    MOVEQ   #47,d1
.2: ADDQ.B  #1,d1
    ADDI.W  #-10,d0
    BPL.S   .2
    MOVE.B  d1,1(a0)
    ADDI.B  #58,d0
    MOVE.B  d0,2(a0)
    CLR.B   3(a0)
    MOVE.L  a0,d0
    RTS

Ostatnie MOVE.L jest tylko po to, żeby funkcja zwracała adres bufora, można jej wtedy użyć jako argumentu dla printf(). Ciekawe czy w asmie można jeszcze coś urwać.
1
[#25] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #20

Chciałbym poprawić jedną drobną omyłkę, która pojawiła się w zamieszczonym przeze mnie kodzie.

Otóż oczywiście zamiast:

'\0'

które oznacza bajt o wartości 0 (używany jako znak końca ciągu w C), chodziło mi o:

'0'

co oznacza znak ASCII symbolu 0.

Jeżeli konwertujemy znaki cyfr z ASCII do cyfr to odejmujemy '0'. Jeżeli konwertujemy cyfry na znaki ASCII, to dodajemy tę wartość.

Przykład, który pokazuje różnicę:

Tutaj jest tablica cyfr:

char cyfry[] = { 0, 0, 0, 1, 2, 3, 4, 5 };

A tutaj jest tablica znaków ASCII z cyframi (z dodanym znakiem końca ciągu):

char ascii[] = "00012345";

Oczywiście skoro chcesz te cyfry narysować na ekranie to nie musisz się martwić o kodowanie ASCII, tylko bezpośrednio odczytać cyfrę. Ale jeżeli masz te grafiki z cyframi połączone z grafiką innych znaków, np. liter to ASCII jest bardzo pomocny.

Jeszcze postaram się pokazać jak powyższy przykład działa dla formatu BCD:

char bcd[] = { 0x00, 0x01, 0x23, 0x45 };


Ostatnia aktualizacja: 25.09.2024 20:16:05 przez Hexmage960
1
[#26] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Krashan, post #24

Mi vbcc wygenerował
_NumToStr
	movem.l	l134,-(a7)
	moveq	#10,d4
	moveq	#100,d3
	move.w	(6+l136,a7),d1
	move.l	(8+l136,a7),a1
	moveq	#47,d2
l115
	sub.w	d3,d1
	addq.b	#1,d2
	tst.w	d1
	bge	l115
	add.w	d3,d1
	move.b	d2,(a1)
	moveq	#47,d2
l116
	sub.w	d4,d1
	addq.b	#1,d2
	tst.w	d1
	bge	l116
	move.b	d2,(1,a1)
	move.w	d1,d0
	ext.l	d0
	add.l	#58,d0
	move.b	d0,(2,a1)
	move.b	#0,(3,a1)
	move.l	a1,d0
l134	reg	d2/d3/d4
	movem.l	(a7)+,d2/d3/d4
l136	equ	12
	rts

O ile fajnie że użył rejestrów d3 i d4, i jest odejmowanie jako sub, to i tak to średnio wygląda.
Użyłem też gcc z Twojej paczki i wysżło mi coś takiego
_NumToStr:	moveq	#$2F,d1
lbC0000A6:	add.w	#$FF9C,d0
	addq.b	#1,d1
	tst.w	d0
	bge.s	lbC0000A6
	add.w	#$64,d0
	move.b	d1,(a0)
	moveq	#$2F,d1
lbC0000B8:	add.w	#$FFF6,d0
	addq.b	#1,d1
	tst.w	d0
	bge.s	lbC0000B8
	move.b	d1,1(a0)
	add.b	#$3A,d0
	move.b	d0,2(a0)
	clr.b	3(a0)
	move.l	a0,d0
	rts


Przełaczniki takie jak w makefile w Untagle. Być może robię coś źle.

Powracając do twojego kodu w asm to jest co urwać tutaj. Dla mnie przede wszystkim powinno być sub.w #100,d0 zamiast add.w #-100,d0. Dzięki temu można przewalić 100 i 10 do osobnych rejestrów i użyć do tego moveq. Zaoszczędzi się wtedy jak dobrze pamiętam ze 4 cykle w pętlach, potem 4 cykle na add.w #100,d0 i 4 cykle na addw. #10,d0. Poza tym tutaj też chciałbym by move.b d1,(a0) --> move.b d1,(a0)+ i wtedy zaoszczędzimy 4 cykle na move.b d1,1(a0) i 4 cykle na move.b d1,2(a0) ,i 4 cykle na clr.b 3(a0).
Czyli podsumowując za pomocą dwóch moveq, które kosztują 8 cykli , zostanie nam 4 * ilość w 1 pętli + 4 * ilość w 2 pętli + 4 + 4 + 4. I chyba wtedy wyjdzie kod w stylu Don Adana.

Cały mój problem polega na tym że ja nie wiem dokładnie jak działa kompilator i w jaki sposób robi się takie optymalizacje. Nawet ostatnio myśłałem coby sobie kupic jaką książkę o budowie kompilatorów i poczytać. Podejrzewam że to bardzo twardy temat.
1
[#27] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@asman, post #26

O vbcc mam swoje dość niepochlebne zdanie, które po raz kolejny się potwierdziło. Uważam, że z kompilatorów C, które można sensownie używać na rzeczywistej (trochę dopalonej, ale bez przesady) Amidze, GCC 2.95.3 jest najlepszy. Do tego umie w C++.

Jeżeli chodzi o kod, który pokazałeś, to ja tu zawiniłem, ale post już był po okresie edycji. W obu pętlach trzeba najpierw dać k++;, a potem odejmowanie. Wtedy kompilator zatrybia, że TST.W jest niepotrzebne i zmienia instrukcje skoku warunkowego na BPL. Mógłby sam na to wpaść i zmienić kolejność wyrażeń, ale widocznie aż tak sprytny nie jest.
Cały mój problem polega na tym że ja nie wiem dokładnie jak działa kompilator
Też nie wiem dokładnie jak działa kompilator, ale lubię sobie zdisasemblować kod, który napisałem i zobaczyć co on tam nawywijał. A co do ścinania cykli, to cóż, aż tak dobry nie jestem i jak piszę w asmie to, po części podświadomie, optymalizuję na rozmiar. Poza tym, to ma sens chyba tylko jak piszemy pod konkretny procesor i to najbardziej 68000. Jak ktoś to odpali na 030/060/Vampire/PiStorm, to całe liczenie cykli bierze w łeb, tak coś czuję.

Niemniej, uzbrojony w Twoją wiedzę, postanowiłem pomęczyć kompilator. Zamiana adresowania bufora z indeksowanego na postincrement była prosta, wystarczyło pisać *bufor++ zamiast bufor[x], gorzej było ze zmuszeniem kompilatora do trzymania 100 i 10 w rejestrach, pomogło dopiero zmniejszenie optymalizacji z -O2 do -O1, trochę słabe, ale może jakby pogmerać w detalicznych ustawieniach optymalizatora, to by coś dało. A i tak kompilator zrobił wygibasa, o którym niżej. Kod w C wygląda teraz tak:
void NumToStr(short liczba, char *bufor)
{
	char k = 47;
	short sto = 100, dycha = 10;
	
	do
	{
		k++;
		liczba -= sto;
	}
	while (liczba >= 0);

	liczba += sto;
	*bufor++ = k;
	k = 47;
	
	do
	{
		k++;
		liczba -= dycha;
	}
	while (liczba >= 0);
	
	*bufor++ = k;
	*bufor++ = 58 + liczba;
	*bufor = 0x00;
}

I odpowiadający mu kod w asemblerze:
_NumToStr:
    MOVE.L   d2,-(sp)
    MOVEQ    #47,d1
    MOVEA.W  #100,a1
    MOVEQ    #10,d2
.1: ADDQ.B   #1,d1
    SUB.W    a1,d0
    BPL.S    .1
    ADD.W    a1,d0
    MOVE.B   d1,(a0)+
    MOVEQ    #47,d1
.2: ADDQ.B   #1,d1
    SUB.W    d2,d0
    BPL.S    .2
    MOVE.B   d1,(a0)+
    ADDI.B   #58,d0
    MOVE.B   d0,(a0)+
    CLR.B    (a0)
    MOVE.L   (sp)+,d2
    RTS

Kompilator wpadł na pomysł, że sobie 100 przechowa w rejestrze adresowym. Pozbawił się przez to możliwości użycia MOVEQ, ale może uznał, że to się per saldo opłaci, wobec MOVEM.L (albo dwóch MOVE.L) na stos i ze stosu. Bo przestrzega zasady, że wszystko oprócz D0/D1/A0/A1 trzeba przechować. Nie wiem czemu dokręcenie optymalizacji skłania kompilator do używania operandów immediate zamiast rejestrów. Może za bardzo chce unikać zrzucania rejestrów na stos i przywracania ze stosu.

Ostatnia aktualizacja: 25.09.2024 22:24:20 przez Krashan
[#28] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@mateusz_s, post #1

--

Ostatnia aktualizacja: 25.09.2024 23:12:40 przez Rafael/ARMO
[#29] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Krashan, post #24

W ASM to mogloby byc np. tak:

; input D0 (word)
; output D1 (longword)
; D2 temp

 move.l #$FFFF00FF,D1 ; wartosci poczatkowe to $FF dla kazdej z 3 cyfr
 moveq #100,D2
B100
 addq.b #1,D1
 sub.w D2,D0
 bcc.b B100
 add.w D2,D0
 swap D1 ; pierwsza cyfra ustawiona
 moveq #10,D2
B10
 addq.b #1,D1
 sub.w D2,D0
 bcc.b B10
 add.w D2,D0
 lsl.w #8,D1 ; druga cyfra ustawiona
 move.b D0,D1 ; trzecia cyfra ustawiona
 rts


Mozesz sobie szybkosc porownac. Jesli to mialoby byc na 68000 to lsl.w #8 mozna wywalic, bo jest wolne.
[#30] Re: [C] Jak wydajnie zamienić liczbę na własny font bitmapowy?

@Don_Adan, post #29

Jednak chyba bardziej mi sie podoba wersja bez przesuniecia (lsl.w)

; input D0 (word)
; output D1 (longword)
; D2, A0 temp

 move.l #$FFFF00FF,D1 ; wartosci poczatkowe to $FF dla kazdej z 3 cyfr
 moveq #100,D2
B100
 addq.b #1,D1
 sub.w D2,D0
 bcc.b B100
 add.w D2,D0
 swap D1 ; pierwsza cyfra ustawiona
 moveq #10,D2
 lea $100.W,A0
B10
 add.w A0,D1 ; druga cyfra ustawiana
 sub.w D2,D0
 bcc.b B10
 add.w D2,D0
 move.b D0,D1 ; trzecia cyfra ustawiona
 rts
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem