IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Visual C++ Discussion :

Visual C++ SIMD Performance


Sujet :

Visual C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 60
    Par défaut Visual C++ SIMD Performance
    Salut ami(e)s dev...
    J'utilise actuellement les intrinsics SSE msvc pour augmenter les performances de 2 de mes fonctions NURBS...
    La 1ère calcule 4 basis functions en une fois
    La 2ème calcule 4 points d'une surface en une fois.
    (Les nurbs ne sont qu'un pretexte....)
    Le problème est que le programme prend le meme temps qu'il soit en mode
    SIMD ou pas...Du coup je me pose des questions sur l'optimisation SIMD de
    msvc...Mais avant peut-etre que je peux mieux écrire mon code...
    Alors pour ceux qui s'y connaissent en optimisation SIMD quelles sont les conventions à adopter pour avoir atteindre la performance voulue....??
    Merci

  2. #2
    screetch
    Invité(e)
    Par défaut
    a l'aveugle (pas de code...)
    les instructions SIMD marchent bien surtout quand on les fait a la suite.
    je ne sais pas ce que tu essayes de faire, mais bon... une addition par exemple, ca coute presque rien. en faire 4 d'un coup, ca coute toujours presque rien.

    Ce qui coûte a l'execution C++, personne ne le sait. Il faut utiliser un profiler pour voir ce que c'est (exemple: AMD COde Analyst, VTune, etc etc) voire même simuler un processeur et voir comment ca s'execute.

    Par exemple, il se peut que le vrai probléme soit que ta fonction n'est pas inlinée, un appel de fonction coutera beaucoup plus cher que tes additions ou multiplications.
    Un autre problème pourrait être que tes valeurs sont très espacées en mémoire, le temps de les charger dans un registre serait très long. A coté de ca, encore une fois, une addition ou une multiplication c'est rien.

    Quand tes données seront correctement préparées, proches les unes des autres, que tu auras beaucoup de traitements a la suite sur ces données, alors le SSE commencera a etre interessant. Sans en savoir plus sur ton problème, je pense juste que ton optimisation est trop petite pour être visible.

    Les intrinsics SSE de visual studio sont pratiquement des equivalents d'instruction assembleur, c'est difficile d'être plus performant que ca.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 60
    Par défaut
    Merci screetch pour ta réponse...Je te donne les deux fonctions
    La 1ère.....
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    void basisDerFuncCustom(GLint degree, GLint n, GLint *index, GLfloat *uv, std::vector<GLfloat> &my_knots, std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > &res){
     
    	//std::cout<<"Entree der custom "<<std::endl;
    	GLint taille = degree+1;
    	std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > left(degree+1);
    	std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > right(degree+1);
    	std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > ndu(taille*taille);
    	std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > ar(2*taille);
    	//res.resize((n+1)*taille);
     
    	ndu[0] = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f);
    	//std::cout<<"ALLoc complete"<<std::endl;
     
    	for(GLint j=1; j<=degree; j++){
     
    		left[j]= _mm_set_ps( (uv[3] - my_knots[index[3]+1-j]), (uv[2] - my_knots[index[2]+1-j]), (uv[1] - my_knots[index[1]+1-j]), (uv[0] - my_knots[index[0]+1-j]));
    		right[j]= _mm_set_ps( (my_knots[index[3]+j] - uv[3]), (my_knots[index[2]+j] - uv[2]), (my_knots[index[1]+j] - uv[1]), (my_knots[index[0]+j] - uv[0]));
    		sfloat4 saved = _mm_setzero_ps(); 
    		sfloat4 temp = _mm_setzero_ps();
     
    		for(GLint r=0; r<j; r++){
    			ndu[(j*taille) + r] = _mm_add_ps( right[r+1], left[j-r] );
    			temp = _mm_div_ps( ndu[(r*taille)+(j-1)], ndu[(j*taille)+r] );
    			ndu[(r*taille)+j] = _mm_add_ps(saved, _mm_mul_ps(right[r+1],temp));
    			saved = _mm_mul_ps(left[j-r], temp);
    		}
    		ndu[(j*taille)+j] = saved;
    	}
     
    	for(GLint j=0; j<=degree; j++)
    		res[j] = ndu[(j*taille)+degree] ;
     
    	for(GLint r=0; r<=degree; r++){
     
    		GLint s1=0; GLint s2=1;
    		ar[0]= _mm_set_ps1(1.0f);
     
    		for(GLint k=1; k<=n; k++){
     
    			sfloat4 d = _mm_set_ps1(0.0f);
    			GLint rk = r-k;  GLint pk = degree-k; GLint j1; GLint j2;
     
    			if(r>=k){
    				ar[s2*taille] = _mm_div_ps(ar[s1*taille], ndu[((pk+1)*taille)+rk]);
    				d = _mm_mul_ps(ar[(s2*taille)], ndu[(rk*taille)+pk]);
    			}
     
    			if(rk>= -1) j1=1;
    			else       j1= -rk;
     
    			if(r-1<=pk) j2=k-1;
    			else        j2=degree-r;
     
    			for(GLint j=j1; j<=j2; j++){
    				ar[(s2*taille)+j] = _mm_div_ps(_mm_sub_ps(ar[(s1*taille)+j], ar[(s1*taille)+j-1]), ndu[((pk+1)*taille)+rk+j]);
    				d = _mm_add_ps(d, _mm_mul_ps(ar[(s2*taille)+j], ndu[((rk+j)*taille)+pk]));
    			}
     
    			if(r <= pk){
    				ar[(s2*taille)+k] = _mm_div_ps( _mm_sub_ps(_mm_setzero_ps(), ar[(s1*taille)+k-1] ), ndu[((pk+1)*taille)+r] );
    				d = _mm_add_ps(d, _mm_mul_ps(ar[(s2*taille)+k], ndu[(r*taille)+pk]));
    			}
    			res[(k*taille)+r] = d;
    			GLint j=s1; s1=s2; s2=j;
    		}
    	}
    	GLint r=degree;
     
    	for(GLint k=1; k<=n; k++){
    		for(GLint j=0; j<=degree; j++)
    			res[(k*taille)+j] = _mm_mul_ps (res[(k*taille)+j], _mm_set_ps((GLfloat)r,(GLfloat)r,(GLfloat)r,(GLfloat)r) );
     
    		r *= (degree-k);
    	}
    }
    La 2ème....plus longue.....
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    void evalDerSurfacePointCustom(GLint d, GLint *IndicesU, GLint *IndicesV, GLfloat *u, GLfloat *v, sfloat4 *, std::vector<glm::detail::tvec3<T> > &Res){
     
    	assert(d <= 1);
    	GLint tailleu = degreeU+1;
    	GLint taillev = degreeV+1;
    	GLint np = knotVectorU.size()-2-degreeU;
    	GLint nq = knotVectorV.size()-2-degreeV;
    	std::vector<std::vector<T> > Bin(d+1,std::vector<T>(d+1,1.0f));
    	std::vector<std::vector<std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > > > SKL(d+1,std::vector<std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > >(d+1,std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> >(3)));
     
    	std::vector<std::vector<std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > > > Aders(d+1,std::vector<std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > >(d+1,std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> >(4)));
     
    	{//Compute Aders and Wders
    		std::vector<std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > > temp(taillev,std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> >(4));
    		std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > Nu((d+1)*tailleu);
    		std::vector<sfloat4, Eigen::aligned_allocator<sfloat4> > Nv((d+1)*taillev);
    		basisDerFuncCustom(degreeU, d, IndicesU, u, knotVectorU, Nu);
    		basisDerFuncCustom(degreeV, d, IndicesV, v, knotVectorV, Nv);
    		GLint du = std::min(d,degreeU);
    		GLint dv = std::min(d,degreeV);
     
    		for(GLint k=0; k<=du; k++){
    			for(GLint s=0; s<=degreeV; s++){
    				for(GLint r=0; r<=degreeU; r++){
    					sfloat4 simd_x = _mm_set_ps(ctrlPointVector[(IndicesU[3]-degreeU+r)*(nq+1)+(IndicesV[3]-degreeV+s)][0], ctrlPointVector[(IndicesU[2]-degreeU+r)*(nq+1)+(IndicesV[2]-degreeV+s)][0], ctrlPointVector[(IndicesU[1]-degreeU+r)*(nq+1)+(IndicesV[1]-degreeV+s)][0], ctrlPointVector[(IndicesU[0]-degreeU+r)*(nq+1)+(IndicesV[0]-degreeV+s)][0]);
    					sfloat4 simd_y = _mm_set_ps(ctrlPointVector[(IndicesU[3]-degreeU+r)*(nq+1)+(IndicesV[3]-degreeV+s)][1], ctrlPointVector[(IndicesU[2]-degreeU+r)*(nq+1)+(IndicesV[2]-degreeV+s)][1], ctrlPointVector[(IndicesU[1]-degreeU+r)*(nq+1)+(IndicesV[1]-degreeV+s)][1], ctrlPointVector[(IndicesU[0]-degreeU+r)*(nq+1)+(IndicesV[0]-degreeV+s)][1]);
    					sfloat4 simd_z = _mm_set_ps(ctrlPointVector[(IndicesU[3]-degreeU+r)*(nq+1)+(IndicesV[3]-degreeV+s)][2], ctrlPointVector[(IndicesU[2]-degreeU+r)*(nq+1)+(IndicesV[2]-degreeV+s)][2], ctrlPointVector[(IndicesU[1]-degreeU+r)*(nq+1)+(IndicesV[1]-degreeV+s)][2], ctrlPointVector[(IndicesU[0]-degreeU+r)*(nq+1)+(IndicesV[0]-degreeV+s)][2]);
    					sfloat4 simd_w = _mm_set_ps(ctrlPointVector[(IndicesU[3]-degreeU+r)*(nq+1)+(IndicesV[3]-degreeV+s)][3], ctrlPointVector[(IndicesU[2]-degreeU+r)*(nq+1)+(IndicesV[2]-degreeV+s)][3], ctrlPointVector[(IndicesU[1]-degreeU+r)*(nq+1)+(IndicesV[1]-degreeV+s)][3], ctrlPointVector[(IndicesU[0]-degreeU+r)*(nq+1)+(IndicesV[0]-degreeV+s)][3]);
    					temp[s][0] = _mm_add_ps(temp[s][0], _mm_mul_ps(Nu[(k*tailleu)+r], simd_x));
    					temp[s][1] = _mm_add_ps(temp[s][1], _mm_mul_ps(Nu[(k*tailleu)+r], simd_y));
    					temp[s][2] = _mm_add_ps(temp[s][2], _mm_mul_ps(Nu[(k*tailleu)+r], simd_z));
    					temp[s][3] = _mm_add_ps(temp[s][3], _mm_mul_ps(Nu[(k*tailleu)+r], simd_w));
    				}
    			}
    			GLint dd = std::min(d-k,dv);
    			for(GLint l=0; l<=dd; l++){
    				for(GLint s=0; s<=degreeV; s++){
    					Aders[k][l][0] = _mm_add_ps(Aders[k][l][0], _mm_mul_ps(Nv[(l*taillev)+s], temp[s][0]));
    					Aders[k][l][1] = _mm_add_ps(Aders[k][l][1], _mm_mul_ps(Nv[(l*taillev)+s], temp[s][1]));
    					Aders[k][l][2] = _mm_add_ps(Aders[k][l][2], _mm_mul_ps(Nv[(l*taillev)+s], temp[s][2]));
    					Aders[k][l][3] = _mm_add_ps(Aders[k][l][3], _mm_mul_ps(Nv[(l*taillev)+s], temp[s][3]));
    				}
    			}
    		}
    	}
    	//Compute nurbs derivates	
    	for(GLint k=0; k<=d; k++){
    		for(GLint l=0; l<=d-k; l++){
    			std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > v(3);
    			v[0] = Aders[k][l][0];
    			v[1] = Aders[k][l][1];
    			v[2] = Aders[k][l][2];
    			for(GLint j=1; j<=l; j++){
    				v[0] = _mm_sub_ps(v[0], _mm_mul_ps(_mm_set1_ps(Bin[l][j]), _mm_mul_ps(Aders[0][j][3], SKL[k][l-j][0])));
    				v[1] = _mm_sub_ps(v[1], _mm_mul_ps(_mm_set1_ps(Bin[l][j]), _mm_mul_ps(Aders[0][j][3], SKL[k][l-j][1])));
    				v[2] = _mm_sub_ps(v[2], _mm_mul_ps(_mm_set1_ps(Bin[l][j]), _mm_mul_ps(Aders[0][j][3], SKL[k][l-j][2])));
    			}
    			for(GLint i=1; i<=k; i++){
    				v[0] = _mm_sub_ps(v[0], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), _mm_mul_ps(Aders[i][0][3], SKL[k-i][l][0])));
    				v[1] = _mm_sub_ps(v[1], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), _mm_mul_ps(Aders[i][0][3], SKL[k-i][l][1])));
    				v[2] = _mm_sub_ps(v[2], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), _mm_mul_ps(Aders[i][0][3], SKL[k-i][l][2])));
     
    				std::vector<sfloat4,Eigen::aligned_allocator<sfloat4> > v2(3);
    				for(GLint j=1; j<=l; j++){
    					v2[0] = _mm_add_ps(v2[0], _mm_mul_ps(_mm_set1_ps(Bin[l][j]), _mm_mul_ps(Aders[i][j][3], SKL[k-i][l-j][0])));
    					v2[1] = _mm_add_ps(v2[1], _mm_mul_ps(_mm_set1_ps(Bin[l][j]), _mm_mul_ps(Aders[i][j][3], SKL[k-i][l-j][1])));
    					v2[2] = _mm_add_ps(v2[2], _mm_mul_ps(_mm_set1_ps(Bin[l][j]), _mm_mul_ps(Aders[i][j][3], SKL[k-i][l-j][2])));
    				}
    				v[0] = _mm_sub_ps(v[0], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), v2[0]));
    				v[1] = _mm_sub_ps(v[1], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), v2[1]));
    				v[2] = _mm_sub_ps(v[2], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), v2[2]));
    			}
    			SKL[k][l][0] = _mm_div_ps(v[0], Aders[0][0][3]);
    			SKL[k][l][1] = _mm_div_ps(v[1], Aders[0][0][3]);
    			SKL[k][l][2] = _mm_div_ps(v[2], Aders[0][0][3]);
    		}
    	}
     
    	for(GLint i=0; i<=d; i++){
    		for(GLint j=0; j<=d; j++){
    			Res[(i*(d+1)+j)*4] =   (glm::detail::tvec3<T>(SKL[i][j][0].m128_f32[0], SKL[i][j][1].m128_f32[0], SKL[i][j][2].m128_f32[0]));
    			Res[(i*(d+1)+j)*4+1] = (glm::detail::tvec3<T>(SKL[i][j][0].m128_f32[1], SKL[i][j][1].m128_f32[1], SKL[i][j][2].m128_f32[1]));
    			Res[(i*(d+1)+j)*4+2] = (glm::detail::tvec3<T>(SKL[i][j][0].m128_f32[2], SKL[i][j][1].m128_f32[2], SKL[i][j][2].m128_f32[2]));
    			Res[(i*(d+1)+j)*4+3] = (glm::detail::tvec3<T>(SKL[i][j][0].m128_f32[3], SKL[i][j][1].m128_f32[3], SKL[i][j][2].m128_f32[3]));
    		}
    	}
    }
    Je précise que les mm_set_ps utilisés étaient obligatoires du fait que les données n'étaient pas encore préparées...voilà merci

  4. #4
    screetch
    Invité(e)
    Par défaut
    je n'ai pas été jusqu'au bout, je prends juste un exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    for(GLint i=1; i<=k; i++){
    v[0] = _mm_sub_ps(v[0], _mm_mul_ps(_mm_set1_ps(Bin[k][i]), _mm_mul_ps(Aders[i][0][3], SKL[k-i][l][0])));
    cette ligne va:
    * lire ce qui se trouve dans Bin[k][i] (probablement rapide)
    * lire ce qu'il y a dans Aders[i][0][3]: c'est un cache miss a coup sûr car Aders[i]... va sauter une ligne entière de tableau a chaque coup
    * lire SKL[k-i][l][0], cache miss

    la tu viens de perdre 40 cycles dans les cache miss a chaque tour de boucle. Apres ca, la soustraction et les multiplications sont presque instantanées

    Une règle générale, c'est de faire varier l'index le plus a gauche le moins souvent possible, car la tu ne fais que des cache miss a chaque tour de boucle.

    Mais si tu veux vraiment savoir, la seule solution est d'utiliser un outil de profiling.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 60
    Par défaut
    Merci screetch...
    J'ai pu doubler les perf en transformant tous les vectors tri ou bi-dimensionnelle
    en uni-dimensionnelle...
    Une règle générale, c'est de faire varier l'index le plus a gauche le moins souvent possible, car la tu ne fais que des cache miss a chaque tour de boucle.
    Moi je dirai tout simplement évitez les vectors de vector.......
    std::vector<std::vector<int> > par ex est diférent de int tab[][] niveau perf...
    Les élts du 1er ne sont pas contigus en mémoire sachant que ceux du 2ème le sont bien....
    Voilà s'il y en a qui ont des petites astuces pour accélérer les SIMD je suis partant...

  6. #6
    screetch
    Invité(e)
    Par défaut
    ca risque d'etre toujours des problèmes d'accès mémoire (ca depend de la taille de tes données)
    tu as utilisé un profiler?

Discussions similaires

  1. Réponses: 31
    Dernier message: 22/04/2014, 14h55
  2. Performance d'accées à la BD sous visual C++
    Par bouzaidi dans le forum C++
    Réponses: 1
    Dernier message: 23/04/2008, 15h10
  3. Réponses: 2
    Dernier message: 01/08/2006, 10h20
  4. les performances du C# et du visual basic
    Par 180degrés dans le forum DirectX
    Réponses: 17
    Dernier message: 01/09/2005, 04h51
  5. Visual C++ 6/7 - performance
    Par Manu35 dans le forum MFC
    Réponses: 9
    Dernier message: 19/01/2004, 17h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo