52 | | On considère une nouvelle instruction de branchement conditionnel : l'instruction `BEQPI` (Branch if equal and post increment). |
53 | | {{{ |
54 | | Beqpi rs, rt, Label |
55 | | }}} |
56 | | |
57 | | Il s'agit d'une instruction de format I. Cette instruction réalise l'opération suivante. Comme pour BEQ, le contenu de RS est comparé au contenu de RT. En cas d'égalité, on se branche à l'instruction portant l'étiquette Label, sinon le branchement échoue et on continue en séquence. De plus, une fois le test effectué, le contenu de RT est incrémenté. |
58 | | |
59 | | Le schéma d'exécution du pipeline du Mips convient-il à l'exécution de cette instruction ? Si oui, proposer un schéma détaillé montrant l'exécution de cette instruction. Si non, expliquer pourquoi. |
60 | | |
61 | | |
62 | | '''''Solution 3 :''''' |
63 | | |
64 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q3.svg)]] |
65 | | |
66 | | Cette instruction peut être exécutée dans le pipeline du Mips puisque l'incrémentation de RT s'effectue après l'évaluation de la condition de branchement. Cette incrémentation peut être prise en charge par le cycle EXE. Puis, la mise à jour de RT s'effectue naturellement dans le cycle `WBK` en sélectionnant RT comme registre destinataire. |
| 165 | On considère un processeur RISC pipeline, appelé '''PROC'''. '''PROC''' a le même jeu d'instructions que le Mips-32. PROC a un pipeline à 7 étages : `IF1`, `IF2`, `DEC`, `EXE`, `MM1`, `MM2`, `WBK`. |
| 166 | |
| 167 | Au début de l'étage '''`IF1`''', l'adresse de l'instruction est envoyée à la mémoire et l'instruction correspondante est récupérée à la fin du cycle '''`IF2`'''. De la même manière, l'accès à la mémoire de données se déroule en deux cycles. Comme dans Mips-32, le calcul de l'adresse d'instruction s'effectue dans '''`DEC`'''. De même, les étages '''`EXE`''' et '''`WBK`''' ressemblent aux étages '''`EXE`''' et '''`WBK`''' du Mips. |
| 168 | |
| 169 | ''a - Montrer, à l'aide d'un schéma détaillé, l'exécution de l'instruction '''`SW`''' dans '''PROC'''.'' |
| 170 | |
| 171 | |
| 172 | '''''Solution 3-a :''''' |
| 173 | |
| 174 | Dans PROC les accès mémoire se font en deux cycles. Cela veut dire que le système mémoire est lui-même en pipeline pour pouvoir traiter une nouvelle demande à chaque cycle. Dans le cycle WBK, à chaque cycle on écrit dans le banc de registres. Lorsque l'instruction ne nécessite pas de sauvegarder un résultat dans un registre, on écrit dans le registre 0. |
| 175 | |
| 176 | [[Image(htdocs:/corrige/Seance4/S4_corrige_Q3_a.svg)]] |
| 177 | |
| 178 | |
| 179 | |
| 180 | '''PROC''' contient tous les bypass qui arrivent dans les étages '''`DEC`''' et '''`EXE`'''. On souhaite étudier l'impact de la mise en place d'un bypass dans l'étage '''`MM1`''' sur la performance du processeur. On considère donc deux versions de '''PROC'''. |
| 181 | |
| 182 | Dans '''PROC1''', il n'y a pas de bypass entre l'étage '''`MM2`''' de l'instruction `i` et l'étage '''`MM1`''' de l'instruction `i+2`. La période de l'horloge est de 2 ns pour '''PROC1'''. |
| 183 | Dans '''PROC2''', il y a un bypass entre l'étage '''`MM2`''' de l'instruction `i` et l'étage '''`MM1`''' de l'instruction `i+2`. La période de l'horloge est de 2,1 ns pour '''PROC2'''. |
| 184 | |
| 185 | ''b - Expliquer pourquoi la période de l'horloge est plus longue dans '''PROC2''' ?'' |
| 186 | |
| 187 | |
| 188 | '''''Solution 3-b :''''' |
| 189 | |
| 190 | Dans un processeur pipeline, le temps de cycle est souvent déterminé par le temps d'accès à la mémoire. En effet, l'accès à la mémoire se trouve souvent sur le chemin critique du processeur (l'opération la plus longue). Dans PROC, l'accès à la mémoire s'effectue en deux cycles. Si l'on place un bypass dans les cycles d'accès à la mémoire, il y a une très forte probabilité d'aggraver le chemin critique du processeur et donc de ralentir le temps de cycle. |
| 191 | |
| 192 | |
| 193 | Pour comparer la performance des 2 versions de '''PROC''', on considère le code de l'exercice 2. |
| 194 | |
| 195 | ''c - Modifier le code du programme pour qu'il soit exécutable sur '''PROC'''.'' |
| 196 | |
| 197 | |
| 198 | '''''Solution 3-c :''''' |
| 199 | |
| 200 | Dans '''PROC''', l'adresse de l'instruction est calculée dans le cycle '''`DEC`''', c'est-à-dire le troisième cycle du pipeline. Il y a donc 2 delayed slots dans ce processeur. La manière la plus simple de rendre le code exécutable est de remplir les delayed slots avec des `Nop`. |
| 201 | {{{ |
| 202 | #!asm |
| 203 | loop: |
| 204 | Lbu r8, 0(r4) ; lire un pixel |
| 205 | Addu r8 , r8, r5 |
| 206 | Lbu r9 , 0(r8) ; lire f(pixel) |
| 207 | Sb r9 , 0(r4) |
| 208 | |
| 209 | Addiu r4 , r4 , 1 |
| 210 | Bne r4 , r10, loop |
| 211 | Nop |
| 212 | Nop |
| 213 | }}} |
| 214 | |
| 215 | |
| 216 | ''d - Analyser, à l'aide d'un schéma simplifié, l'exécution de ce code dans '''PROC1'''. Combien de cycles sont nécessaires pour l'exécution d'une itération de la boucle ? Calculer le CPI et le CPI-utile. Combien dure (en seconde) le traitement d'une image de 1024 pixels ?'' |
| 217 | |
| 218 | |
| 219 | '''''Solution 3-d :''''' |
| 220 | |
| 221 | On a les dépendances de données suivantes : |
| 222 | * entre `Lbu` et `Addu` (2 cycles de gel et bypass MM2-EXE) |
| 223 | * entre `Addu` et `Lbu` (bypass EXE-EXE) |
| 224 | * entre `Lbu` et `Sb` (2 cycles de gel et bypass MM2-EXE) |
| 225 | * entre `Addiu` et `Bne` (1 cycle de gel et bypass EXE-DEC) |
| 226 | |
| 227 | [[Image(htdocs:/corrige/Seance4/S4_corrige_Q3_d.svg)]] |
| 228 | |
| 229 | En tout, il faut donc 13 cycles pour exécuter une itération de la boucle. |
| 230 | |
| 231 | CPI = 13/8 cycles/instruction -- CPI-utile = 13/6 cycles/instruction |
| 232 | |
| 233 | Pour une image de 1024 pixels il faut 13 x 1024 = 13312 cycles soit 13312 x 2 = 26624 ns |
| 234 | |
| 235 | |
| 236 | ''e - Analyser, à l'aide d'un schéma simplifié, l'exécution de ce code dans '''PROC2'''. Combien de cycles sont nécessaires pour l'exécution d'une itération de la boucle. Calculer le CPI et le CPI-utile. Combien dure (en seconde) le traitement d'une image de 1024 pixels ?'' |
| 237 | |
| 238 | |
| 239 | |
| 240 | '''''Solution 3-e :''''' |
| 241 | |
| 242 | On a les dépendances de données suivantes : |
| 243 | * entre `Lbu` et `Addu` (2 cycles de gel et bypass MM2-EXE) |
| 244 | * entre `Addu` et `Lbu` (bypass EXE-EXE) |
| 245 | * entre `Lbu` et `Sb` (1 cycle de gel et bypass MM2-MM1) |
| 246 | * entre `Addiu` et `Bne` (1 cycle de gel et bypass EXE-DEC) |
| 247 | |
| 248 | [[Image(htdocs:/corrige/Seance4/S4_corrige_Q3_e.svg)]] |
| 249 | |
| 250 | En tout, il faut donc 12 cycles pour exécuter une itération de la boucle. |
| 251 | |
| 252 | CPI = 12/8 cycles/instruction -- CPI-utile = 12/6 cycles/instruction |
| 253 | |
| 254 | Pour une image de 1024 pixels il faut 12 x 1024 = 12288 cycles soit 12288 x 2.1 = 25804.8 ns |
| 255 | |
| 256 | La mise en place du bypass permet de gagner un cycle dans le traitement (gain de 1/13). En contrepartie on perd 5% sur le temps de cycle. On aboutit donc globalement à une accélération dans le traitement de l'image. |
| 257 | |
| 258 | |
| 259 | ''f - Pour '''PROC1''', réordonner le code afin d'éviter au maximum les cycles perdus. Combien de cycles sont maintenant nécessaires pour l'exécution d'une itération de la boucle. Calculer le CPI et le CPI-utile. Combien dure (en seconde) le traitement d'une image de 1024 pixels ?'' |
| 260 | |
| 261 | |
| 262 | '''''Solution 3-f :''''' |
| 263 | |
| 264 | Il y a en tout 7 cycles perdus (5 cycles de gel et 2 de `Nop`). Les 4 premières instructions sont dépendantes. On ne peut donc pas changer leur ordre. La seule instruction que l'on peut réordonner est la deuxième `Addiu`. Elle peut être placée entre le premier `Lbu` et `Addiu` pour combler un cycle de gel. On peut aussi placer le `Sb` dans le deuxième delayed slot pour l'écarter du `Lbu`. |
| 265 | {{{ |
| 266 | #!asm |
| 267 | loop: |
| 268 | lbu r8 , 0(r4) ; lire un pixel |
| 269 | addiu r4 , r4 , 1 |
| 270 | addu r8 , r8 , r5 |
| 271 | lbu r9 , 0(r8) ; lire f(pixel) |
| 272 | |
| 273 | bne r4 , r10, loop |
| 274 | nop |
| 275 | sb r9 , -1(r4) |
| 276 | }}} |
| 277 | |
| 278 | On a les dépendances de données suivantes : |
| 279 | * entre `Lbu` et `Addu` (1 cycle de gel et bypass MM2-EXE) |
| 280 | * entre `Addu` et `Lbu` (bypass EXE-EXE) |
| 281 | * entre `Lbu` et `Sb` (bypass MM2-EXE) |
| 282 | * entre `Addiu` et `Bne` (bypass MM2-DEC) |
| 283 | |
| 284 | [[Image(htdocs:/corrige/Seance4/S4_corrige_Q3_f.svg)]] |
| 285 | |
| 286 | En tout, il faut donc 8 cycles pour exécuter une itération de la boucle. |
| 287 | |
| 288 | CPI = 8/7 cycles/instruction -- CPI-utile = 8/6 cycles/instruction |
| 289 | |
| 290 | Pour une image de 1024 pixels il faut 8 x 1024 = 8192 cycles soit 8192 x 2 = 16384 ns. |
| 291 | |
| 292 | |
| 293 | ''g - Pour '''PROC2''', réordonner le code afin d'éviter au maximum les cycles perdus. Combien de cycles sont maintenant nécessaires pour l'exécution d'une itération de la boucle. Calculer le CPI et le CPI-utile. Combien dure (en seconde) le traitement d'une image de 1024 pixels ?'' |
| 294 | |
| 295 | |
| 296 | |
| 297 | '''''Solution 3-g :''''' |
| 298 | |
| 299 | Le même ordonnancement convient à '''PROC2'''. En tout, il faut donc 8 cycles pour exécuter une itération de la boucle. |
| 300 | |
| 301 | CPI = 8/7 cycles/instruction -- CPI-utile = 8/6 cycles/instruction |
| 302 | |
| 303 | Pour une image de 1024 pixels il faut 8 x 1024 = 8192 cycles soit 8192 x 2.1 = 17203.2 ns. |
| 304 | |
| 305 | Le réordonnancement a permis d'éliminer les cycles de gel entre `Lbu` et `Sb`. Le bypass entre '''`MM2`''' et '''`MM1`''' n'apporte donc aucune amélioration. Globalement le traitement est ralenti à cause de la période d'horloge. |
| 306 | |
| 307 | |
| 308 | ''h - Pour améliorer la performance du traitement, on décide de dérouler une fois la boucle (traitement de 2 pixels par itération). Proposer un code optimisé pour '''PROC1'''. Combien de cycles sont maintenant nécessaires pour l'exécution d'une itération de la boucle. Calculer le CPI et le CPI-utile. Combien dure (en seconde) le traitement d'une image de 1024 pixels ?'' |
| 309 | |
| 310 | |
| 311 | |
| 312 | '''''Solution 3-h :''''' |
| 313 | {{{ |
| 314 | #!asm |
| 315 | loop: |
| 316 | Lbu r8 , 0(r4) ; lire un pixel |
| 317 | Lbu r12, 1(r4) ; lire un pixel |
| 318 | Addiu r4 , r4 , 2 |
| 319 | Addu r8 , r8 , r5 |
| 320 | Addu r12, r12, r5 |
| 321 | Lbu r9 , 0(r8) ; lire f(pixel) |
| 322 | Lbu r13, 0(r12) ; lire f(pixel) |
| 323 | |
| 324 | Bne r4 , r10, loop |
| 325 | Sb r9 , -2(r4) |
| 326 | Sb r13, -1(r4) |
| 327 | }}} |
| 328 | |
| 329 | Il n'y a aucun cycle perdu. En tout, il faut donc 10 cycles pour exécuter une itération de la boucle. |
| 330 | |
| 331 | CPI = 10/10 cycles/instruction -- CPI-utile = 10/10 cycles/instruction |
| 332 | |
| 333 | Pour une image de 1024 pixels il faut 512 itérations soit 10 x 512 = 5120 cycles soit 5120 x 2 = 10240 ns. |
| 334 | |
| 335 | |
| 336 | ''i - Pour améliorer la performance du traitement, on décide de dérouler une fois la boucle (traitement de 2 pixels par itération). Proposer un code optimisé pour '''PROC2'''. Combien de cycles sont maintenant nécessaires pour l'exécution d'une itération de la boucle. Calculer le CPI et le CPI-utile. Combien dure (en seconde) le traitement d'une image de 1024 pixels ?'' |
| 337 | |
| 338 | |
| 339 | '''''Solution 3-i :''''' |
| 340 | |
| 341 | Le même code convient à '''PROC2'''. En tout, il faut donc 10 cycles pour exécuter une itération de la boucle. |
| 342 | |
| 343 | CPI = CPI-utile = 1 cycles/instruction |
| 344 | |
| 345 | Pour une image de 1024 pixels il faut 10x 512 = 5120 cycles soit 5120 x 2.1 = 10752 ns. |
| 346 | |
| 347 | Le réordonnancement a permis d'éliminer les cycles de gel entre `Lbu` et `Sb`. Le bypass entre '''`MM2`''' et '''`MM1`''' n'apporte donc aucune amélioration. Globalement le traitement est ralenti à cause de la période d'horloge. |
72 | | On considère une nouvelle instruction de branchement conditionnel : l'instruction BEQPD (Branch if equal and pre-decrement). |
73 | | {{{ |
74 | | Beqpd rs, rt, Label |
75 | | }}} |
76 | | |
77 | | Il s'agit d'une instruction de format I. Cette instruction réalise l'opération suivante. Comme pour `BEQ`, le contenu de RS est comparé au contenu de RT. En cas d'égalité, on se branche à l'instruction portant l'étiquette Label, sinon le branchement échoue et on continue en séquence. En plus, avant d'effectuer le test, le contenu de RT est decrémenté. |
78 | | |
79 | | Le schéma d'exécution du pipeline du Mips convient-il à l'exécution de cette instruction ? Si oui, proposer un schéma détaillé montrant l'exécution de cette instruction. Si non, expliquer pourquoi. |
80 | | |
81 | | |
82 | | '''''Solution 4 :''''' |
83 | | |
84 | | Cette instruction ne peut pas être exécutée dans le pipeline du Mips. En effet, avant d'évaluer la condition de branchement, il faut décrémenter RT. Cette action doit donc s'effectuer dans le cycle DEC puisque c'est dans ce cycle que s'effectue le calcul de l'adresse cible du branchement. Or, il est clair que l'ensemble de ces opérations (lecture des opérandes, décrémentation de RT puis, évaluation de la condition de branchement) nécessite un temps bien supérieur au temps de cycle si l'on considère comme référence pour la période d'horloge, le temps nécessaire à l'exécution de l'étage `EXE` (un calcul - typiquement une addition - et un multiplexeur pour sélectionner le résultat parmi tous les résultats calculés par les différents opérateurs). Ainsi, l'exécution de cette instruction dans le pipeline du MIPS aboutirait à un pipeline fortement déséquilibré ce qui est contraire aux règles de conception des pipelines. |
85 | | |
86 | | |
87 | | |
88 | | = Exercice 5 : = |
89 | | |
90 | | Montrer à l’aide d’un schéma détaillé l’exécution de la suite d’instructions suivante dans le pipeline du Mips-32. |
91 | | {{{ |
92 | | Add r3 , r2 , r1 |
93 | | Add r3 , r3 , r1 |
94 | | }}} |
95 | | |
96 | | Quelle est la condition du déclenchement des bypass ? |
97 | | |
98 | | |
99 | | '''''Solution 5 :''''' |
100 | | |
101 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q5.svg)]] |
102 | | |
103 | | On remarque une dépendance de données entre ces deux instructions (sur RS). Cette dépendance est résolue grâce à un bypass entre la sortie de l'étage EXE de la première instruction et le début de l'étage `EXE` de la seconde. |
104 | | Le multiplexeur qui se trouve avant l'additionneur est le bypass. Il se déclenche lorsque le numéro du registre RS de l'instruction courante (I_RD) est identique au numéro du registre RD de l'instruction précédente (I_RE). |
105 | | |
106 | | |
107 | | |
108 | | = Exercice 6 : = |
109 | | |
110 | | Montrer à l’aide d’un schéma détaillé l’exécution de la suite d’instructions suivante dans le pipeline du Mips-32. |
111 | | {{{ |
112 | | Add r0 , r2 , r1 |
113 | | Add r3 , r0 , r1 |
114 | | }}} |
115 | | Donner l'expression exacte de la condition du déclenchement des bypass ? |
116 | | |
117 | | |
118 | | '''''Solution 6 :''''' |
119 | | |
120 | | A première vue on pourrait penser qu'il y a une dépendance de données entre ces deux instructions. Cependant, ce n'est pas le cas car la première instruction écrit dans le registre R0 (en fait, cette instruction ne fait rien). Autrement dit, il ne faut pas activer le bypass. Le schéma simplifié est le suivant. |
121 | | |
122 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q6_a.svg)]] |
123 | | |
124 | | Le schéma détaillé correspond à l'exécution de 2 instructions d'addition. |
125 | | |
126 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q6_b.svg)]] |
127 | | |
128 | | On peut donc dire que le bypass se déclenche si le numéro du registre RS de l'instruction courante (I_RD) est identique au numéro du registre RD de l'instruction précédente (I_RE) et qu'il ne s'agit pas du numéro 0. Il faut ajouter à cette condition : il faut que l'instruction précédente écrive dans le registre RD et que l'instruction courante ait besoin du registre RS. |
129 | | |
130 | | |
131 | | |
132 | | = Exercice 7 : = |
133 | | |
134 | | Montrer à l’aide d’un schéma détaillé l’exécution de la suite d’instructions suivante dans le pipeline du Mips-32. |
135 | | {{{ |
136 | | Lw r3 , 0 (r2) |
137 | | Add r3 , r3 , r1 |
138 | | }}} |
139 | | |
140 | | |
141 | | '''''Solution 7 :''''' |
142 | | |
143 | | Il y a une dépendance de données entre ces deux instructions qui provoque un cycle de gel (stall). |
144 | | |
145 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q7.svg)]] |
146 | | |
147 | | |
148 | | |
149 | | = Exercice 8 : = |
150 | | |
151 | | Analyser l’exécution de la suite d’instructions suivante dans le processeur Mips-32 à l’aide d’un schéma simplifié. |
152 | | {{{ |
153 | | #!asm |
154 | | Addiu r2 , r0 , 4 |
155 | | Lui r3 , 0x000c |
156 | | Add r2 , r2 , r2 |
157 | | Ori r3 , r3 , 0x4568 |
158 | | Lw r2 , 0(r3) |
159 | | Lbu r2 , 0(r2) |
160 | | Ori r2 , r2 , 0x0001 |
161 | | Bltzal r2 , suite |
162 | | Addu r0 , r0 , r0 |
163 | | suite: |
164 | | Jr r31 |
165 | | Addu r31, r31, -8 |
166 | | }}} |
167 | | |
168 | | |
169 | | '''''Solution 8 :''''' |
170 | | |
171 | | Le schéma suivant montre les dépendances de données dans ce code. |
172 | | |
173 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q8_a.svg)]] |
174 | | |
175 | | |
176 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q8_b.svg)]] |
177 | | |
178 | | |
179 | | |
180 | | |
181 | | = Exercice 9 : = |
182 | | |
183 | | La réalisation du processeur Mips-32 nécessite la mise en œuvre de 8 bypass (raccourcis). Pour chacun de ces bypass, proposer un exemple de suite d'instructions qui illustre la nécessité de ce bypass. |
184 | | |
185 | | |
186 | | '''''Solution 9 :''''' |
187 | | |
188 | | Il existe deux bypass (un sur RS et un sur RT) entre la sortie de l'étage `EXE` et le début de l'étage `EXE`. Ces bypass résolvent les dépendances de données d'ordre 1 (entre une instruction et l'instruction qui la précède). |
189 | | |
190 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q9_a.svg)]] |
191 | | |
192 | | Exemple (bypass sur RS) : |
193 | | {{{ |
194 | | Add r3 , r2 , r1 |
195 | | Add r3 , r3 , r4 |
196 | | }}} |
197 | | |
198 | | Exemple (bypass sur RT) : |
199 | | {{{ |
200 | | Add r3 , r2 , r1 |
201 | | Add r3 , r4 , r3 |
202 | | }}} |
203 | | |
204 | | Il existe deux bypass (un sur RS et un sur RT) entre la sortie de l'étage `EXE` et le début de l'étage `DEC`. Ces bypass résolvent les dépendances de données d'ordre 2 (entre une instruction et la deuxième instruction qui la précède). Ces bypass sont nécessaires lorsque l'opérande (RS ou RT) est consommé dans le cycle `DEC`. |
205 | | |
206 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q9_b.svg)]] |
207 | | |
208 | | Exemple (bypass sur RS) : |
209 | | {{{ |
210 | | Add r3 , r2 , r1 |
211 | | ... (une instruction quelconque) |
212 | | Beq r3 , r4 , Suite |
213 | | }}} |
214 | | |
215 | | Exemple (bypass sur RT) : |
216 | | {{{ |
217 | | Add r3 , r2 , r1 |
218 | | ... (une instruction quelconque) |
219 | | Beq r4 , r3 , Suite |
220 | | }}} |
221 | | |
222 | | Il existe deux bypass (un sur RS et un sur RT) entre la sortie de l'étage `MEM` et le début de l'étage `EXE`. Ces bypass résolvent les dépendances de données d'ordre 2 (entre une instruction et la deuxième instruction qui la précède). Ces bypass sont nécessaires lorsque l'opérande (RS ou RT) est produit par le cycle `MEM`. |
223 | | |
224 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q9_c.svg)]] |
225 | | |
226 | | Exemple (bypass sur RS) : |
227 | | {{{ |
228 | | Lw r3 , 0 (r2) |
229 | | ... (une instruction quelconque) |
230 | | Add r5 , r3 , r4 |
231 | | }}} |
232 | | |
233 | | Exemple (bypass sur RT) : |
234 | | {{{ |
235 | | Lw r3 , 0 (r2) |
236 | | ... (une instruction quelconque) |
237 | | Add r5 , r4 , r3 |
238 | | }}} |
239 | | |
240 | | Il existe deux bypass (un sur RS et un sur RT) entre la sortie de l'étage `MEM` et le début de l'étage `DEC`. Ces bypass résolvent les dépendances de données d'ordre 3 (entre une instruction et la troisième instruction qui la précède). |
241 | | |
242 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q9_d.svg)]] |
243 | | |
244 | | Exemple (bypass sur RS) : |
245 | | {{{ |
246 | | Add r3 , r2 , r1 |
247 | | ... (une instruction quelconque) |
248 | | ... (une instruction quelconque) |
249 | | Add r5 , r3 , r4 |
250 | | }}} |
251 | | |
252 | | Exemple (bypass sur RT) : |
253 | | {{{ |
254 | | Add r3 , r2 , r1 |
255 | | ... (une instruction quelconque) |
256 | | ... (une instruction quelconque) |
257 | | Add r5 , r4 , r3 |
258 | | }}} |
259 | | |
260 | | |
261 | | |
262 | | = Exercice 10 : = |
263 | | |
264 | | La fonction strlen calcule le nombre de caractères : |
265 | | {{{ |
266 | | #!c |
267 | | unsigned int strlen (char * str) { |
268 | | unsigned int i = 0; |
269 | | while (str [i] != '\0') { |
270 | | i++; |
271 | | } |
272 | | return (i); |
273 | | } |
274 | | }}} |
275 | | |
276 | | Différents codes assembleur peuvent être produits pour cette fonction pour un processeur non-pipeline : |
277 | | |
278 | | 1ère version : |
279 | | {{{ |
280 | | #!asm |
281 | | _strlen: |
282 | | Addiu r29, r29, -1*4 |
283 | | Addu r2, r0 , r0 ; initialisation |
284 | | _strlen_loop: |
285 | | Lb r9 , 0(r4) ; lire 1 caractere |
286 | | Beq r9 , r0 , _strlen_end_loop ; fin de chaine ? |
287 | | Addiu r4 , r4 , 1 ; caractere suivant |
288 | | Addiu r2 , r2 , 1 ; caractere suivant |
289 | | J _strlen_loop ; boucle |
290 | | |
291 | | _strlen_end_loop: |
292 | | Addiu r29, r29, 1*4 |
293 | | Jr r31 |
294 | | }}} |
295 | | |
296 | | 2ème version : |
297 | | {{{ |
298 | | #!asm |
299 | | _strlen: |
300 | | Addiu r29, r29, -1*4 |
301 | | Addiu r2, r0 , -1 ; initialisation |
302 | | _strlen_loop: |
303 | | Lb r9 , 0(r4) ; lire 1 caractere |
304 | | Addiu r4 , r4 , 1 ; caractere suivant |
305 | | Addiu r2 , r2 , 1 ; caractere suivant |
306 | | Bne r9 , r0 , _strlen_loop ; fin de chaine ? |
307 | | |
308 | | Addiu r29, r29, 1*4 |
309 | | Jr r31 |
310 | | }}} |
311 | | |
312 | | 3ème version : |
313 | | {{{ |
314 | | #!asm |
315 | | _strlen: |
316 | | Addiu r29, r29, -1*4 |
317 | | Addiu r8, r4 , 1 ; sauvegarde adresse debut |
318 | | _strlen_loop: |
319 | | Lb r9 , 0(r4) ; lire 1 caractere (octet) |
320 | | Addiu r4 , r4 , 1 ; caractere suivant |
321 | | Bne r9 , r0 , _strlen_loop ; fin de chaine ? |
322 | | Subu r2 , r4 , r8 ; calculer nbr de carac |
323 | | |
324 | | Addiu r29, r29, 1*4 |
325 | | Jr r31 |
326 | | }}} |
327 | | |
328 | | Pour chacune des trois versions, transformer le code de telle façon qu'il puisse être exécuté sur le processeur Mips pipeline à 5 étages. |
329 | | |
330 | | |
331 | | '''''Solution 10 :''''' |
332 | | |
333 | | Dans le processeur Mips, l'instruction qui suit un branchement est exécutée dans tous les cas. Pour tenir compte de cette particularité, sans perturber le code généré par le compilateur, il suffit d'ajouter une instruction NOP après chaque branchement. |
334 | | |
335 | | 1ère version : |
336 | | {{{ |
337 | | #!asm |
338 | | _strlen: |
339 | | Addiu r29, r29, -1*4 |
340 | | Addu r2, r0 , r0 ; initialisation |
341 | | _strlen_loop: |
342 | | Lb r9 , 0(r4) ; lire 1 caractere |
343 | | Beq r9 , r0 , _strlen_end_loop ; fin de chaine ? |
344 | | Nop |
345 | | Addiu r4 , r4 , 1 ; caractere suivant |
346 | | Addiu r2 , r2 , 1 ; caractere suivant |
347 | | J _strlen_loop ; boucle |
348 | | Nop |
349 | | |
350 | | Addiu r29, r29, 1*4 |
351 | | Jr r31 |
352 | | Nop |
353 | | }}} |
354 | | |
355 | | 2ème version : |
356 | | {{{ |
357 | | #!asm |
358 | | _strlen: |
359 | | Addiu r29, r29, -1*4 |
360 | | Addiu r2, r0 , -1 ; initialisation |
361 | | _strlen_loop: |
362 | | Lb r9 , 0(r4) ; lire 1 caractere |
363 | | Addiu r4 , r4 , 1 ; caractere suivant |
364 | | Addiu r2 , r2 , 1 ; caractere suivant |
365 | | Bne r9 , r0 , _strlen_loop ; fin de chaine ? |
366 | | Nop |
367 | | |
368 | | Addiu r29, r29, 1*4 |
369 | | Jr r31 |
370 | | Nop |
371 | | }}} |
372 | | |
373 | | 3ème version : |
374 | | {{{ |
375 | | #!asm |
376 | | _strlen: |
377 | | Addiu r29, r29, -1*4 |
378 | | Addiu r8, r4 , 1 ; sauvegarde adresse debut |
379 | | _strlen_loop: |
380 | | Lb r9 , 0(r4) ; lire 1 caractere (octet) |
381 | | Addiu r4 , r4 , 1 ; caractere suivant |
382 | | Bne r9 , r0 , _strlen_loop ; fin de chaine ? |
383 | | Nop |
384 | | Subu r2 , r4 , r8 ; calculer nbr de carac |
385 | | |
386 | | Addiu r29, r29, 1*4 |
387 | | Jr r31 |
388 | | Nop |
389 | | }}} |
390 | | |
391 | | |
392 | | Analyser l'exécution de la boucle sur le processeur Mips à l'aide d'un schéma simplifié. |
393 | | |
394 | | |
395 | | '''''Solution 10 :''''' |
396 | | |
397 | | 1ère version : |
398 | | |
399 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q10_1_a.svg)]] |
400 | | |
401 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q10_1_b.svg)]] |
402 | | |
403 | | 2ème version : |
404 | | |
405 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q10_2_a.svg)]] |
406 | | |
407 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q10_2_b.svg)]] |
408 | | |
409 | | 3ème version : |
410 | | |
411 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q10_3_a.svg)]] |
412 | | |
413 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q10_3_b.svg)]] |
414 | | |
415 | | |
416 | | |
417 | | Calculer le nombre de cycles nécessaires à l'exécution d'une itération de la boucle. |
418 | | |
419 | | |
420 | | '''''Solution 10 :''''' |
421 | | |
422 | | * 1ère version : 9 cycles par itération |
423 | | * 2ème version : 5 cycles par itération |
424 | | * 3ème version : 5 cycles par itération |
425 | | |
426 | | |
427 | | |
428 | | Calculer le CPI et le CPI-utile de la boucle. |
429 | | |
430 | | |
431 | | '''''Solution 10 :''''' |
432 | | |
433 | | * 1ère version : CPI = 9/7 cycles/inst -- CPI-utile = 9/5 cycles/inst |
434 | | * 2ème version : CPI = 5/5 cycles/inst -- CPI-utile = 5/4 cycles/inst |
435 | | * 3ème version : CPI = 5/4 cycles/inst -- CPI-utile = 5/3 cycles/inst |
436 | | |
437 | | |
438 | | |
439 | | |
440 | | = Exercice 11 : = |
441 | | |
442 | | La fonction strupper transforme une chaîne de caractères en majuscules : |
443 | | {{{ |
444 | | #!c |
445 | | char * strupper (char * src) { |
446 | | int i = 0; |
447 | | while (src[i] != '\0') { |
448 | | if ((src[i] >= 'a') && (src[i] <= 'z')) { |
449 | | src[i] = src[i] - 'a' + 'A' |
450 | | } |
451 | | i++; |
452 | | } |
453 | | return src; |
454 | | } |
455 | | }}} |
456 | | |
457 | | La compilation de cette fonction pour un processeur non-pipeline a produit le code suivant: |
458 | | {{{ |
459 | | #!asm |
460 | | _strupper: |
461 | | Addiu r29, r29, -1*4 |
462 | | |
463 | | Addu r2 , r0 , r4 ; valeur de retour |
464 | | Addiu r11, r0 , 'a' ; pour la comparaison |
465 | | Addiu r12, r0 , 'z' ; pour la comparaison |
466 | | |
467 | | _strupper_loop: |
468 | | Lb r8 , 0(r4) ; lire src[i] |
469 | | Slt r9 , r8 , r11 ; src[i] < 'a' |
470 | | Slt r10, r12, r8 ; 'z' < src[i] |
471 | | Or r10, r10, r9 ; et des 2 conditions |
472 | | Bne r10, r0 , _strupper_endif ; si 1 des 2 cond vraie |
473 | | Addiu r8 , r8 , 'A'-'a'; transformer en majuscule |
474 | | Sb r8 , 0(r4) |
475 | | |
476 | | _strupper_endif: |
477 | | Addiu r4 , r4 , 1 ; caractère suivant |
478 | | Bne r8 , r0 , _strupper_loop |
479 | | |
480 | | Addiu r29, r29, 1*4 |
481 | | Jr r31 |
482 | | }}} |
483 | | |
484 | | Transformer ce code de telle façon qu'il puisse être exécuté sur le processeur Mips pipeline à 5 étages. |
485 | | |
486 | | |
487 | | '''''Solution 11 :''''' |
488 | | |
489 | | Dans le processeur Mips, l'instruction qui suit un branchement est exécutée dans tous les cas. Pour tenir compte de cette particularité, sans perturber le code généré par le compilateur, il suffit d'ajouter une instruction NOP après chaque branchement. |
490 | | |
491 | | {{{ |
492 | | #!asm |
493 | | __strupper: |
494 | | Addiu r29, r29, -1*4 |
495 | | |
496 | | Addu r2 , r0 , r4 ; valeur de retour |
497 | | Addiu r11, r0 , 'a' ; pour la comparaison |
498 | | Addiu r12, r0 , 'z' ; pour la comparaison |
499 | | |
500 | | _strupper_loop: |
501 | | Lb r8 , 0(r4) ; lire src[i] |
502 | | Slt r9 , r8 , r11 ; src[i] < 'a' |
503 | | Slt r10, r12, r8 ; 'z' < src[i] |
504 | | Or r10, r10, r9 ; et des 2 conditions |
505 | | Bne r10, r0 , _strupper_endif ; si 1 des 2 cond vraie |
506 | | Nop |
507 | | Addiu r8 , r8 , 'A'-'a'; transformer en majuscule |
508 | | Sb r8 , 0(r4) |
509 | | |
510 | | _strupper_endif: |
511 | | Addiu r4 , r4 , 1 ; caractère suivant |
512 | | Bne r8 , r0 , _strupper_loop |
513 | | Nop |
514 | | |
515 | | Addiu r29, r29, 1*4 |
516 | | Jr r31 |
517 | | Nop |
518 | | }}} |
519 | | |
520 | | |
521 | | Analyser l'exécution de la boucle sur le processeur Mips à l'aide d'un schéma simplifié dans le cas où le `if` (du code source) réussit, puis dans le cas où le `if` échoue. |
522 | | |
523 | | |
524 | | '''''Solution 11 :''''' |
525 | | |
526 | | Attention, il y a une dépendance entre le dernier `Addiu` et le `Lb` de l'itération suivante. |
527 | | |
528 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q11_a.svg)]] |
529 | | |
530 | | Cas où le `if` réussit : |
531 | | |
532 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q11_b.svg)]] |
533 | | |
534 | | Cas où le `if` échoue : |
535 | | |
536 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q11_c.svg)]] |
537 | | |
538 | | |
539 | | Calculer le nombre de cycles nécessaires à l'exécution d'une itération de la boucle dans le cas ou le `if` réussit. |
540 | | |
541 | | |
542 | | '''''Solution 11 :''''' |
543 | | |
544 | | Il faut 13 cycles pour une itération si le `if` réussit |
545 | | |
546 | | |
547 | | Calculer le nombre de cycles nécessaires à l'exécution d'une itération de la boucle dans le cas ou le `if` échoue. |
548 | | |
549 | | |
550 | | '''''Solution 11 :''''' |
551 | | |
552 | | Il faut 11 cycles pour une itération si le `if` échoue. |
553 | | |
554 | | |
555 | | Sachant que 30% des caractères sont minuscules calculer le CPI et le CPI-utile de la boucle. |
556 | | |
557 | | |
558 | | '''''Solution 11 :''''' |
559 | | |
560 | | CPI = 1.208 cycle/instruction |
561 | | |
562 | | CPI_utile = 1.526 cycle/instruction utile |
563 | | |
564 | | |
565 | | |
566 | | = Exercice 12 : = |
567 | | |
568 | | La fonction suivante calcule le PGCD de deux nombres positifs non nuls : |
569 | | {{{ |
570 | | #!c |
571 | | unsigned int pgcd (unsigned int a, unsigned int b) { |
572 | | while (a != b) { |
573 | | if (a < b) { |
574 | | b = b - a; |
575 | | } |
576 | | else { |
577 | | a = a - b; |
578 | | } |
579 | | } |
580 | | return a; |
581 | | } |
582 | | }}} |
583 | | |
584 | | La compilation de cette fonction pour un processeur non-pipeline a produit le code suivant: |
585 | | {{{ |
586 | | #!asm |
587 | | _pgcd: |
588 | | Beq r4 , r5 , _pgcd_end |
589 | | |
590 | | _pgcd_loop: |
591 | | Sltu r8 , r4 , r5 ; a < b |
592 | | Beq r8 , r0 , _pgcd_else |
593 | | Subu r5 , r5 , r4 |
594 | | J _pgcd_endif |
595 | | |
596 | | _pgcd_else: |
597 | | Subu r4 , r4 , r5 |
598 | | _pgcd_endif: |
599 | | Bne r4 , r5 , _pgcd_loop |
600 | | |
601 | | _pgcd_end: |
602 | | Add r2 , r4 , r0 ; valeur de retour |
603 | | Jr r31 |
604 | | }}} |
605 | | |
606 | | Transformer ce code de telle façon qu'il puisse être exécuté sur le processeur Mips pipeline à 5 étages. |
607 | | |
608 | | |
609 | | '''''Solution 12 :''''' |
610 | | |
611 | | Il suffit d'ajouter une instruction NOP après chaque branchement. |
612 | | {{{ |
613 | | #!asm |
614 | | _pgcd: |
615 | | Beq r4 , r5 , _pgcd_end |
616 | | Nop |
617 | | |
618 | | _pgcd_loop: |
619 | | Sltu r8 , r4 , r5 ; a < b |
620 | | Beq r8 , r0 , _pgcd_else |
621 | | Nop |
622 | | Subu r5 , r5 , r4 |
623 | | J _pgcd_endif |
624 | | Nop |
625 | | |
626 | | _pgcd_else: |
627 | | Subu r4 , r4 , r5 |
628 | | _pgcd_endif: |
629 | | Bne r4 , r5 , _pgcd_loop |
630 | | Nop |
631 | | |
632 | | _pgcd_end: |
633 | | Add r2 , r4 , r0 ; valeur de retour |
634 | | Jr r31 |
635 | | Nop |
636 | | }}} |
637 | | |
638 | | |
639 | | Analyser l'exécution de la boucle sur le processeur Mips à l'aide d'un schéma simplifié, dans le cas où le `if` (du code source) réussit, puis dans le cas où le `if` échoue. |
640 | | |
641 | | |
642 | | '''''Solution 12 :''''' |
643 | | |
644 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q12_a.svg)]] |
645 | | |
646 | | Cas où le `if` réussit : |
647 | | |
648 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q12_b.svg)]] |
649 | | |
650 | | Cas où le `if` échoue : |
651 | | |
652 | | [[Image(htdocs:/corrige/Seance3/S3_corrige_Q12_c.svg)]] |
653 | | |
654 | | |
655 | | |
656 | | |
657 | | Calculer le nombre de cycles nécessaires à l'exécution d'une itération de la boucle dans le cas ou le `if` réussit. |
658 | | |
659 | | |
660 | | '''''Solution 12 :''''' |
661 | | |
662 | | Il faut 9 cycles pour une itération si le `if` réussit. |
663 | | |
664 | | |
665 | | Calculer le nombre de cycles nécessaires à l'exécution d'une itération de la boucle dans le cas ou le `if` échoue. |
666 | | |
667 | | |
668 | | '''''Solution 12 :''''' |
669 | | |
670 | | Il faut 8 cycles pour une itération si le `if` échoue. |
671 | | |
672 | | |
673 | | Sachant que dans un cas sur deux le `if` échoue, calculer le CPI et le CPI-utile de la boucle. |
674 | | |
675 | | |
676 | | '''''Solution 12 :''''' |
677 | | |
678 | | CPI = 1.214 cycle/instrution |
679 | | |
680 | | CPI_utile = 1.889 cycle/instrution utile |
681 | | |
682 | | |
| 353 | On considère le processeur pipeline P9. Il a le même jeu d'instructions que le Mips. Il est construit autour d'un pipeline à 9 étages : |
| 354 | |
| 355 | `IF1, IF2, DEC1, DEC2, EXE1, EXE2, MEM1, MEM2, WBK` |
| 356 | |
| 357 | Le découpage en étages de ce pipeline a été obtenu à partir du pipeline du processeur Mips. Chacun des étages `IFC`, `DEC`, `EXE` et `MEM` a été divisé en deux étages. Le calcul de l'adresse de l'instruction suivante s'achève dans l'étage `DEC2`. |
| 358 | |
| 359 | On souhaite étudier le fonctionnement de ce processeur. Le code suivant est la boucle principale d'une fonction qui calcule la valeur absolue des éléments d'un tableau d'entiers. A l'entrée de la boucle `R4` contient l'adresse du tableau, le registre `R9` contient l'adresse de la fin du tableau. |
| 360 | {{{ |
| 361 | #!asm |
| 362 | loop: |
| 363 | Lw r8 , 0(r4) ; lire un élément |
| 364 | Bgez r8 , _endif |
| 365 | Sub r8 , r0 , r8 ; calculer l'opposé |
| 366 | Sw r8, 0(r4) |
| 367 | |
| 368 | _endif: |
| 369 | Addiu r4 , r4 , 4 |
| 370 | Bne r4 , r9 , loop |
| 371 | }}} |
| 372 | |
| 373 | ''a - Le découpage du pipeline en 9 étages a-t-il un impact sur le compilateur ?'' |
| 374 | |
| 375 | '''''Solution 4-a :''''' |
| 376 | |
| 377 | Oui. Dans le processeur P9, le calcul de l'adresse de l'instruction suivante se fait dans le cycle '''`DEC2`''' (4ème cycle du pipeline). Il y a donc 3 delayed slot après chaque branchement dans cette réalisation. Le compilateur doit en tenir compte pour générer le code et pour effectuer les optimisations. |
| 378 | |
| 379 | |
| 380 | ''b - Dans le processeur Mips, il existe des dépendances de données d'ordre 1, 2 et 3. Dans P9 quelles sont les dépendances de données ?'' |
| 381 | |
| 382 | |
| 383 | '''''Solution 4-b :''''' |
| 384 | |
| 385 | Dans le processeur P9, il y a des dépendances de données d'ordre 1 à 6. En effet, le cycle `DEC1` (où s'effectue la lecture du banc de registres) de l'instruction `i+7` s'effectue après le cycle `WBK` de l'instruction `i`. |
| 386 | |
| 387 | [[Image(htdocs:/corrige/Seance4/S4_corrige_Q4_b.svg)]] |
| 388 | |
| 389 | |
| 390 | ''c - Quels sont les bypass nécessaires à l'exécution des instructions dans ce P9 ? Illustrer pour chaque bypass son utilisation à l'aide d'un exemple d'une suite d'instructions.'' |
| 391 | |
| 392 | |
| 393 | '''''Solution 4-c :''''' |
| 394 | |
| 395 | * 1 bypass de la sortie de `EXE2` vers `EXE2` (ordre 1) : sur `RT` |
| 396 | {{{ |
| 397 | Add r7 , r1 , r2 |
| 398 | Sw r7 , 0(r3) |
| 399 | }}} |
| 400 | |
| 401 | * 2 bypass de la sortie de `EXE2` vers `EXE1` (ordre 2) : un sur `RS` et un sur `RT` |
| 402 | {{{ |
| 403 | Add r7 , r1 , r2 Add r8 , r1 , r2 |
| 404 | ... ... |
| 405 | Add r9 , r7 , r8 Add r9 , r7 , r8 |
| 406 | }}} |
| 407 | |
| 408 | * 1 bypass de la sortie de `MEM2` vers `EXE2` (ordre 3) : sur `RT` |
| 409 | {{{ |
| 410 | Lw r7 , 0(r1) |
| 411 | ... |
| 412 | ... |
| 413 | Sw r7 , 0(r3) |
| 414 | }}} |
| 415 | |
| 416 | * 2 bypass de la sortie de `EXE2` vers `DEC2` (ordre 3) : un sur `RS` et un sur `RT` |
| 417 | ou |
| 418 | * 2 bypass de la sortie de `MEM1` vers `EXE1` (ordre 3) : un sur `RS` et un sur `RT` |
| 419 | {{{ |
| 420 | Add r7 , r1 , r2 Add r8 , r1 , r2 |
| 421 | ... ... |
| 422 | ... ... |
| 423 | Add r9 , r7 , r8 Add r9 , r7 , r8 |
| 424 | }}} |
| 425 | |
| 426 | * 2 bypass de la sortie de `EXE2` vers `DEC1` (ordre 4) : un sur `RS` et un sur `RT` |
| 427 | {{{ |
| 428 | Add r7 , r1 , r2 Add r8 , r1 , r2 |
| 429 | ... ... |
| 430 | ... ... |
| 431 | ... ... |
| 432 | Beq r7 , r8 , suite Beq r7 , r8 , suite |
| 433 | }}} |
| 434 | |
| 435 | * 2 bypass de la sortie de `MEM2` vers `EXE1` (ordre 4) : un sur `RS` et un sur `RT` |
| 436 | {{{ |
| 437 | Lw r7 , 0(r1) Lw r8 , 0(r1) |
| 438 | ... ... |
| 439 | ... ... |
| 440 | ... ... |
| 441 | Add r9 , r7 , r8 Add r9 , r7 , r8 |
| 442 | }}} |
| 443 | |
| 444 | * 2 bypass de la sortie de `MEM1` vers `DEC1` (ordre 5) : un sur `RS` et un sur `RT` |
| 445 | {{{ |
| 446 | Add r7 , r1 , r2 Add r8 , r1 , r2 |
| 447 | ... ... |
| 448 | ... ... |
| 449 | ... ... |
| 450 | ... ... |
| 451 | Beq r7 , r8 , suite Beq r7 , r8 , suite |
| 452 | }}} |
| 453 | |
| 454 | * 2 bypass de la sortie de `MEM2` vers `DEC2` (ordre 5) : un sur `RS` et un sur `RT` |
| 455 | {{{ |
| 456 | Lw r7 , 0(r1) Lw r8 , 0(r1) |
| 457 | ... ... |
| 458 | ... ... |
| 459 | ... ... |
| 460 | ... ... |
| 461 | Add r9 , r7 , r8 Add r9 , r7 , r8 |
| 462 | }}} |
| 463 | |
| 464 | * 2 bypass de la sortie de `MEM2` vers `DEC1` (ordre 6) : un sur `RS` et un sur `RT` |
| 465 | {{{ |
| 466 | Add r7 , r1 , r2 Add r8 , r1 , r2 |
| 467 | ... ... |
| 468 | ... ... |
| 469 | ... ... |
| 470 | ... ... |
| 471 | ... ... |
| 472 | Add r9 , r7 , r8 Add r9 , r7 , r8 |
| 473 | }}} |
| 474 | |
| 475 | Au total il y a 16 bypass. |
| 476 | |
| 477 | |
| 478 | ''d - Quelles sont les situations qui provoquent des cycles de gel ? Illustrer chaque cas à l'aide d'un exemple d'une suite d'instructions.'' |
| 479 | |
| 480 | |
| 481 | '''''Solution 4-d :''''' |
| 482 | |
| 483 | Toutes les dépendances qui ne peuvent pas être résolues par un bypass provoquent des cycles de gel. |
| 484 | |
| 485 | * dépendance d'ordre 1 sur `RS` ou sur `RT` (autre que les Store) |
| 486 | {{{ |
| 487 | Add r7 , r1 , r2 |
| 488 | Add r9 , r8 , r7 |
| 489 | }}} |
| 490 | |
| 491 | * dépendance d'ordre 2 sur `RS` ou sur `RT` lorsque la valeur est produite par le cycle `MEM2` ou lorsque l'opérande est consommé dans le cycle `DEC1`. |
| 492 | {{{ |
| 493 | Add r7 , r1 , r2 Lw r7 , 0(r1) |
| 494 | ... ... |
| 495 | Beq r7 , r8 , suite Add r9 , r7 , r8 |
| 496 | }}} |
| 497 | |
| 498 | * dépendance d'ordre 3 sur `RS` ou sur `RT` lorsque la valeur est produite par le cycle `MEM2` ou lorsque l'opérande est consommé dans le cycle `DEC1`. |
| 499 | {{{ |
| 500 | Add r7 , r1 , r2 Lw r7 , 0(r1) |
| 501 | ... ... |
| 502 | ... ... |
| 503 | Beq r7 , r8 , suite Add r9 , r7 , r8 |
| 504 | }}} |
| 505 | |
| 506 | * dépendance d'ordre 4 sur `RS` ou sur `RT` lorsque la valeur est produite par le cycle `MEM2` et consommée dans le cycle `DEC1`. |
| 507 | {{{ |
| 508 | Lw r7 , 0(r1) |
| 509 | ... |
| 510 | ... |
| 511 | ... |
| 512 | Beq r7 , r8 , suite |
| 513 | }}} |
| 514 | |
| 515 | * dépendance d'ordre 5 sur `RS` ou sur `RT` lorsque la valeur est produite par le cycle `MEM2` et consommée dans le cycle `DEC1`. |
| 516 | {{{ |
| 517 | Lw r7 , 0(r1) |
| 518 | ... |
| 519 | ... |
| 520 | ... |
| 521 | ... |
| 522 | Beq r7 , r8 , suite |
| 523 | }}} |
| 524 | |
| 525 | |
| 526 | ''e - Modifier le code de la boucle pour qu'il soit exécutable sur le processeur P9.'' |
| 527 | |
| 528 | |
| 529 | '''''Solution 4-e :''''' |
| 530 | |
| 531 | La manière la plus simple est de remplir les delayed slots avec des `Nop`. |
| 532 | {{{ |
| 533 | #!asm |
| 534 | loop: |
| 535 | Lw r8 , 0(r4) ; lire un élément |
| 536 | Bgez r8 , _endif |
| 537 | Nop |
| 538 | Nop |
| 539 | Nop |
| 540 | Sub r8 , r0 , r8 ; calculer l'opposé |
| 541 | Sw r8 , 0(r4) |
| 542 | |
| 543 | _endif: |
| 544 | Addiu r4 , r4 , 4 |
| 545 | Bne r4 , r9 , loop |
| 546 | Nop |
| 547 | Nop |
| 548 | Nop |
| 549 | }}} |
| 550 | |
| 551 | |
| 552 | ''f - Analyser l'exécution d'une itération de la boucle à l'aide d'un schéma simplifié.'' |
| 553 | |
| 554 | |
| 555 | '''''Solution 4-f :''''' |
| 556 | |
| 557 | On a les dépendances de données suivantes : |
| 558 | * entre le `Lw` et le `Bgez` (5 cycles de gel) |
| 559 | * entre le `Sub` et le `Sw` (pas de cycle de gel) |
| 560 | * entre le `Addiu` et le `Bne` (3 cycles de gel) |
| 561 | |
| 562 | [[Image(htdocs:/corrige/Seance4/S4_corrige_Q4_f.svg)]] |
| 563 | |
| 564 | |
| 565 | |
| 566 | ''g - Calculer le CPI et le CPI-utile en supposant que 50% des nombres sont négatifs.'' |
| 567 | |
| 568 | |
| 569 | '''''Solution 4-g :''''' |
| 570 | |
| 571 | Il faut 20 cycles pour une itération si le branchement échoue et 18 cycles s'il réussit. D'où : |
| 572 | |
| 573 | CPI = (20*0.5 + 18*0.5)/(12*0.5 + 10*0.5) = 1.73 cycles/inst |
| 574 | |
| 575 | CPI-utile = (20*0.5 + 18*0.5)/(6*0.5 + 4*0.5) = 3.8 cycles/inst-utile |
| 576 | |
| 577 | |
| 578 | |
| 579 | |
| 580 | ''h - Réordonner ce code de manière à éviter au maximum les cycles perdus.'' |
| 581 | |
| 582 | |
| 583 | '''''Solution 4-h :''''' |
| 584 | |
| 585 | On peut déplacer le Addiu entre le `Lw` et le `Bgez`. |
| 586 | {{{ |
| 587 | #!asm |
| 588 | loop: |
| 589 | Lw r8 , 0(r4) ; lire un élément |
| 590 | Addiu r4 , r4 , 4 |
| 591 | Bgez r8 , _endif |
| 592 | Nop |
| 593 | Nop |
| 594 | Nop |
| 595 | Sub r8 , r0 , r8 ; calculer l'opposé |
| 596 | Sw r8 , -4(r4) |
| 597 | |
| 598 | _endif: |
| 599 | Bne r4 , r9 , loop |
| 600 | Nop |
| 601 | Nop |
| 602 | Nop |
| 603 | }}} |
| 604 | |
| 605 | Il faut maintenant 16 cycles par itération si le branchement échoue et 14 cycles s'il réussit. |
| 606 | |
| 607 | |
| 608 | ''i - Optimiser ce code à l'aide de la technique "software pipeline".'' |
| 609 | |
| 610 | |
| 611 | '''''Solution 4-i :''''' |
| 612 | |
| 613 | A première vue, on peut décomposer le traitement d'un élément du tableau en deux étapes : la lecture et le test et le rangement. On peut alors réorganiser le code de manière à traiter deux éléments dans une itération : la lecture de l'élément `i` et le rangement de l'élément `i-1`. |
| 614 | {{{ |
| 615 | #!asm |
| 616 | loop: |
| 617 | Bgez r8 , _endif |
| 618 | Nop |
| 619 | Nop |
| 620 | Nop |
| 621 | Sub r10, r0 , r8 ; calculer l'opposé (elem i-1) |
| 622 | Sw r10, -4(r4) |
| 623 | |
| 624 | _endif: |
| 625 | Lw r8 , 0(r4) ; lire un élément (elem i) |
| 626 | Addiu r4 , r4 , 4 |
| 627 | Bne r4 , r9 , loop |
| 628 | Nop |
| 629 | Nop |
| 630 | Nop |
| 631 | }}} |
| 632 | |
| 633 | On peut maintenant réordonner ce code. Il faut écarter le `Addiu` et le `Bne`. On peut par exemple placer le `Addiu` avant le `Bgez`. Par contre, il vaut mieux ne pas placer le `Lw` dans les delayed slot du `Bne`. En effet, on a une dépendance entre le `Lw` et le `Bgez` de l’itération suivante. Pour que cette dépendance ne crée pas de cycle de gel, il faut au moins une distance de 6. |
| 634 | {{{ |
| 635 | #!asm |
| 636 | loop: |
| 637 | Addiu r4 , r4 , 4 |
| 638 | Bgez r8 , _endif |
| 639 | Nop |
| 640 | Nop |
| 641 | Nop |
| 642 | Sub r10, r0 , r8 ; calculer l'opposé (elem i-1) |
| 643 | Sw r10, -8(r4) |
| 644 | |
| 645 | _endif: |
| 646 | Lw r8 , -4(r4) ; lire un élément (elem i) |
| 647 | Bne r4 , r9 , loop |
| 648 | Nop |
| 649 | Nop |
| 650 | Nop |
| 651 | }}} |
| 652 | |
| 653 | Il faut maintenant 12 cycles pour une itération si le branchement échoue et 10 cycles s'il réussit. |
| 654 | |
| 655 | Une autre idée consiste à sortir le `Sub` de la partie du code exécutée de manière conditionnelle (on exécute le `Sub` dans tous les cas, et on ne fait le `Sw` que si le branchement échoue). On peut gagner alors 1 cycle en déplaçant le Sub dans le dernier delayed Slot du `Bgez`. |
| 656 | |
| 657 | Il faut maintenant 11 cycles pour une itération si le branchement échoue et 10 cycles s'il réussit. |
| 658 | {{{ |
| 659 | #!asm |
| 660 | loop: |
| 661 | Addiu r4 , r4 , 4 |
| 662 | Bgez r8 , _endif |
| 663 | Nop |
| 664 | Nop |
| 665 | Sub r10, r0 , r8 ; calculer l'opposé (elem i-1) |
| 666 | Sw r10, -8(r4) |
| 667 | |
| 668 | _endif: |
| 669 | Lw r8 , -4(r4) ; lire un élément (elem i) |
| 670 | Bne r4 , r9 , loop |
| 671 | Nop |
| 672 | Nop |
| 673 | Nop |
| 674 | }}} |
| 675 | |
| 676 | Autre solution : |
| 677 | {{{ |
| 678 | #!asm |
| 679 | loop: |
| 680 | Bgez r8 , _endif |
| 681 | Addiu r4 , r4 , 4 |
| 682 | Sub r10, r0 , r8 ; calculer l'opposé |
| 683 | Lw r8 , -4(r4) ; lire un élément |
| 684 | |
| 685 | Sw r10, -8(r4) |
| 686 | |
| 687 | _endif: |
| 688 | Bne r4 , r9 , loop |
| 689 | Nop |
| 690 | Nop |
| 691 | Nop |
| 692 | }}} |
| 693 | Avec cette solution on a 9 cycles que le branchement réussisse ou non. |
| 694 | |
| 695 | |
| 696 | |
| 697 | |
| 698 | ''j - Le temps de cycle du processeur P9 est égal à 0,6 fois le temps de cycle du processeur Mips. Comparer le temps nécessaire à l'exécution d'une itération de la boucle entre le Mips et P9. Quelle conclusion en tirez-vous ?'' |
| 699 | |
| 700 | |
| 701 | '''''Solution 4-j :''''' |
| 702 | |
| 703 | Si l'on considère le code original, dans P9, il faut 20 cycles pour l'exécuter. Dans le Mips, il faut 11 cycles si le branchement échoue et 9 cycles s'il réussit. |
| 704 | {{{ |
| 705 | #!asm |
| 706 | loop: |
| 707 | Lw r8 , 0(r4) ; lire un élément |
| 708 | Bgez r8 , _endif |
| 709 | Nop |
| 710 | Sub r8 , r0 , r8 ; calculer l'opposé |
| 711 | Sw r8 , 0(r4) |
| 712 | |
| 713 | _endif: |
| 714 | Addiu r4 , r4 , 4 |
| 715 | Bne r4 , r9 , loop |
| 716 | Nop |
| 717 | }}} |
| 718 | |
| 719 | Ainsi, par rapport au Mips, l'exécution du code sans optimisation dans le P9 prend (20*0.5 + 18*0.5)*0.6/(11*0.5 + 9*0.5) = 1.14 fois plus de temps. |
| 720 | |
| 721 | Si l'on optimise le code dans le Mips : |
| 722 | {{{ |
| 723 | #!asm |
| 724 | loop: |
| 725 | Bgez r8 , _endif |
| 726 | Addiu r4 , r4 , 4 |
| 727 | Sw r10, -8(r4) |
| 728 | |
| 729 | _endif: |
| 730 | Lw r8 , -4(r4) ; lire un élément |
| 731 | Bne r4 , r6 , loop |
| 732 | Sub r10 , r0 , r8 ; calculer l'opposé |
| 733 | }}} |
| 734 | |
| 735 | et si on considère la meilleure optimisation obtenue dans P9, l'exécution dans P9 prend (11*0.5 + 10*0.5)*0.6/(6*0.5+5*0.5) = 1.15 fois plus de temps. |
| 736 | |
| 737 | Même si la fréquence de fonctionnement de P9 est plus élevée que celle du Mips, globalement, sur ce code, le traitement est ralenti. Cela est dû au grand nombre de delayed slots dans P9 et au fait que la boucle étant très court, il n'y a pas beaucoup d'occasion d'ordonnancement. Sans doute en déroulant la boucle, on peut obtenir de meilleurs résultats. |