10

Lever le voile sur la table de cycles du Cortex A8 : Part 2

jan

Lors de mon précédent article sur le fonctionnement du pipeline de NEON, je n’avais pas voulu entrer trop dans les détails. Pourtant, pour avoir une bonne compréhension du fonctionnement de NEON, et comment optimiser au maximum son utilisation il faut absolument comprendre ce que sont les “unités fonctionnelles” (functional unit).

Rem 26/04/2010 : Certaines des informations mentionnées dans ce post sont fausses. Les instructions NEON n’entrent pas dans les pipelines aux cycles indiqués (même si au final les temps sont correct). Depuis j’ai grandement amélioré ma compréhension de NEON. Un jour je ferai un post synthétisant le véritable fonctionnement de NEON mais pour le moment, le temps me manque !

Introduction

Rappelons nous de l’exemple que j’avais donné dans mon précédent post. On pouvait voir une instruction (vshll.u16) suivre une autre instruction (vmlsl) et pourtant se terminer avant celle-ci.

Mais que ce passe t-il pour le code suivant.

	vmull.u16			d2, d0, d1[0]
	vmull.u16			d3, d0, d2[0]
	vmull.u16			d1, d0, d3[0]

J’ai volontairement choisi les instructions les pires, avec l’utilisation du registres de destination en guise de source de l’instruction suivante.
De plus le court-cuircuit du vmul ne s’applique pas ici puisqu’il ne fonctionne que pour les multiplication avec accumulation.

Si on s’en tient aux explications (incomplètes) que j’avais précédemment donné, on serait tenter de penser que les déroulement des instruction dans le pipeline de NEON est le suivant.

			vmull.u16				vmull.u16				vmull.u16
Cycle 0	Execution				-							-
Cycle 1	Lecture Dm(d1[0])		Execution				-
Cycle 2	Lecture Dn(d0)			Lock !!!					Execution
Cycle 3	Calcul					Lock !!!					Lock !!!
Cycle 4	Calcul					Lock !!!					Lock !!!
Cycle 5	Calcul					Lock !!!					Lock !!!
Cycle 6	Ecriture Dd(d2)		Lecture Dm(d2[0])		Lock !!!
Cycle 7	-							Lecture Dn(d0)			Lock !!!
Cycle 8	-							Calcul					Lock !!!
Cycle 9	-							Calcul					Lock !!!
Cycle 10	-							Calcul					Lock !!!
Cycle 11	-							Ecriture Dd(d3)		Lecture Dm(d3[0])
Cycle 12	-							-					  		Lecture Dn(d0)
Cycle 13	-							-					  		Calcul
Cycle 14	-							-					  		Calcul
Cycle 15	-							-					  		Calcul
Cycle 16	-							-					  		Ecriture Dd(d0)

On voit bien que chaque nouvelle instruction prend de plus en plus de temps (nombre de cycle entre son entrée dans le pipeline et sa terminaison).
Si en plus on exécutait une boucle sur ce code on saturerai vite le pauvre NEON qui ne saurait plus quoi faire de toutes ces données.

En fait, Cela ne se passe pas tout à fait comme cela.

Les pipelines et les unités fonctionnelles

NEON dispose de deux pipelines. Cela veut dire qu’il peut initier deux instructions dans le même cycle. Par contre, NEON peut travailler simultanément sur bien plus d’instructions !

Les deux pipelines sont spécialisés. Il ne peuvent pas recevoir n’importe quelle instruction.
Une fois qu’une instruction est entrée dans le pipeline, elle est affectée à une unités fonctionnelle. Cette unité fonctionnelle est choisi en fonction du type de l’instruction.
L’unité fonctionnelle est composé d’étapes, et l’instruction va à chaque cycle passer à l’étape suivante à condition que l’étape suivante ne soit pas utilisée par une autre instruction.

Pour l’instant ça semble compliqué! Mais ça va s’éclaircir.

Les deux pipelines sont spécialisés et ne peuvent chacun traiter qu’une type particulier d’instruction.

  • Pipeline 0: Ce pipeline est réservé au opération d’accès mémoire (Load et Store) ainsi qu’aux permutations
  • Pipeline 1: Ce pipeline est réservé à toutes les autres opérations

Rem: Je précise que c’est moi qui ai donnée des numéros aux pipelines NEON. A priori ils n’en n’ont pas… d’autres sites pourraient les nommer autrement.

Lorsqu’une instruction entre dans l’un (ou l’autre) des pipelines de NEON, elle est affectée à une unité fonctionnelle précise “choisie” en fonction du type de l’instruction.
Dans NEON, il existe 6 unités fonctionnelles dédiées au pipeline 1 et 1 unité fonctionnelle dédiée au pipeline 0.

  • L’unité SIMD de multiplications d’entiers
  • L’unité SIMD de décalages de bits
  • L’unité SIMD de calculs arithmétiques d’entiers
  • L’unité SIMD de multiplications flottantes
  • L’unité SIMD de calculs arithmétiques flottant
  • L’unité de calculs flottants non vectoriel.
  • L’unité d’accès mémoire et de permutation.

Lorsque l’on parle de pipeline à plusieurs étages en fait on commet une erreur. Ce sont les unités fonctionnelles qui ont plusieurs étages. On voit ici que le plupart des unités fonctionnelles dispose de 6 étages et ce sont ces étages que l’on retrouve dans la table de cycles.

La règle du blocage du pipeline de NEON est la suivante:
Il ne peux y avoir qu’une seule instruction à un cycle donnée à un étage d’une unité fonctionnelle precise.

L’étude par l’exemple sera sans doute plus parlant. Reprenons l’exemple du début.

	vmull.u16			d2, d0, d1[0]
	vmull.u16			d3, d0, d2[0]
	vmull.u16			d1, d0, d3[0]
	vmull.u16			d2, d0, d1[0]
			i1:vmull.u16			i2:vmull.u16			i3:vmull.u16		i4:vmull.u16
Cycle 0	Decodage					-							-						-
Cycle 1	N1:DUP					Decodage					-						-
Cycle 2	N2:MUL1					Lock (N1)				Decodage				-
Cycle 3	N3:MUL2					Lock (N1)				Stall					-
Cycle 4	N4:ACC1					Lock (N1)				Stall					-
Cycle 5	N5:ACC2					Lock (N1)				Stall					-
Cycle 6	N6:WB						N1:DUP					Stall					-
Cycle 7	-							N2:MUL1					Lock (N1)			Decodage
Cycle 8	-							N3:MUL2					Lock (N1)			Stall
Cycle 9	-							N4:ACC1					Lock (N1)			Stall
Cycle 10	-							N5:ACC2					Lock (N1)			Stall
Cycle 11	-							N6:WB						N1:DUP				Stall
Cycle 12	-							-							N2:MUL1				Lock (N1)
Cycle 13	-							-							N3:MUL2				Lock (N1)
Cycle 14	-							-							N4:ACC1				Lock (N1)
Cycle 15	-							-							N5:ACC2				Lock (N1)
Cycle 16	-							-							N6:WB					N1:DUP

Voila ce qui se passe.
Au cycle 0 : i1 est décodée puis l’unité fonctionnelle de multiplication d’entier lui est affectée.
Au cycle 1 : i1 entre dans le premier étage de l’unité fonctionnelle alors que i2 est décodée.
Au cycle 2 : i1 passe au niveau N2:MUL1 de l’unité fonctionnelle. i2 peut alors entrer à son tour dans le premier étage de l’unité fonctionnelle. i3 est décodée.
Au cycle 3 : i1 passe au niveau N3:MUL2. i2 quant à elle reste bloquée au niveau N1 puisque ce niveau est celui où elle est sensée lire d2[0] (le résultat de i1). Du coup elle ne libère pas N1:DUP, et i3 ne peut pas entrer dans l’unité fonctionnelle.

Dès lors, plus aucune instruction n’entrera dans le pipeline NEON jusqu’au cycle 7 puisque i3 bloque l’entrée. On a alors des cycles perdus appelés Stall

J’ai indiqué par Lock les cycles d’attentes et par Stall les cycles qui bloquent l’entrée de nouvelles instructions dans le pipeline.
Finalement, peu importe que votre code contiennent des Lock. S’il n’y a aucun cycle perdu pour le décodage de nouvelles instructions, alors votre code est optimal (ou presque).
On cherchera donc généralement à minimiser les Stall, sachant que c’est généralement plus simple à faire si on a déjà minimisé les Lock.

Alors on peut à présent se poser la question combien de cycles prennent les instructions !!!
Généralement les développeurs assembleur expriment via cette expression combien de cycles sont consommés – dans le contexte de leur programme – par une instruction.

Si on s’en tient a la documentation le l’ARM, tous les instructions utilisées (ici) prennent 1 cycle, mais dans le langage courant on dira que l’instruction 3 prends 5 cycles (1 cycle pour son exécution réelle et 4 cycles de Stall). Cette notion de chiffrage, en terme de cycles, de la durée d’une instruction est pour le moins complexe. Je ne suis d’ailleurs pas sur qu’il existe réellement une norme à ce sujet.

En bref

Le calcul du nombre de cycles d’un programme (ou plus exactement des cycles auxquels les instructions peuvent s’exécuter) peut difficilement se calculer à la main.
Il nous faudrait un petit programme qui nous calcule tout cela du mieux possible. Je pense que je vais m’atteler à l’écriture de ce petit programme car c’est tout de même extrêmement utile pour optimiser au mieux un programme Cortex A8. Bon évidement avec le A9, cela n’a plus aucun sens (encore que) puisque le Cortex A9 est capable d’exécuter les instructions dans le désordre.
Cela étant dit, une optimisation en amont de l’ordre des instructions ne peut pas faire de mal !

 | Tags:

12 Responses to “Lever le voile sur la table de cycles du Cortex A8 : Part 2”

  1. Mark, I don’t agree with you, I think the post was helpful and not shallow at all.

  2. SEO Newport dit :

    Thank you for an excellent weblog post, where does one find all of your details?

  3. Etienne SOBOLE dit :

    Yes.
    You’re right. There is many mistake in this post.

    Since this post I’ve made a lot of test, and that’s true that instructions do not process exactly as I said.
    I have no time to update the post.

    I should mention in all my post that this is how I suppose it’s work when I write the post.

  4. Personalized Napkins dit :

    Amazing Submit, Thank you

  5. Awesome Submit, Thanks a lot

  6. nursery art dit :

    I am impressed with this web site , real I am a big fan .

  7. Blogging keeps me insane. Keep up all the positive work. I too love to blog. I found this one to be very informative.

  8. Cannon dit :

    Vous avez de bons points il, c’est pourquoi j’aime toujours verifier votre blog, Il semble que vous etes un expert dans ce domaine. maintenir le bon travail, Mon ami recommander votre site.

    Mon francais n’est pas tres bon, je suis de l’Allemagne.

  9. Etienne SOBOLE dit :

    Salut Cannon.
    J’ai une bonne dizaine de commentaires chaque jour dont le seul but est de mettre un lien vers leur site pour faire grimper leur classement google, mais c’est bien le premier à faire l’effort de le mettre en français…

    Es-tu un robot qui spam automatiquement ou une humaine ?

    Bon je vire quand même ton lien !!! Parce que ça n’a pas grand chose à voir :)

    PS: si quelqu’un sait comment limiter ce genre de commentaires via un module wordpress, je suis preneur. Genre un sorte de black liste ça serait pas mal. Si ça n’existe pas je vais peut être le développer moi même !!!

  10. 0xFF dit :

    Bon article !
    Qu’en est-il du Cortex A5 par rapport a A8 et A9 ? J’ai entendu dire qu’il etait encore plus efficace ?

  11. Etienne SOBOLE dit :

    Ouaip. Je connais pas tres bien le A15, mais le HTC One S est équipé du S4 MSM8260A qui lui est en fait un des premier processeurs basé sur le A15 ! Et en effet il semble que ça dépote un max !
    Enfin peut être pas la partie NEON !!!

  12. 0xFF dit :

    Je parle bien du A5 et pas du A15 (c’etait pas une faute de frappe haha!)

    Le A5 est suppose etre le moins cher des Cortex A (remplacant le ARM9), il supporte le multicore et NEON est en option. Un exemple est le MSM7227A (le A est important car sinon c’est un ARM11), ou le TCC8925 (un CPU coreen).

Répondre

Human control : 6 + 3 =