Pomoc w optymalizacji kodu c + ASM


(Smutasek2) #1

Witam.

Szukam pomocy w optymalizacji niżej podanego kodu.

Ja od prawie 2 lat piszę programy wyłącznie przy pomocy AutoHotkey'a którego znam na wylot lecz kompletnie nie mam bladego pojęcia o programowaniu w C a tym bardziej ASM. Podany kod jest więc tutaj moją pierwszą próbą więc z pewnością coś będzie dało się tutaj przyspieszyć.

Funkcja ta ma za zadanie nakładanie przezroczystości grafiki, w zasadzie działa to tak jak GdiAlphaBlend(hdc, x, y, w, h, Ahdc, 0, 0, w,h,(alpha << 16)) jednak ma on pewną wadę, przy obliczaniu nowej przezroczystości zaokrągla on wartości do najbliższej liczby i w momencie zapętlenia funkcji GDI obraz nigdy nie stanie się w pełni przezroczysty (efekt zanikania).

Mój kod jest już tej wady pozbawiony lecz z pewnością można by go trochę przyspieszyć (w porównaniu do AHK już jest demonem gdyż to samo napisane natywnie w AHK przetwarza 1 obraz na sekundę 200x200 a ten kod robi ponad 30 obrazów na sekundę 700x700 i jeszcze mi zostaje sporo zapasu na CPU - AHK jest szybki i łatwy ale nie w tego typu operacjach ...)

 

Dla ułatwienia napiszę co konkretnie robi ta fukcja:

  • czyta wartość każdego kanału (AARRGGBB)

  • out= Wartość*Alpha/255

  • zapis do pamięci 'out' w to samo miejscę

 

Z góry dziękuję za wszelką pomoc.

typedef unsigned int UINT;
typedef int INT;
typedef unsigned char UINT8;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef unsigned __int64 QWORD;
ChangeData(pGraphic,Alpha,x,y,width,height)
{
BYTE d= 255;
BYTE v= Alpha; //increase performance while v is BYTE
DWORD i= 0 ;
DWORD offsett;
UINT gw;
UINT gh;
UINT bmp;
_asm
{
	//bmp		
		mov edi, pGraphic
		sub edi, 28
		xor eax, eax
		mov eax, [edi]
		mov bmp, eax
	//dw	
		mov edi, pGraphic
		add edi, 24
		xor eax, eax
		mov eax, [edi]
		mov gw, eax
	//dh	
		add edi, 4
		xor eax, eax
		mov eax, [edi]
		mov gh, eax
		
}

// var*subpixels
x = x*4;
gw = gw*4;
width = width*4;

// prevent overwrite non bmp data
if((width+x)>gw)
	width = gw-x;
if((height+y)>gh)
	height = gh-y;


for (i = 0; i<height; i++)
{
	offsett = (bmp-((i+y)*(gw))+x);
	_asm
	{
		mov edi, offsett
		mov ecx, width
	Width: // revalue subpixels in line loop
		xor eax, eax
		mov al, [edi] ;read value from memory [byte]

	// subp*alpha/255
		mul v ; ax = Alpha * al
		div d ; al = ax / 255

	// store in memory
		mov [edi], al
		inc edi
	loop Width
	}
}
return 1;

(kostek135) #2

Jak możesz to przyśpieszyć? Przestać bawić się w pisanie syf-kodu (to czy napiszesz asm wstawki, czy wszystko będzie przy użyciu czystego C, nie ma znaczenia na dzień dzisiejszy), a wykorzystać wiele procesorów/rdzeni a może nawet napisać program, który zostanie zrónoleglony na jakimś klastrze…


(dobryteddy) #3

Tu masz jeden przykład z użyciem instrukcji SSSE3/SSE4:  http://wm.ite.pl/articles/sse4-alphaover.html

 

Takich przykładów znajdziesz sporo w google z uzyciem różnych instrukcji procesora. Pojedyncze przetwarzanie piksela to nienajlepszy pomysł :slight_smile: Najlepiej jest zastosować instrukcje MMX/SSE(2,3,4).

Jeżeli byś operował bardzo dużymi obrazami to można też pokusić się o tzw. AMP: http://msdn.microsoft.com/en-us/library/hh265137.aspx

 

 

Zamiast tego (tzw. partial register stalls):

 

proponowałbym instrukcję: movzx eax, BYTE PTR [edi]

 

Zamiast tego (tzw. partial condition code updates):

 

proponowałbym instrukcję: add edi, 1


(Smutasek2) #4

ja zrobiłem ten “syf” wyłącznie dlatego, że jest to moja pierwsza funkcja napisania w języku innym niż AHK, dodatkowo część linijek w C jest dla przejrzysotści plus sam fakt, że ASM nie jest łatwy z początku więc napisałem wyłącznie najłatwiejszą cześć oraz najbardziej obciążającą CPU w ASM. Także celem tego tematu nie jest napisanie ultra wydajnej funkcji tylko nauczenie się czegoś nowego, w tym wypadku naprawa pospolitych błędów bądź odpowiednia optymalizacja kodu, z doścwiadczenia wiem, że czasem zmiana/dopisanie paru linijek znacząco potrafi poprawić wydajność.