Nicolás Wolovick $Date: 2012-04-04 10:45:06 -0300 (Wed, 04 Apr 2012) $, $Revision: 3362 $
x86_64
1 void store(double *a, double *b, double *c) {
2 *c = *a + *b;
3 }
Luego de gcc -march=native -S -o - store.c
1 store:
2 .LFB0:
3 .cfi_startproc
4 pushq %rbp
5 .cfi_def_cfa_offset 16
6 .cfi_offset 6, -16
7 movq %rsp, %rbp
8 .cfi_def_cfa_register 6
9 movq %rdi, -8(%rbp)
10 movq %rsi, -16(%rbp)
11 movq %rdx, -24(%rbp)
12 movq -8(%rbp), %rax
13 movsd (%rax), %xmm1
14 movq -16(%rbp), %rax
15 movsd (%rax), %xmm0
16 addsd %xmm1, %xmm0
17 movq -24(%rbp), %rax
18 movsd %xmm0, (%rax)
19 popq %rbp
20 .cfi_def_cfa 7, 8
21 ret
22 .cfi_endproc
Notar como pone y saca las cosas del stack
Acá van notas de presentación.
Luego de gcc -march=native -O2 -S -o - store.c
1 store:
2 .LFB0:
3 .cfi_startproc
4 movsd (%rdi), %xmm0
5 addsd (%rsi), %xmm0
6 movsd %xmm0, (%rdx)
7 ret
8 .cfi_endproc
C
a GAS
(Bryant, O’Hallaron, x86-64 Machine-Level Programming)
(Bryant, O’Hallaron, x86-64 Machine-Level Programming)
1 #include <math.h>
2
3 struct point {
4 double x;
5 double y;
6 };
7
8 double max_dist(struct point *a, const unsigned int size) {
9 double result = 0.0;
10 unsigned int i = 0;
11 for(i=0; i<size; ++a, ++i) {
12 double dst = 0.0;
13 dst = sqrt((a->x * a->x) + (a->y * a->y));
14 if (dst>result)
15 result = dst;
16 }
17 return result;
18 }
clang -O2 -S -o
1 max_dist: # @max_dist
2 .Ltmp0:
3 .cfi_startproc
4 # BB#0:
5 pxor %xmm0, %xmm0
6 testl %esi, %esi
7 je .LBB0_3
8 # BB#1: # %.lr.ph.preheader
9 addq $8, %rdi
10 pxor %xmm1, %xmm1
11 .align 16, 0x90
12 .LBB0_2: # %.lr.ph
13 # =>This Inner Loop Header: Depth=1
14 movsd -8(%rdi), %xmm0
15 movsd (%rdi), %xmm2
16 mulsd %xmm2, %xmm2
17 mulsd %xmm0, %xmm0
18 addsd %xmm2, %xmm0
19 sqrtsd %xmm0, %xmm0
20 addq $16, %rdi
21 decl %esi
22 maxsd %xmm1, %xmm0
23 movaps %xmm0, %xmm1
24 jne .LBB0_2
25 .LBB0_3: # %._crit_edge
26 ret
27 .Ltmp1:
28 .size max_dist, .Ltmp1-max_dist
29 .Ltmp2:
30 .cfi_endproc
compilar esto con gcc y ver que hace.
x86_64
x86_64
1 while (1) {
2 Fetch PC; Decode;
3 Read Inputs; Execute; Write Output;
4 Next PC;
5 }
CPI: Cicles Per Instruction IPC: 1/CPI: Instructions Per Cycle.
CPI = 1, pero ciclos lentos.
Alternativa: Ejecución Multiciclo.
Procesadores de los 70's y 80's:
8080
, Z80
, 68000
Pipelining implementa la primera forma de ILP.
Supone que insn1; insn2; insn3; ...
son independientes.
Procesadores avanzados de fines de 80's: Cray XMP
, NEC SX
, 80486
.
Ejemplo trivial:
Paralelismo entre las fases de la línea de montaje.
¿Y si hay dependencia secuencial?: data hazard
1 a = b+c;
2 d = a*a;
La instrucción 2
tiene que esperar que la 1
termine de operar.
PC
).Inyecta burbujas en el pipeline para mantener la ilusión de secuencialidad.
1 LD *R1*, 45(R2)
2 DADD R5, R6, R7
3 DSUB R8, R6, R7
4 OR R9, R6, R7
1 LD *R1*, 45(R2)
2 DADD R5, *R1*, R7
3 DSUB R8, R6, R7
4 OR R9, R6, R7
1 LD *R1*, 45(R2)
2 DADD R5, R6, R7
3 DSUB R8, *R1*, R7
4 OR R9, R6, R7
1 LD *R1*, 45(R2)
2 DADD R5, R6, R7
3 DSUB R8, R6, R7
4 OR R9, *R1*, R7
(Hennessy, Patterson, Computer Architecture ...)
Dependencia: clausura transitiva de la relación binaria depende-de
.
Es una propiedad del programa.
La implementación del pipeline determina cuantos stalls se producen.
IBM 360/91
.No puedo mover el salto ni para arriba ni para abajo.
1 DADDU R2, R3, R4
2 BEQZ R2, L1
3 LW R1, 0(R2)
4 L1: ...
Y por más de que no dependa arriba, tampoco puedo moverlo!
1 DADDU R1, R2, R3
2 BEQZ R4, L
3 DSUBU R1, R5, R6
4 L: ...
5 OR R7, R1, R8
Es muy complicado reordenar un branch
.
Opciones:
Postura optimista:
En pipelines profundos el undo penaliza mucho.
Saber que rama tomará es no-computable.
Lo mejor que podemos acertar es el 50%.
1 int max(int x, int y)
2 {
3 return (x < y) ? y : x;
4 }
1 cmpl %esi, %edi
2 cmovll %esi, %edi
3 movl %edi, %eax
4 ret
NOP
innecesario si el salto era predecible.Aumenta el ancho del pipeline.
El IPC pico es N para un N-way superscalar µP.
Mucha área del µP destinada a descubrir paralelismo.
Ya no paga más!
Pasarle la pelota al programador/compilador.
Explicitar el paralelismo.
(Core i7 3960X, Sandy Bridge Elostcircuit.com
)
(Memory Wall -- HPBD 070809 HIGH PERFORMANCE COMPUTING - WIKI, Dell Inc.)
Trade-off entre: latencia, ancho de banda, tamaño, precio.
Falta un nivel más arriba: registros.
(Hennessy, Patterson, Computer Architecture ...)
Típicamente 8 o 16-way, más no paga:
Se trae mucho más que una palabra (4 u 8 bytes).
Típicamente 64 bytes.
Favorece la localidad espacial.
1 unsigned int i = 0, s = 0;
2 for(i=s=0; i<size; i++) {
3 s +=a[i];
4 }
(Hennessy, Patterson, Computer Architecture ...)
Comparación n-way en paralelo.
Estrategia de reemplazo least-recently used (LRU), o alguna aproximación.
Favorece la localidad temporal.
1 unsigned int i = 0, s = 0;
2 for(i=s=0; i<size; i++) {
3 s +=a[i];
4 }
Dos estrategias:
Dos estrategias en write miss:
Cada acceso a memoria consulta la tabla de página.
(Hennessy, Patterson, Computer Architecture ...)
Caché completamente asociativo.
("Opteron TLB", Hennessy, Patterson, Computer Architecture ...)
Ahora hay TLB multi-niveles, uno en L1 y otro en L2.
Ayudando al compilador para aprovechar:
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |