Programao Paralela usando Memrias Transacionais da Academia Indstria

  • Slides: 144
Download presentation
Programação Paralela usando Memórias Transacionais: da Academia à Indústria IV Escola Regional de Alto

Programação Paralela usando Memórias Transacionais: da Academia à Indústria IV Escola Regional de Alto Desempenho de São Paulo julho de 2013 Guido Araujo (IC-UNICAMP) email: guido@ic. unicamp. br Alexandro Baldassin (IGCE-UNESP) email: alex@rc. unesp. br Blue Gene/Q

2 Roteiro • Parte 1 • Programação paralela na atualidade • Transações: uma estratégia

2 Roteiro • Parte 1 • Programação paralela na atualidade • Transações: uma estratégia otimista • Modelo de execução • Parte 2 • Arquitetura básica de uma STM • Exemplo usando o GCC • Arquitetura básica de uma HTM • IBM Blue. Gene Q

PARTE 1 Introdução e Conceitos Básicos

PARTE 1 Introdução e Conceitos Básicos

Sistema de memória compartilhada c 0 c 1 temp c 2 c 3 temp

Sistema de memória compartilhada c 0 c 1 temp c 2 c 3 temp counter store (counter, temp) load (temp, counter) • Usado como modelo de execução de Pthreads e Open. MP

5 Memória Compatilhada • Processo cria várias threads de execução • Threads compartilham espaço

5 Memória Compatilhada • Processo cria várias threads de execução • Threads compartilham espaço de endereçamento • Comunicação é feita diretamente através de leituras e escritas em memória compartilhada • Acessos concorrentes de leitura e escrita podem causar inconsistências (condições de corrida) • Sincronização é usada para evitar tais cenários

6 Sincronização • Evitar intercalações inconsistentes de execução shared counter; void work() { counter++;

6 Sincronização • Evitar intercalações inconsistentes de execução shared counter; void work() { counter++; } Qual o resultado esperado após a primeira execução?

7 Sincronização • Evitar intercalações inconsistentes de execução shared counter; void work() { counter++;

7 Sincronização • Evitar intercalações inconsistentes de execução shared counter; void work() { counter++; } i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp);

8 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

8 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp);

9 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

9 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp);

10 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

10 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp); O que ocorreu aqui?

11 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

11 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp); Qual o valor lido?

12 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

12 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp);

13 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

13 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp);

14 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

14 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp);

15 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++)

15 Sincronização • Evitar intercalações inconsistentes de execução t 1 (counter++) t 2 (counter++) i 1: temp = load(counter); i 2: temp = temp + 1; i 3: store(counter, temp); Está correto?

16 Mecanismos de Sincronização • Bloqueantes • travas (locks) • variáveis de condição (condition

16 Mecanismos de Sincronização • Bloqueantes • travas (locks) • variáveis de condição (condition variables) • semáforos/monitores • Não-bloqueantes • livre de espera (wait-free) • livre de trava (lock-free) • livre de obstrução (obstruction-free)

17 Exemplo • Considere o seguinte problema: implementar uma lista de inteiros ordenados em

17 Exemplo • Considere o seguinte problema: implementar uma lista de inteiros ordenados em ordem crescente, admitindo operações como inserção, remoção e consulta • Esta tarefa pode ser desenvolvida facilmente por alunos de disciplinas de introdução à computação • Desejamos uma versão paralela, que permita operações concorrentes na lista.

18 Lista ordenada – sequencial class Node { int key; Node next; } class

18 Lista ordenada – sequencial class Node { int key; Node next; } class List { private Node head; public List() { head = new Node(Integer. MIN_VALUE); head. next = new Node(Integer. MAX_VALUE); } }

19 Lista ordenada – sequencial a head c tail public boolean add(int item) {

19 Lista ordenada – sequencial a head c tail public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; }

20 Lista ordenada – sequencial pred curr a head c tail public boolean add(int

20 Lista ordenada – sequencial pred curr a head c tail public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } add(b)

21 Lista ordenada – sequencial pred a head curr c tail public boolean add(int

21 Lista ordenada – sequencial pred a head curr c tail public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } add(b)

22 Lista ordenada – sequencial pred curr a head c b tail public boolean

22 Lista ordenada – sequencial pred curr a head c b tail public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } add(b)

23 Lista ordenada – sequencial pred curr a head c b tail public boolean

23 Lista ordenada – sequencial pred curr a head c b tail public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } add(b)

24 Lista ordenada – sequencial pred curr a head c b tail public boolean

24 Lista ordenada – sequencial pred curr a head c b tail public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } add(b)

25 Lista ordenada – sequencial pred curr a head pred c b a tail

25 Lista ordenada – sequencial pred curr a head pred c b a tail public boolean add(int item) { Node pred, curr; b c tail head remove(b) pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } curr public boolean remove(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item == curr. key) { pred. next = curr. next; return true; } else return false; }

26 Paralelizando • Como desenvolver uma versão paralela do exemplo anterior? • Sendo otimista

26 Paralelizando • Como desenvolver uma versão paralela do exemplo anterior? • Sendo otimista • Operações de inserção, remoção e busca podem “potencialmente” ser executadas em paralelo Thread 1 remove(d) Thread 2 search(b) a head b c d f tail

27 Paralelizando • Como desenvolver uma versão paralela do exemplo anterior? • Sendo otimista

27 Paralelizando • Como desenvolver uma versão paralela do exemplo anterior? • Sendo otimista • Operações de inserção, remoção e busca podem “potencialmente” ser executadas em paralelo • Mais seguro • Ser pessimista e assumir que sempre haverá conflitos • Lock global

28 Lista ordenada – lock global public boolean add(int item) { Node pred, curr;

28 Lista ordenada – lock global public boolean add(int item) { Node pred, curr; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; }

29 Lista ordenada – lock global public boolean add(int item) { Node pred, curr;

29 Lista ordenada – lock global public boolean add(int item) { Node pred, curr; boolean valid = false; lock(); pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; valid = true; } lock. unlock(); return valid; pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; return true; } else return false; } }

30 Lista ordenada – lock global • Ideia do lock global • Antes de

30 Lista ordenada – lock global • Ideia do lock global • Antes de iniciar o trecho de código que altera a lista, adquirir a trava (lock) • Após trecho de código, liberar a trava (unlock) • Funciona? • Solução simples, mas não escala! • Operações são serializadas

31 Serializando public boolean add(int item) { Node pred, curr; boolean valid = false;

31 Serializando public boolean add(int item) { Node pred, curr; boolean valid = false; lock(); pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; valid = true; } lock. unlock(); return valid; • Como melhorar a solução? • Sugestões? public boolean add(int item) { Node pred, curr; boolean valid = false; lock(); pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; valid = true; } lock. unlock(); return valid; } }

32 Lista ordenada – locks finos • Ideia • Associar um lock a cada

32 Lista ordenada – locks finos • Ideia • Associar um lock a cada nó da lista • Antes do conteúdo do nó ser acessado, adquirimos seu respectivo lock (liberando-o após o acesso) • Essa abordagem funciona? • Considere duas operações concorrentes para remoção dos itens ‘b’ e ‘a’, por duas threads distintas (T 1 e T 2)

33 Lista ordenada – locks finos a head T 1 remove(b). . . head.

33 Lista ordenada – locks finos a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail

34 Lista ordenada – locks finos pred curr a head T 1 remove(b). .

34 Lista ordenada – locks finos pred curr a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail

35 Lista ordenada – locks finos pred curr a head T 1 remove(b). .

35 Lista ordenada – locks finos pred curr a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail

36 Lista ordenada – locks finos curr pred a head T 1 remove(b). .

36 Lista ordenada – locks finos curr pred a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail

37 Lista ordenada – locks finos curr pred a b c head T 1

37 Lista ordenada – locks finos curr pred a b c head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } tail Assuma um “page fault” aqui!!

38 Lista ordenada – locks finos curr pred a head T 1 remove(b). .

38 Lista ordenada – locks finos curr pred a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail remove(a). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; T 2 pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; }

39 Lista ordenada – locks finos pred curr a head T 1 remove(b). .

39 Lista ordenada – locks finos pred curr a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail remove(a). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; T 2 pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; }

40 Lista ordenada – locks finos pred curr a b head T 1 remove(b).

40 Lista ordenada – locks finos pred curr a b head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } Thread azul volta pred. unlock(); return valid; } c tail remove(a). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; T 2 pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; }

41 Lista ordenada – locks finos pred curr a b head T 1 remove(b).

41 Lista ordenada – locks finos pred curr a b head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; Resultado? } c tail remove(a). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; T 2 pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; }

42 Lista ordenada – locks finos “a” ainda ficou!! a head T 1 remove(b).

42 Lista ordenada – locks finos “a” ainda ficou!! a head T 1 remove(b). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; } b c tail remove(a). . . head. lock(); pred = head; curr = pred. next; while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; T 2 pred. lock(); } if (item == curr. key) { pred. next = curr. next; valid = true; } pred. unlock(); return valid; }

43 Lista ordenada – locks finos • Antes de alterar um nó, uma thread

43 Lista ordenada – locks finos • Antes de alterar um nó, uma thread necessita adquirir as travas para o nó atual e o próximo • Note que as threads envolvidas precisam adquirir os locks na mesma ordem para evitar o risco de deadlock • Não é trivial provar a corretude! • Exemplo de código para a operação de inserção. . .

44 Lista ordenada – locks finos public boolean add(int item) { boolean valid =

44 Lista ordenada – locks finos public boolean add(int item) { boolean valid = false; head. lock(); Node pred = head; Node curr = pred. next; curr. lock(); while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; curr. lock(); } if (item != curr. key) { Node new. Node = new Node(item); new. Node. next = curr; pred. next = new. Node; valid = true; } curr. unlock(); pred. unlock(); return valid; }

45 Lista ordenada – locks finos public boolean add(int item) { boolean valid =

45 Lista ordenada – locks finos public boolean add(int item) { boolean valid = false; head. lock(); Node pred = head; Node curr = pred. next; curr. lock(); while (curr. key < item) { pred. unlock(); pred = curr; curr = curr. next; curr. lock(); } if (item != curr. key) { Node new. Node = new Node(item); new. Node. next = curr; pred. next = new. Node; valid = true; } curr. unlock(); pred. unlock(); return valid; } Grande parte do código é específico para sincronização (6 de 18 linhas = ~33%)

46 Problemas com lock finos • Risco alto de deadlock • Diferentes locks adquiridos

46 Problemas com lock finos • Risco alto de deadlock • Diferentes locks adquiridos em diferentes ordens • Operações lock e unlock custosas • Geralmente envolvem alguma forma de syscall • Dificuldade relacionada a engenharia de software • Como encapsular um método com locks? • Como compor código?

47 Composição de código com locks • Imagine que nossa aplicação precise utilizar as

47 Composição de código com locks • Imagine que nossa aplicação precise utilizar as operações da lista ligada para implementar uma outra operação de nível mais alto, como mover um elemento de uma lista para outra • Não temos acesso ao código fonte • Apenas sabemos que cada operação é atômica public boolean move(List new, List old, int item) { old. remove(item); new. add(item); }

48 Composição de código com locks • Imagine que nossa aplicação precise utilizar as

48 Composição de código com locks • Imagine que nossa aplicação precise utilizar as operações da lista ligada para implementar uma outra operação de nível mais alto, como mover um elemento de uma lista para outra • Não temos acesso ao código fonte • Apenas sabemos que cada operação é atômica public boolean move(List new, List old, int item) { old. remove(item); new. add(item); } Atômico?

49 Composição de código com locks • Colocar um lock global? public boolean move(List

49 Composição de código com locks • Colocar um lock global? public boolean move(List from, List to, int item) { newlock(); from. remove(item); E se ocorrer busca(item, from) to. add(item); thread neste ponto? newlock. unlock(); } Funciona? em outra

50 Composição de código com locks • Colocar um lock global? public boolean move(List

50 Composição de código com locks • Colocar um lock global? public boolean move(List from, List to, int item) { newlock(); from. remove(item); to. add(item); newlock. unlock(); } • Esta solução requer que todas as operações atômicas da lista sejam envoltas pelo novo lock • Uma solução alternativa seria quebrar o encapsulamento e expor a implementação da lista (quais locks foram usados)

51 Composição de código com locks • Cada lista expõe seu lock global public

51 Composição de código com locks • Cada lista expõe seu lock global public boolean move(List from, List to, int item) { from. lock(); to. lock(); from. remove(item); to. add(item); from. unlock(); to. unlock(); } • Esta solução funciona?

52 Composição de código com locks • Cada lista expõe seu lock global public

52 Composição de código com locks • Cada lista expõe seu lock global public boolean move(List from, List to, int item) { from. lock(); to. lock(); from. remove(item); to. add(item); from. unlock(); to. unlock(); } • Esta solução funciona? Thread 1 move(lista_clientes, lista_devedores, USER 1); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock(); Thread 2 move(lista_devedores, lista_clientes, USER 2); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock();

53 Composição de código com locks • Cada lista expõe seu lock global public

53 Composição de código com locks • Cada lista expõe seu lock global public boolean move(List from, List to, int item) { from. lock(); to. lock(); from. remove(item); to. add(item); from. unlock(); to. unlock(); } • Esta solução funciona? Thread 1 move(lista_clientes, lista_devedores, USER 1); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock(); Thread 2 move(lista_devedores, lista_clientes, USER 2); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock();

54 Composição de código com locks • Cada lista expõe seu lock global public

54 Composição de código com locks • Cada lista expõe seu lock global public boolean move(List from, List to, int item) { from. lock(); to. lock(); from. remove(item); to. add(item); from. unlock(); to. unlock(); } • Esta solução funciona? Thread 1 move(lista_clientes, lista_devedores, USER 1); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock(); Thread 2 move(lista_devedores, lista_clientes, USER 2); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock();

55 Composição de código com locks • Cada lista expõe seu lock global public

55 Composição de código com locks • Cada lista expõe seu lock global public boolean move(List from, List to, int item) { from. lock(); to. lock(); from. remove(item); to. add(item); from. unlock(); to. unlock(); } • Esta solução funciona? Thread 1 move(lista_clientes, lista_devedores, USER 1); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock(); Thread 2 move(lista_devedores, lista_clientes, USER 2); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock();

56 Composição de código com locks • Cada lista expõe seu lock global public

56 Composição de código com locks • Cada lista expõe seu lock global public boolean move(List from, List to, int item) { from. lock(); to. lock(); from. remove(item); to. add(item); from. unlock(); to. unlock(); } • Esta solução funciona? Thread 1 Thread 2 move(lista_clientes, lista_devedores, USER 1); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock(); DEADLOCK move(lista_devedores, lista_clientes, USER 2); from. lock(); to. lock(); from. remove(USER 1); to. add(USER 1); from. unlock(); to. unlock();

57 Desempenho Programação concorrente Lock global Complexidade

57 Desempenho Programação concorrente Lock global Complexidade

58 Programação concorrente Desempenho Lock-free Locks finos Lock global Complexidade

58 Programação concorrente Desempenho Lock-free Locks finos Lock global Complexidade

59 Programação concorrente Desempenho HTM Lock-free Locks finos TM STM Lock global Complexidade

59 Programação concorrente Desempenho HTM Lock-free Locks finos TM STM Lock global Complexidade

60 Memória Transacional (TM) X 1 i 1: temp 1 = load(counter); i 2:

60 Memória Transacional (TM) X 1 i 1: temp 1 = load(counter); i 2: temp 1 = temp 1 + 1; X 2 RAW i 1: temp 2 = temp 1; i 2: temp 2 = temp 2 + 1; i 3: store(counter, temp 1); 1 antes de 2 SUCESSO!! i 3: store(counter, temp 2);

61 Memória Transacional (TM) X 1 i 1: temp 1 = load(counter); i 2:

61 Memória Transacional (TM) X 1 i 1: temp 1 = load(counter); i 2: temp 1 = temp 1 + 1; X 2 RAW i 1: temp 2 = temp 1; i 2: temp 2 = temp 2 + 1; i 3: store(counter, temp 2); 2 antes de 1 i 3: store(counter, temp 1); ABORTA!!

62 Memória Transacional (TM) • No modelo transacional, programadores usam o conceito de transação

62 Memória Transacional (TM) • No modelo transacional, programadores usam o conceito de transação como abstração • Atomicidade • Consistência • Isolamento • Vantagens • Nível de abstração maior • Potencial ganho de desempenho • Dependente de implementação (visto mais adiante) • Composição de código

63 Memória Transacional (TM) • No modelo transacional, programadores usam o conceito de transação

63 Memória Transacional (TM) • No modelo transacional, programadores usam o conceito de transação como abstração • Atomicidade • Consistência • Isolamento Detalhes de como realizar a sincronização são movidos do programador para o sistema de execução • Vantagens • Nível de abstração maior • Potencial ganho de desempenho • Dependente de implementação (visto mais adiante) • Composição de código

64 Programando com TM • Programador delimita a public boolean add(int item) { Node

64 Programando com TM • Programador delimita a public boolean add(int item) { Node pred, curr; boolean valid = false; região que deve ser executada atomicamente atomic { pred = head; curr = pred. next; while (curr. key < item) { pred = curr; curr = curr. next; } if (item != curr. key) { Node node = new Node(item); node. next = curr; pred. next = node; valid = true; } } if (valid) return true; return false; • Exemplo com lista ligada • Sistema de execução (pode ser hardware ou software) cuida de garantir atomicidade, isolamento e consistência }

65 TM e composição de código • O modelo transacional permite composição de código

65 TM e composição de código • O modelo transacional permite composição de código de forma natural • Aninhamento de transações atomic { … } … }

66 TM e composição de código • O modelo transacional permite composição de código

66 TM e composição de código • O modelo transacional permite composição de código de forma natural • Aninhamento de transações atomic { … } … } • Exemplo anterior para mover elementos entre listas public boolean move(List new, List old, int item) { atomic { old. remove(item); new. add(item); } }

67 Bom para quê? • Estruturas de dados cuja escalabilidade é ruim com abordagens

67 Bom para quê? • Estruturas de dados cuja escalabilidade é ruim com abordagens baseadas em locks • Exemplo: árvore rubro-negra • Aplicações nas quais o uso de lock é muito conservativo • Aplicações irregulares (uso extensivo de ponteiros) • Algoritmos de grafos • Exemplo de sistema grande de porte • Servidor do jogo Quake (Zyulkyarov et al. )

68 Implementação • O mecanismo transacional precisa fornecer… • Versionamento de dados • Os

68 Implementação • O mecanismo transacional precisa fornecer… • Versionamento de dados • Os dados temporários (especulativos) usados pela transação precisam ser mantidos em algum local • Essencial para garantiar atomicidade e consistência • Isolamento da execução • É necessário um mecanismo para detectar e resolver os conflitos entre transações

69 Versionamento de dados • Imediato (eager/pessimistic/direct) • Memória compartilhada atualizada imediatamente (valor antigo

69 Versionamento de dados • Imediato (eager/pessimistic/direct) • Memória compartilhada atualizada imediatamente (valor antigo armazenado em buffer) • Efetivação rápida, mas cancelamento lento • Deferido (lazy/optimistic/deferred) • Armazena atualização em buffer interno • Cancelamento rápido, mas efetivação lenta

70 Versionamento imediato

70 Versionamento imediato

71 Versionamento imediato

71 Versionamento imediato

72 Versionamento imediato

72 Versionamento imediato

73 Versionamento imediato

73 Versionamento imediato

74 Versionamento deferido

74 Versionamento deferido

75 Versionamento deferido

75 Versionamento deferido

76 Versionamento deferido

76 Versionamento deferido

77 Versionamento deferido

77 Versionamento deferido

78 Isolamento da execução • Detecção de conflitos • Usa-se dois conjuntos: • Read

78 Isolamento da execução • Detecção de conflitos • Usa-se dois conjuntos: • Read set: dados lidos • Write set: dados escritos • Conflito se há intersecção entre o conjunto de leitura e escrita de transações diferentes • Resolução de conflitos • Depende do gerenciador de contenção • Exemplo: abortar imediatamente, esperar, . . . • Importante para garantir progresso

79 Formas de detecção de conflitos • Adiantado (eager/pessimistic/encounter-time) • Ocorre no momento dos

79 Formas de detecção de conflitos • Adiantado (eager/pessimistic/encounter-time) • Ocorre no momento dos acessos ao dados • Pode evitar executar código desnecessário • Mais suscetível a livelock • Tardio (lazy/optimistic/commit-time) • Ocorre na efetivação da transação • Potencialmente menos conflitos • Menos suscetível a livelock, porém starvation pode ser um problema

80 Detecção de conflitos adiantado

80 Detecção de conflitos adiantado

81 Detecção de conflitos adiantado

81 Detecção de conflitos adiantado

82 Detecção de conflitos adiantado

82 Detecção de conflitos adiantado

83 Detecção de conflitos adiantado

83 Detecção de conflitos adiantado

84 Detecção de conflitos tardio

84 Detecção de conflitos tardio

85 Detecção de conflitos tardio

85 Detecção de conflitos tardio

86 Detecção de conflitos tardio

86 Detecção de conflitos tardio

87 Detecção de conflitos tardio

87 Detecção de conflitos tardio

PARTE 2 Implementação em STM e HTM

PARTE 2 Implementação em STM e HTM

89 Implementação de TM • O suporte transacional pode ser realizado em hardware, software

89 Implementação de TM • O suporte transacional pode ser realizado em hardware, software ou uma mescla de ambos (híbrido) • Hardware (HTM) • Melhor desempenho • Problemas com virtualização (espaço, tempo) • Software (STM) • Desempenho depende muito da aplicação • Extremamente flexível • Ideal para testar novas ideias

90 STM – Suporte • Interface • API básica • start, commit, barreiras de

90 STM – Suporte • Interface • API básica • start, commit, barreiras de leitura e escrita • Object vs word • Duas formas principais de implementação • Non-blocking • Blocking • As implementações possuem diferentes garantias de progresso e consistência

91 STM – Shavit & Touitou (1995) PO 5 ’ 9 C D

91 STM – Shavit & Touitou (1995) PO 5 ’ 9 C D

92 STM – Shavit & Touitou (1995) • Cunhou o termo “software transactional memory”

92 STM – Shavit & Touitou (1995) • Cunhou o termo “software transactional memory” (STM) • Transações eram “estáticas” • Uma transação precisa especificar, de forma antecipada, um vetor com as posições de memória que serão acessadas • Basicamente um CAS múltiplo • A implementação, non-blocking, influenciou as novas propostas que surgiriam em seguida

93 DSTM – Herlihy et al. (2003) PO ’ 03 C D

93 DSTM – Herlihy et al. (2003) PO ’ 03 C D

94 DSTM – Herlihy et al. (2003) • Definitivamente desencadeou a pesquisa em STM

94 DSTM – Herlihy et al. (2003) • Definitivamente desencadeou a pesquisa em STM (junto com Harris & Fraser – OOPSLA 2003) • Granularidade: objetos • Detecção de conflitos adiantada/versionamento deferido • Implementação é obstruction-free • Gerenciador de contenção é responsável por garantir progresso

95 Rob Ennals (2005, 2006) • Motivado pelo baixo desempenho de implementações non -blocking,

95 Rob Ennals (2005, 2006) • Motivado pelo baixo desempenho de implementações non -blocking, Rob Ennals propôs implementar sistemas STM baseados em locks • Efficient Software Transactional Memory (2005) • “This paper described how software transactional memory could be made more efficient if one was prepared to sacrifice non-blocking properties. It was rejected from SPAA, but was widely circulated and was fairly influential on the design of subsequent STM implementations. ” • Software Transactional Memory Should Not Be Obstruction-Free (2006) • “This paper was submitted to SCOOL 2005, but was deemed to be "too controversial for publication" and so was instead made the topic of a panel instead. ”

96 Lock-based STMs • A ideia de Ennals gerou bastante controvérsia • Até então

96 Lock-based STMs • A ideia de Ennals gerou bastante controvérsia • Até então acreditava-se que as implementações deveriam ser nonblocking por questões de garantia de progresso • Ennals argumentou que o sistema runtime de linguagens modernas podem controlar o escolamento das threads, evitando o problema de preempção • As principais implementações (mais rápidas) de STM atuais são blocking

97 TL 2 – Dice et al. (2006) 6 0 ’ C DIS

97 TL 2 – Dice et al. (2006) 6 0 ’ C DIS

98 TL 2 – Dice et al. (2006) • Principal representante de uma classe

98 TL 2 – Dice et al. (2006) • Principal representante de uma classe de implementações que adotam locks • Granularidade: principalmente palavras • Locks para escrita adquiridos durante fase de commit • Versionamento deferido • Relógio global é usado para manter consistência

99 TL 2 – Interface • API • Start. Tx(), Commit. Tx(), Abort. Tx()

99 TL 2 – Interface • API • Start. Tx(), Commit. Tx(), Abort. Tx() • Read. Tx(), Write. Tx() • Exemplo: void Push. Left(DQueue *q, int val) { QNode *qn = malloc(sizeof(QNode)); qn->val = val; do { Start. Tx(); QNode *left. Sentinel = Read. Tx(&(q->left)); . . . Write. Tx(&(old. Left. Node->left), qn); } while (!Commit. Tx()); }

100 TL 2 – Interface • API Lembrando que • Start. Tx(), Commit. Tx(),

100 TL 2 – Interface • API Lembrando que • Start. Tx(), Commit. Tx(), Abort. Tx() potencialmente o compilador • Read. Tx(), Write. Tx() gera esse código a partir de blocos atômicos • Exemplo: void Push. Left(DQueue *q, int val) { QNode *qn = malloc(sizeof(QNode)); qn->val = val; do { Start. Tx(); QNode *left. Sentinel = Read. Tx(&(q->left)); . . . Write. Tx(&(old. Left. Node->left), qn); } while (!Commit. Tx()); }

101 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK .

101 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK . . . versioned locks

102 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK .

102 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK . . . Sempre incrementado (+2) quando uma transação é efetivada versioned locks

103 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK .

103 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK . . . Toda posição de memória acessada transacionalmente é mapeada para um registro (versioned lock) nessa tabela através de uma função hash versioned locks

104 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK .

104 TL 2 – Metadados Compartilhado Global Clock Ownership Record Table (ORT) GCLOCK . . . versão da palavra (se lock bit ==0) ou endereço da transação que travou registro lock bit versioned locks

105 TL 2 – Metadados Privado descritor transação (txdsc) Ativa, Abortada, Efetivada status versão

105 TL 2 – Metadados Privado descritor transação (txdsc) Ativa, Abortada, Efetivada status versão conjunto de leitura (cjt. Leitura) conjunto de escrita (cjt. Escrita) endereço valor endereço

106 TL 2 – Funcionamento GCLOCK descritor transação (txdsc) status versão conjunto de leitura

106 TL 2 – Funcionamento GCLOCK descritor transação (txdsc) status versão conjunto de leitura (cjt. Leitura) conjunto de escrita (cjt. Escrita) endereço valor Start. Tx() 1. txdsc. versao <- GCLOCK 2. txdsc. status <- ACTIVE endereço

107 TL 2 – Funcionamento GCLOCK descritor transação (txdsc) status versão conjunto de leitura

107 TL 2 – Funcionamento GCLOCK descritor transação (txdsc) status versão conjunto de leitura (cjt. Leitura) conjunto de escrita (cjt. Escrita) endereço valor endereço Write. Tx(endereco, valor) 1. cjt. Escrita. insere(endereco, valor)

108 TL 2 – Funcionamento ORT GCLOCK ORT-address = hash(endereço) . . . descritor

108 TL 2 – Funcionamento ORT GCLOCK ORT-address = hash(endereço) . . . descritor transação (txdsc) status versão conjunto de leitura (cjt. Leitura) conjunto de escrita (cjt. Escrita) endereço valor endereço Read. Tx(endereco, valor) 1. 2. 3. 4. 5. 6. 7. 8. se (endereco em cjt. Escrita) retorna cjt. Escrita[endereco]. valor v 1 = ORT[hash(endereco)] valor = memoria[endereco] v 2 = ORT[hash(endereco)] se (v 1. lock travado || v 1 != v 2 || v 1. versao > txdsc. versao) aborta transacao cjt. Leitura. insere(endereco) retorna valor

109 TL 2 – Funcionamento ORT GCLOCK ORT-address = hash(endereço) descritor transação (txdsc) status

109 TL 2 – Funcionamento ORT GCLOCK ORT-address = hash(endereço) descritor transação (txdsc) status versão Commit. Tx(endereco, valor) 1. trava elementos em Cjt. Escrita 2. incrementa GCLOCK (+2) 3. valida Cjt. Leitura 4. atualiza memória com valores no Cjt. Escrita 5. endereço destrava Cjt. Escrita e atualiza versao na ORT . . . conjunto de leitura (cjt. Leitura) conjunto de escrita (cjt. Escrita) endereço valor Memória Compartilhada

110 TL 2 – Características • Contenção em GCLOCK pode ser tornar crítico para

110 TL 2 – Características • Contenção em GCLOCK pode ser tornar crítico para um número muito grande de transações concorrentes • Implementação cuidadosa • Deve evitar deadlock • Commit-time locking (CTL) com versionamento tardio • Conflitos write-after-write (WAW) e read-after-write (RAW) detectados de forma tardia

111 Novas lock-based STMs • A maioria das STMs que surgiram depois usam o

111 Novas lock-based STMs • A maioria das STMs que surgiram depois usam o mesmo conceito de relógio global para garantir consistências (time-based STMs) • Principais variações • Encounter-time locking (ETL) com versionamento tardio ou adiantado, com extensão de versões • Tipos de conflitos (read-after-write, write-after-write) tratados de formas diferentes • Exemplos de STMs da mesma classe • Tiny. STM, Swis. STM

112 Críticas sobre STMs AC. m 8 m Co ue’ 0 e Qu 8

112 Críticas sobre STMs AC. m 8 m Co ue’ 0 e Qu 8 0 ’ M

113 Research toy

113 Research toy

114 STM strikes back m m o C ’ 11 M. AC

114 STM strikes back m m o C ’ 11 M. AC

115 STM strikes back ’ 11 M. AC m m o C Autores argumentam

115 STM strikes back ’ 11 M. AC m m o C Autores argumentam que artigo anterior usou conjunto de aplicações/hardware inadequado

116 STM strikes back

116 STM strikes back

117 Suporte transacional no GCC 4. 7 • Suporte experimental a TM existe no

117 Suporte transacional no GCC 4. 7 • Suporte experimental a TM existe no GCC a partir da versão 4. 7 (abril de 2012) • As construções adicionadas à linguagem são baseadas no documento “Draft Specification of Transactional Language Constructs for C++”, versão 1. 1 • http: //gcc. gnu. org/wiki/Transactional. Memory “The support is experimental. In particular, this also means that several parts of the implementation are not yet optimized. If you observe performance that is lower than expected, you should not assume that transactional memory is inherently slow; instead, please just file a bug. ”

118 Construções GCC • Principais construções • __transaction_atomic {…} • __transaction_relaxed {…} • __transaction_cancel

118 Construções GCC • Principais construções • __transaction_atomic {…} • __transaction_relaxed {…} • __transaction_cancel • Anotações • __attribute__((transaction_safe)) • __attribute__((transaction_pure)) • Opção -fgnu-tm deve ser passada ao compilador

119 Exemplo com GCC – lista ligada int list_add(list_node_t *head, int item) { list_node_t

119 Exemplo com GCC – lista ligada int list_add(list_node_t *head, int item) { list_node_t *pred, *curr; __transaction_atomic { pred = head; curr = head->next; while (curr->key < item) { pred = curr; curr = curr->next; } list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t)); node->key = item; node->next = curr; pred->next = node; } return 1; } Demais operações implementadas da mesma forma

120 Teste de “stress” da lista void *list_exercise(void *arg) { int operations = (int)arg;

120 Teste de “stress” da lista void *list_exercise(void *arg) { int operations = (int)arg; Todas as threads executam a mesma rotina int add_or_remove, chance, value, last_value = 0; add_or_remove = 1; /* 1 - add, 0 - remove */ while (operations--) { chance = (int)(erand 48(seed)*100); value = (int)(erand 48(seed)*RANGE*2); if (chance <= UPD_RATE) { if (add_or_remove) { list_add(linked_list, value); last_value = value; } else list_remove(linked_list, last_value); add_or_remove ^= 1; } else list_contain(linked_list, value); }

121 Resultados “informais” • Máquina: AMD Opteron • Taxa de atualização (UPD_RATE): 20% •

121 Resultados “informais” • Máquina: AMD Opteron • Taxa de atualização (UPD_RATE): 20% • Tamanho do conjunto: 100 elementos 18 16 14 Tempo (s) 12 10 8 6 4 2 0 1 2 4 Threads 8

122 Compondo código usando TM • Movendo elemento de uma lista para outra void

122 Compondo código usando TM • Movendo elemento de uma lista para outra void move(list_node_t *from, list_node_t *to, int val) { __transaction_atomic { if (!list_remove(from, val)) __transaction_cancel; list_add(to, val); } } • Se list_remove e list_add forem definidos em outro arquivo, é necessário usar transaction_safe __attribute__((transaction_safe)) int list_add(list_node_t *head, int item);

123 Detalhes da geração e execução • O compilador gera duas versões para cada

123 Detalhes da geração e execução • O compilador gera duas versões para cada rotina especificada com o atributo transaction_safe • A versão transacional é usada quando a rotina é chamada dentro de uma transação • O código gerado é linkado com uma biblioteca de runtime chamada libitm • Essa biblioteca pode ser substituída em tempo de execução • Permite que diferentes implementações possam ser avaliadas de forma simples • Especificação segue basicamente a ABI proposta pela Intel • Intel Transactional Memory Compiler and Runtime Application Binary Interface, revisão 1. 1 – maio de 2009

124 HTM – Suporte • Interface • Conjunto de instruções do processador • Exemplo:

124 HTM – Suporte • Interface • Conjunto de instruções do processador • Exemplo: Intel TSX • Versionamento • Cache ou buffer de escrita • Conflitos • Protocolo de coerência de cache (snoop ou diretório) • R/W bits adicionados à cache

125 HTM – Exemplo Execução Versionamento e detecção de conflitos atrasados

125 HTM – Exemplo Execução Versionamento e detecção de conflitos atrasados

126 HTM – Exemplo Execução

126 HTM – Exemplo Execução

127 HTM – Exemplo Execução

127 HTM – Exemplo Execução

128 HTM – Exemplo Execução

128 HTM – Exemplo Execução

129 HTM – Exemplo Execução

129 HTM – Exemplo Execução

130 HTM – Herlihy & Moss (1993) 3 ’ 9 A ISC

130 HTM – Herlihy & Moss (1993) 3 ’ 9 A ISC

131 HTM – Herlihy & Moss (1993) • Novas instruções (6) • ST, LTX

131 HTM – Herlihy & Moss (1993) • Novas instruções (6) • ST, LTX • COMMIT, VALIDATE, ABORT • Cache transacional separada • Mantém os conjuntos de leitura e escrita da transação • Protocolo de coerência • Baseado em snoop

132 Problemas com a abordagem • Cache adicional (em paralelo com as de dado

132 Problemas com a abordagem • Cache adicional (em paralelo com as de dado e instruções) complica bastante o projeto de processadores modernos • Estender cache de dados tornou-se mais popular (1 bit extra para read, e outro para escrita especulativa) • Nenhuma virtualização • E se a cache transbordar? • Uso ainda requer programador experiente

133 Novos HTMs • 2009 - Sun’s Rock • 2009 – AMD Advanced Synchronization

133 Novos HTMs • 2009 - Sun’s Rock • 2009 – AMD Advanced Synchronization Facility (ASF) • 2011 - IBM Blue. Gene/Q Nosso foco • 2013 – Intel Transactional Synchronization Extensions (TSX) Processador acaba de ser lançado (Haswell)

134 IBM Blue. Gene/Q • Revelado no Hot Chips 2011 • 18 cores @1.

134 IBM Blue. Gene/Q • Revelado no Hot Chips 2011 • 18 cores @1. 6 GHz • 16 cores para aplicações, 1 para SO, 1 aumentar yield • Usados no supercomputador Sequoia • Each core • 1. 47 Billion transistors • 55 Watts

135 Blue Gene/Q packaging hierarchy 2. Module Single Chip 3. Compute Card One single

135 Blue Gene/Q packaging hierarchy 2. Module Single Chip 3. Compute Card One single chip module, 16 GB DDR 3 Memory 4. Node Card 32 Compute Cards, Optical Modules, Link Chips, Torus 1. Chip 16 cores 5 b. I/O Drawer 8 I/O Cards 8 PCIe Gen 2 slots 6. Rack 2 Midplanes 1, 2 or 4 I/O Drawers 7. System 20 PF/s 5 a. Midplane 16 Node Cards Ref: SC 2010 Slide produced by Martin Ohmacht for PACT 2011 Workshop

Suporte Para Operacões Atômicas na L 2 IEEE Micro, March/April 2012

Suporte Para Operacões Atômicas na L 2 IEEE Micro, March/April 2012

Example Thread 0 Thread 1 Thread 63

Example Thread 0 Thread 1 Thread 63

L 2 structure • 32 MB / 16 way set-associative / 128 B por

L 2 structure • 32 MB / 16 way set-associative / 128 B por linha / 256 K linhas na cache • Ponto de coerência • Cada fatia: • 2 MB / 16 K lines ⇒ 1024 sets com 16 linhas/set • 1024 sets Crossbar switch DEVBUS L 2 L 2 L 2_counter L 2 L 2_counter L 2_central memory controller 1 3 L 2 memory controller Slide adapted from Martin Ohmacht’s presentation at Workshop held at PACT 2011

Uma visão simplificada da organização lógica de um slice da L 2 (32 MB)

Uma visão simplificada da organização lógica de um slice da L 2 (32 MB) Speculative Bit Thread 128 B Data Index ID Tag 00… 00 16 K 11… 11 16 ways 16 K linhas x 16 ways x 128 Bytes = 32 M Bytes

Diretório da L 2 Para cada linha escrita especulativamente, o diretório armazena um id

Diretório da L 2 Para cada linha escrita especulativamente, o diretório armazena um id da thread que é a dona. Quando ocorre um acesso a uma linha especulativa, o diretório compara a id da tread fazendo o acesso com a id da dona, e interrompe o thread mais jovem que está em conflicto. Se o conflito é entre uma thread especulativa e um thread não especulativa, a thread especulativa é abortada.

Speedup com relação à execução sequential

Speedup com relação à execução sequential

143 Conclusões • Memória Transacional veio para ficar como um novo paradigma de programação

143 Conclusões • Memória Transacional veio para ficar como um novo paradigma de programação paralela • Duas grandes empresas na área lançaram processadores contendo extensões para TMs (IBM e Intel) • Boas oportunidades para realização de pesquisa! • Se estiver interessado em fazer pesquisa entre em contato!!

OBRIGADO!

OBRIGADO!