[ANSI C] Proszę o pomoc w optymalizacji funkcji.

0

Witam. Piszę funkcję która miksuje/zmywa ze sobą dwa kolory w formacie RGB565 (16bit).

Konkretnie chcę jej używać do rysowania obrazków z kanałem alpha oraz do generowania gradientów. Docelowa platforma to Rockchip 260x (odtwarzacz MP4). Rdzeń pracuje z częstotliwością 72Mhz.

Napisałem ową funkcje ale ona jest zbyt wolna - obrazek o rozdzielczości 320x50 przetwarza przez 600ms co jest nie do przyjęcia (przetworzenie całego ekranu 320x230 zajęło by prawie 3sek).

Oto co naskrobałem:

typedef unsigned char byte;
typedef unsigned int  TColor;

typedef	struct{
	byte	R;
	byte	G;
	byte	B;
}TRGB;


TRGB RGB(byte R, byte G, byte B){
	TRGB 	temp;
	temp.R = R;
	temp.G = G;
	temp.B = B;
	return temp;
}

unsigned int RGB16bpp(TRGB RGB){
	byte	r16, g16, b16;
	
	r16 = (double)RGB.R*(double)31/(double)255;
	g16 = (double)RGB.G*(double)63/(double)255;
	b16 = (double)RGB.B*(double)31/(double)255;
	
	return (unsigned int)((r16 << 11) | (g16 << 5) | b16);
}

byte GetR16Value(unsigned int Color){
	byte	r16;
		
  	r16 = 0x001F & (Color >> 11);
  	return ((double)r16*(double)255/(double)31);
}

byte GetG16Value(unsigned int Color){
	byte	g16;
	
	g16 = 0x003F & (Color >> 5);
        return ((double)g16*(double)255/(double)63);
}

byte GetB16Value(unsigned int Color){
	byte	b16;
		
  	b16 = 0x001F & Color;
  	return ((double)b16*(double)255/(double)31);
}

TRGB GetRGB16bpp(unsigned int Color){
	TRGB	temp;
	temp.R = GetR16Value(Color);	
	temp.G = GetG16Value(Color);	
	temp.B = GetB16Value(Color);	
	return temp;
}

unsigned int MixColors(unsigned int FirstColour, unsigned int SecondColour, int Transition, int Scale){
	TRGB	TempColor, First, Second;
	
	First = GetRGB16bpp(FirstColour);		
	Second = GetRGB16bpp(SecondColour);

	
	TempColor.R = First.R + (((long)Second.R - (long)First.R ) * Transition / Scale);
	TempColor.G = First.G + (((long)Second.G - (long)First.G ) * Transition / Scale);
	TempColor.B = First.B + (((long)Second.B - (long)First.B ) * Transition / Scale);
	return RGB16bpp(TempColor);

Czy pomógłby mi ktoś zoptymalizować te funkcje? Pierwszą rzeczą którą jak mniemam trzeba będzie wywalić to zamiana wartości RGB do zakresy 0-255 (standardowo w RGB565 są to zakresy 0-63, 0-31, 0-63). Ale to tylko przypuszczenia, proszę o rady.

EDIT:
Muszę nadmienić że kompilator jest prymitywny, nie rozróżnia float od double i trzeba ciągle kombinować żeby uniknąć dziwnych zachowań np tutaj (musiałem dodać (long) chociaż nie wiem czemu:

TempColor.B = First.B + (((long)Second.B - (long)First.B ) * Transition / Scale);
0

Przede wszystkim pozbądź się tych rzutowań na double - konwersja byte/int <-> double jest dość powolna. No i za dużo tu wywołań funkcji na piksel.

0

Właściwie, to jeżeli zależy na prędkości, nie powinno być żadnych wywołań funkcji (chyba że kompilator obsługuje funkcje inline).
Taki kod rozbity na funkcje fajny jest, ale nie tutaj.
Jak rozumiem MixColors odpalasz dla każdego piksela w pętli. Tak też nie powinno być - umieść cały kod konwersji w tej pętli, tak żeby nie było w niej żadnego wywołania - tylko same obliczenia.

0

Hmm dzięki za rady, pozbędę się konwersji na double oraz spróbuje użyć funkcji inline (obsługuje je kompilator).

Tylko że te funkcje inline zawsze były dla mnie ciężkie do ogarnięcia. Czy mógłby ktoś mi podpowiedzieć jakby np wyglądała funkcja GetR16Value w wersji inline? Bo zdaje się to będzie <ort>najprostrze </ort>do zrealizowania w inline.

0

funkcja inline nie rozni sie skladnia od fukncji nie-inline. Inline znaczy ze kod fukcji bedzie wstawiony w miejscu jej wywolania.
Wyjasnienie:
Tak wyglada normalna:

int dodaj(int a, int b)
{
return a+b;
}
int main()
{
int aa,bb;
cin >> aa >> bb;
cout << dodaj(aa,bb);
}

Tak samo bedzie wygladac funkcja inline, tylko kompilator potrakuje ten kod tak(no podobnie):


int main()
{
int aa,bb;
cin >> aa >> bb;
cout << aa+bb;
}

Przypisze wartosci aa do dawnego a , bb do dawnego b , i wstawi kod poprzedniej funkcji w miejsce wywolania. Wywolanie fukncji zajmuje wiecej czasu niz wywolanie samego kodu.

0
flasher86 napisał(a)

Czy mógłby ktoś mi podpowiedzieć jakby np wyglądała funkcja GetR16Value w wersji inline?

Tak samo, ze słowem inline przed typem:

inline int GetR16Value()
{
}

łyknie to GCC (byle w jakiejś nowej wersji), ale MSVC niestety nie.

0

W ANSI C nie ma inline, prawda?

0

Najlepiej to po prostu wszystko zmieścić w jednej funkcji. Wyglądałoby to mniej-więcej tak:

unsigned int MixColors(unsigned int FirstColour, unsigned int SecondColour, int Transition, int Scale)
{
    unsigned int r=FirstColour>>11;
    unsigned int g=(FirstColour&0x7E0)>>6;
    unsigned int b=FirstColour&0x1F;
    return  ((r + ((SecondColour>>11)-r)*Transition/Scale)|0x1F<<11)       /* R */
          | ((g + (((SecondColour&0x7E0)>>6)-g)*Transition/Scale)|0x3F<<6) /* G */
	  | ((b + (SecondColour&0x1F)*Transition/Scale)|0x1F);             /* B */;
}

Nie gwarantuję że ten kod działa poprawnie, ale sprawdź na ile poprawia się prędkość.

Świętowit napisał(a)

W ANSI C nie ma inline, prawda?

Jest w C99, dlatego naciskałem na nową wersję gcc ;-)

0

dla unsigned int c
zamiast c255.0/63.0 wolelibyśmy napisać (255c)/63,
ale mniejszy błąd popełnimy pisząc (c*1040)>>8, a i działać będzie znacznie szybciej
działa gdy int ma nie mniej niż 16 bitów, a c<64

0

Wywal te double w pierwszej kolejności. Te proste procesory używane w odtwarzaczach, telefonach itp. nie mają sprzętowego wykonywania operacji na liczbach zmiennoprzecinkowych, a jedynie emulację programową. Zmiana na inty to jakieś 10-100x przyspieszenie.

Inne poprawki wprowadzaj dopiero, gdy zmiana na inty okaże się niewystarczająca.

0

Dzięki za odpowiedzi. Jeszcze jedno pytanie. Czy jeśli deklaruje dodatkowe zmienne tak by kod był bardziej przejrzysty to czy to spowalnia wykonywanie całości?

Co do kompilatora, to jest to zmodyfikowana wersja SDCC, dziurawa jak sito ale to jedyna możliwość uruchomienia czegokolwiek na tym sprzęcie. Jakiś czas temu wyciekło SDK dla rodziny odtwarzaczy opartych o Rockchip i muszę zadowolić się tym co jest.

Co do używania double, potrzebne było głównie do konwersji wartości RGB do pełnego zakresu 0-255. Ale na zdrowy rozum jest to zupełnie zbędne skoro wejściowy i wyjściowy kolor będą tak samo w RGB565.

Jak by nie było porobię testy wydajności i dam znać ;-)

0

Wielkie dzięki za pomoc chłopaki! Po wszystkich zabiegach które mi poleciliście zszedłem do 275ms przy przetworzeniu całego ekranu!! to 10x szybciej niż przy użyciu moich wynalazków. Całość obróbki zawarłem w pętlach i pozbyłem się konwersji do double a także do zakresu 0-255. Miodzio [browar]

1 użytkowników online, w tym zalogowanych: 0, gości: 1