Dziwne wyniki testów SIMD

0

Ostatnio postanowiłem przyśpieszyć obliczenia w swoich programach za pomocą SIMD intela, postanowiłem najpierw sprawdzić jakie korzyści takie działanie przyniesie a wiec napisałem program testujący :

#include <iostream>
#include <ctime>

__declspec(align(16)) struct Vectorf{
	float x,y,z,tmp;
};

using namespace std;

inline void add2(Vectorf *a, Vectorf *b){
	a->x+=b->x;
	a->y+=b->y;
	a->z+=b->z;	
	a->tmp+=b->tmp;
}

inline void add(Vectorf *a, Vectorf *b, Vectorf *c){
	c->x=a->x+b->x;
	c->y=a->y+b->y;
	c->z=a->z+b->z;	
	c->tmp=a->tmp+b->tmp;
}

inline void add_fast(Vectorf *a, Vectorf *b, Vectorf *c){
	__asm{
		mov esi, a
		mov ebx, b
		mov edi, c

		movaps xmm0, [esi]
		addps xmm0, [ebx]
		movaps [edi], xmm0

/*  wersja gdy nie ma wyrownania do 16 
		movups xmm0, [esi]
		movups xmm1, [ebx]
		addps xmm0, xmm1
		movups [edi], xmm0*/
	}
}

#define n 50000000

int main(){
	Vectorf a,b,c;
	clock_t start,koniec;

	cout<<sizeof(Vector)<<endl;

	start = clock();
	for(long i=0; i<n; i++)	{
		a.x=3.3f; a.y=-2.6f; a.z=1.2f; a.tmp=0.0f;
		b.x=-2.8f; b.y=5.0f; b.z=-4.3f; b.tmp=0.0f; 
		add2(&a,&b);
	}
	koniec = clock();
	cout<<"Czas : "<<(float)(koniec-start)/CLOCKS_PER_SEC<<endl;
	cout<<"add per sec: "<<(float)n/((float)(koniec-start)/CLOCKS_PER_SEC)<<endl;

	start = clock();
	for(long i=0; i<n; i++){
		a.x=3.3f; a.y=-2.6f; a.z=1.2f;a.tmp=0.0f;
		b.x=-2.8f; b.y=5.0f; b.z=-4.3f;b.tmp=0.0f;
		add(&a,&b,&c);
	}
	koniec = clock();
	cout<<"Czas : "<<(float)(koniec-start)/CLOCKS_PER_SEC<<endl;
	cout<<"add per sec: "<<(float)n/((float)(koniec-start)/CLOCKS_PER_SEC)<<endl;

	start = clock();
	for(long i=0; i<n; i++){
		a.x=3.3f; a.y=-2.6f; a.z=1.2f; a.tmp=0.0f;
		b.x=-2.8f; b.y=5.0f; b.z=-4.3f; b.tmp=0.0f;
		add_fast(&a,&b,&c);		
	}

	koniec = clock();
	cout<<"Czas : "<<(float)(koniec-start)/CLOCKS_PER_SEC<<endl;
	cout<<"add per sec: "<<(float)n/((float)(koniec-start)/CLOCKS_PER_SEC)<<endl;
	cout<<"float'y  : "<<2.5f * 3.4f << endl;
	cout<<"int'y : "<<2*5<<endl;

	a.x=3.3f; a.y=-2.6f; a.z=1.2f;
	b.x=-2.8f; b.y=5.0f; b.z=-4.3f;
	add_fast(&a, &b, &c);
	cout<<c.x<<" "<<c.y<<" "<<c.z<<endl;

	system("pause");
	return 0;
}

Wyniki dla wersji Debbug są takie jak można się było spodziewać :
czas 1 : 2.719 // a+=b
czas 2 : 2.797 // c=a+b
czas 3 : 2.297 // SIMD c=a+b

Jeśli jednak przejdę na release wyniki są takie :
czas 1 : 0 // a+=b
czas 2 : 0 // c=a+b
czas 3 : 0.906 // SIMD C=a+b

Czemu tak się dzieje? SIMD chyba powinno być szybsze w obu przypadkach. Nie wiem czy ja coś źle robię, czy kompilator coś miesza w ramach optymalizacji?

0

Kompilator zobaczył pewnie, że w każdej iteracji ustawiasz wektory, modyfikujesz... i nie używasz ich. W efekcie zredukował pętlę, i zamiast 50000000 iteracji, masz tylko jedną. Albo i wcale. W ciele pętli efektów ubocznych nie ma - co sobie będzie żałował.

Dla SIMD tego nie zrobił, bo dałeś własną wstawkę asemblerową, i kompilator nie zna twoich intencji na tyle, żeby pętlę wywalić całkiem.

Tak ja to widzę.

0

Tak myślałem, tylko nie bardzo wiem jak w takim razie zrobić dobry test ;-) albo inaczej.. czy w takim razie z wyników testu bez optymalizacji wynika że warto stosować SIMD, czy raczej lepiej korzystać z FPU i liczyć na optymalizacje?

Dobra wymyśliłem nieco inny test :

a.x=0.0f; a.y=0.0f; a.z=0.0f; a.tmp=0.0f;
b.x=-0.1; b.y=0.2; b.z=-0.2; b.tmp=0.1f;
start = clock();
for(long i=0; i<n; i++)	{		
	add2(&a,&b); 
}
koniec = clock();
cout<<a.x<<" "<<a.y<<" "<<a.z<<" "<<a.tmp<<endl;
cout<<"Czas : "<<(float)(koniec-start)/CLOCKS_PER_SEC<<endl;
cout<<"add per sec: "<<(float)n/((float)(koniec-start)/CLOCKS_PER_SEC)<<endl;

Teraz wyniki są raczej dobre :
czas 1 : 0.266 // a+=b
czas 2 : 0.265 // a = a+b
czas 3 : 0.250 // SIMD a=a+b

Nie są to rewelacyjne wyniki ale widać że SIMD się opłaca, tzn dla upakowanych danych co 16bajtów, bo kiedy korzystałem z alternatywnej wersji SIMD gdzie dane leżą w pamięci jak chcą wyniki były dużo gorsze niż FPU. Dalej ciężko powiedzieć czy takie programownanie sie opłaca

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