• Nenhum resultado encontrado

4.2 Re´ uso de computa¸c˜ ao

4.2.8 Memoizer

Memoizer [53] ´e uma biblioteca Python que utiliza t´ecnicas de re´uso de computa¸c˜ao para obter ganhos de desempenho em aplica¸c˜oes financeiras. Constru¸c˜ao de es- trat´egias de compra e venda para acionistas ´e uma tarefa computacionalmente in- tensa e possui muitas computa¸c˜oes repetidas em si. Esta biblioteca utiliza memo- riza¸c˜ao em arquivos, que consiste em utilizar o nome da fun¸c˜ao e seus argumentos para gerar um c´odigo hash que serve como o nome de um arquivo que cont´em o resultado da fun¸c˜ao. Argumentos destas fun¸c˜oes possuem dados grandes, matrizes e dataframe com ordens de grandeza de gigabytes, por conta disso, ´e necess´ario um esquema de lookup na cache que n˜ao utilize algoritmos de hash complexos, como md5, e que tamb´em n˜ao seja mais custoso do que executar novamente a fun¸c˜ao. O algoritmo hash utilizado pelo Memoizer ´e o xxhash 64

Os autores utilizam uma classe decorator, que possui m´etodos para realizar a memoriza¸c˜ao de fun¸c˜oes. Para ativar a memoriza¸c˜ao dessas fun¸c˜oes pela classe de decorator ´e necess´ario realizar uma anota¸c˜ao do tipo @¡Nome Da Classe imediata- mente antes da assinatura da fun¸c˜ao. Memoizer permite ao programador identificar quais fun¸c˜oes ser˜ao memorizadas e tamb´em automaticamente desativa a memo- riza¸c˜ao de fun¸c˜oes que possuam referˆencia `a palavra rand em seu corpo. Isto ´e feito para eliminar o risco de memorizar fun¸c˜oes n˜ao determin´ısticas que possam alterar a l´ogica da aplica¸c˜ao, caso seus resultados sejam reaproveitados. A Memoizer tamb´em permite que a memoriza¸c˜ao para diferentes fun¸c˜oes sejam parametrizadas a fim de permitir a melhor estrat´egia de re´uso de computa¸c˜ao para aplica¸c˜oes financeiras, e, assim, obter maior desempenho.

Cap´ıtulo 5

Sucuri

Nesta se¸c˜ao apresentamos a biblioteca python Dataflow Sucuri[21]. Sucuri ´e uma biblioteca minimalista que permite ao programador escrever aplica¸c˜oes paralelas utilizando o modelo de programa¸c˜ao Dataflow.

5.1

Arquitetura Sucuri

Nesta se¸c˜ao, descrevemos com mais detalhes o funcionamento da arquitetura da Sucuri.

A vers˜ao da arquitetura da Sucuri utilizada neste trabalho ´e centralizada, ou seja, possui um processo l´ıder que administra os demais processos respons´aveis pela execu¸c˜ao de um grafo Dataflow. Ela ´e composta dos seguintes componentes b´asicos: unidade de casamento, fila de tarefas prontas e workers.

A figura 5.1 apresenta a esquematiza¸c˜ao da arquitetura da Sucuri e a intera¸c˜ao das unidades b´asicas.

A comunica¸c˜ao entre os processos paralelos workers e o escalonador central (pro- cesso l´ıder) ´e realizada atrav´es de troca de mensagens. A troca de mensagens pode ser feita de duas formas: atrav´es de escrita e leitura em uma ´area de mem´oria comum, onde a utiliza¸c˜ao da Sucuri ´e feita com o paradigma de mem´oria comparti- lhada; ou via MPI[54], onde a utiliza¸c˜ao da Sucuri ´e feita com mem´oria distribu´ıda executando em clusters.

Em uma execu¸c˜ao do grafo Dataflow, o escalonador central da Sucuri inicializa os workers paralelos e identifica no grafo Dataflow quais s˜aos os n´os que n˜ao possuem entrada, esses n´os fontes iniciam a execu¸c˜ao. O escalonador cria objetos chamados tasks, tamb´em chamados de tarefas, os quais s˜ao instˆancias da classe Task, a partir destes n´os fontes. As tarefas possuem as seguintes informa¸c˜oes principais: operandos de entrada, no caso de n´os fonte, estes s˜ao nulos, e o identificador do n´o que gerou esta tarefa. Essas tarefas s˜ao armazenadas na fila de tarefa prontas.

Os workers ao serem inicializados enviam uma mensagem para o escalonador central informando que os mesmos est˜ao ociosos e podem consumir tarefas. O esca- lonador, por sua vez, utilizando um canal de comunica¸c˜ao (mem´oria ou MPI), envia a tarefa para o worker ocioso. Este worker ir´a receber a tarefa e comput´a-la. Cada worker possui uma c´opia do grafo Dataflow e utiliza o identificador do n´o contido na tarefa para descobrir qual n´o dever´a ser computado com os operandos de entrada tamb´em contidos no objeto Task. Ap´os a computa¸c˜ao da tarefa pelo worker, os operandos resultantes s˜ao enviados ao escalonador central em forma de mensagem. Al´em dos operandos resultantes, esta mensagem tamb´em cont´em os identificadores dos n´os destinat´arios de cada operando.

O escalonador central ao receber a mensagem de um determinado worker, a encaminha para a unidade de casamento. Esta ir´a propagar os operandos contidos na mensagem recebida `as portas de entrada dos n´os destinat´arios, cujos identificadores tamb´em est˜ao presentes na mensagem recebida. Se todos os operandos de entrada de um determinado n´o estiverem dispon´ıveis, uma tarefa ´e instanciada a partir do n´o, e ´e despachada para a fila de tarefas prontas. Se o grafo em execu¸c˜ao for um DAG Streaming (ver se¸c˜ao 5.4), a tarefa s´o ´e instanciada e despachada a partir do n´o, se todos os operandos de entrada estiverem dispon´ıveis e, al´em disso, estes estiverem associados `a mesma tag.

A tarefa ´e criada utilizando os valores dos operandos e o identificador do n´o, e ´e armazenada na fila de tarefas prontas. Um worker, quando estiver ocioso, solicita uma nova tarefa. O escalonador retira a tarefa da fila de prontos obedecendo um padr˜ao FIFO e a entrega ao worker, e o processo de propaga¸c˜ao de operandos ´e reiniciando.

Figura 5.1: Esquematiza¸c˜ao da arquitetura da Sucuri.

A figura 5.2 apresenta o pipeline da Sucuri para execu¸c˜ao de uma aplica¸c˜ao Data- flow. Os est´agios representados por um retˆangulo executam dentro do escalonador, enquanto que os demais s˜ao executados por workers, podendo estes estarem na

Figura 5.2: Pipeline da Sucuri.

mesma m´aquina onde o escalonador est´a sendo executado ou em m´aquinas externas interligadas por rede `a maquina hospedeira do processo de escalonamento.