1 / 144

Guido Araujo (IC-UNICAMP) email: guido@ic.unicamp.br Alexandro Baldassin (IGCE-UNESP)

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

masato
Download Presentation

Guido Araujo (IC-UNICAMP) email: guido@ic.unicamp.br Alexandro Baldassin (IGCE-UNESP)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ProgramaçãoParalelausandoMemóriasTransacionais: 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 AlexandroBaldassin (IGCE-UNESP) email: alex@rc.unesp.br Blue Gene/Q

  2. Roteiro • Parte 1 • Programaçãoparalelanaatualidade • Transações: umaestratégiaotimista • Modelo de execução • Parte 2 • Arquiteturabásica de uma STM • Exemplousando o GCC • Arquiteturabásica de uma HTM • IBM BlueGene Q

  3. Parte 1 IntroduçãoeConceitosBásicos

  4. Sistema de memóriacompartilhada c0 c1 c2 c3 temp temp counter store (counter,temp) load (temp, counter) • Usadocomo modelo de execução de Pthreads e OpenMP

  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++; } shared counter; void work() { counter++; } Qualoresultadoesperadoapós a primeiraexecução?

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

  8. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp);

  9. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp);

  10. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); O queocorreuaqui?

  11. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); Qualo valor lido?

  12. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp);

  13. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp);

  14. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp);

  15. Sincronização • Evitar intercalações inconsistentes de execução t2 (counter++) t1 (counter++) i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); i1: temp = load(counter); i2: temp = temp + 1; i3:store(counter, temp); Estácorreto?

  16. Mecanismos de Sincronização • Bloqueantes • travas (locks) • variáveis de condição (conditionvariables) • 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 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 List { private Node head; public List() { head = new Node(Integer.MIN_VALUE); head.next = new Node(Integer.MAX_VALUE); } }

  19. Lista ordenada – sequencial head 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; } a c

  20. Lista ordenada – sequencial pred curr head tail add(b) 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; } a c

  21. Lista ordenada – sequencial pred curr head tail add(b) 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; } a c

  22. Lista ordenada – sequencial pred curr head tail add(b) 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; } a c b

  23. Lista ordenada – sequencial pred curr head tail add(b) 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; } a c b

  24. Lista ordenada – sequencial pred curr head tail add(b) 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; } a c b

  25. Lista ordenada – sequencial pred curr pred curr head tail tail head 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; } 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; } remove(b) c a c a b b

  26. Paralelizando • Como desenvolverumaversãoparalela do exemplo anterior? • Sendootimista • Operações de inserção, remoção e buscapodem “potencialmente” serexecutadasemparalelo Thread 1 remove(d) Thread 2 search(b) a b c d f tail head

  27. Paralelizando • Como desenvolverumaversãoparalela do exemplo anterior? • Sendootimista • Operações de inserção, remoção e buscapodem “potencialmente” serexecutadasemparalelo • Maisseguro • Serpessimista e assumirquesemprehaveráconflitos • Lock global

  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; boolean valid = false; lock.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; } 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; }

  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; lock.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.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 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 (T1 e T2)

  33. Lista ordenada – locks finos head tail 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(); returnvalid; } T1 a b c

  34. Lista ordenada – locks finos curr pred head tail 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(); returnvalid; } T1 a b c

  35. Lista ordenada – locks finos curr pred head tail 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(); returnvalid; } T1 a b c

  36. Lista ordenada – locks finos curr pred head tail 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(); returnvalid; } T1 a b c

  37. Lista ordenada – locks finos curr pred head tail 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(); returnvalid; } Assuma um “page fault” aqui!! T1 a b c

  38. Lista ordenada – locks finos curr pred head tail 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(); returnvalid; } remove(a) ... 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(); returnvalid; } T1 T2 a b c

  39. Lista ordenada – locks finos curr pred pred curr head tail 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(); returnvalid; } remove(a) ... 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(); returnvalid; } T1 T2 a b c

  40. Lista ordenada – locks finos curr pred pred curr head tail 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(); returnvalid; } remove(a) ... 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(); returnvalid; } T1 T2 a b c Thread azulvolta

  41. Lista ordenada – locks finos curr pred pred curr head tail 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(); returnvalid; } remove(a) ... 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(); returnvalid; } T1 T2 a b c Resultado?

  42. Lista ordenada – locks finos “a” aindaficou!! head tail 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(); returnvalid; } remove(a) ... 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(); returnvalid; } T1 T2 a b c

  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 = 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 newNode = new Node(item); newNode.next = curr; pred.next = newNode; valid = true; } curr.unlock(); pred.unlock(); return 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 newNode = new Node(item); newNode.next = curr; pred.next = newNode; 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 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 quenossaaplicação precise utilizar as operaçõesda listaligadaparaimplementarumaoutraoperação de nívelmais alto, como mover um elemento de umalistaparaoutra • Nãotemosacessoaocódigofonte • Apenassabemosquecadaoperaçãoéatômica public booleanmove(List new, Listold, int item) { old.remove(item); new.add(item); }

  48. Composição de código com locks • Imagine quenossaaplicação precise utilizar as operaçõesda listaligadaparaimplementarumaoutraoperação de nívelmais alto, como mover um elemento de umalistaparaoutra • Nãotemosacessoaocódigofonte • Apenassabemosquecadaoperaçãoéatômica public booleanmove(List new, Listold, int item) { old.remove(item); new.add(item); } • Atômico?

  49. Composição de código com locks • Colocar um lock global? public booleanmove(Listfrom, Listto, int item) { newlock.lock(); from.remove(item); to.add(item); newlock.unlock(); } E se ocorrerbusca(item,from) emoutra thread nesteponto? Funciona?

  50. Composição de código com locks • Colocar um lock global? • Estasoluçãorequerquetodas as operaçõesatômicas da listasejamenvoltaspelo novo lock • Uma soluçãoalternativaseriaquebrar o encapsulamento e expor a implementação da lista (quais locks foramusados) public booleanmove(Listfrom, Listto, int item) { newlock.lock(); from.remove(item); to.add(item); newlock.unlock(); }

More Related