Você está na página 1de 3

Segundo Trabalho - Relatório Final

Computação Concorrente (MAB-117) - 2021/1 Remoto

Ana Claudia Ribeiro dos Santos


DRE: 118060382

Rebeca Batista Medeiros da Fonseca


DRE: 114156733

1. Projeto Final e implementação da solução concorrente


A solução pretende seguir os critérios solicitados no exercício. Para isso, criamos um padrão
de threads no estilo produtor/consumidor com uma thread produtora e x threads
consumidores (valor de x variável conforme digitação na linha de comando).
A thread produtora é responsável por pegar as linhas do arquivo em texto e colocar na
memória. Isso é feito e mostrado no código com um loop infinito chamando a função insere
que faz esse mecanismo. A forma de “quebrar” esse loop infinito é criando uma variável
chamada total_numeros que é alimentado no começo do programa main (sabemos esse valor
pois ele é o primeiro número do arquivo de entrada) e decrementada em blocos toda vez que
o produtor insere valores no buffer compartilhado. Quando essa variável chegar a zero
significa que o loop pode ser interrompido, pois já “produziu” tudo que tinha, ou seja, pegou
do arquivo de entrada e colocou no buffer todo o seu conteúdo.
Complementar a esse padrão temos o consumidor que é responsável pegar esse buffer
compartilhado e ordenar ele. Na função da thread consumidora existe um loop infinito que
retira um elemento desse buffer e o processa. A forma de “quebrar” esse loop infinito é
quando essa variável total_numeros chegar a zero, pois como dito ela é alimentada no
começo do programa com o valor de números a ser lido e se chegou a zero significa que não
há números a serem lidos mais, porém só isso não é garantia que o consumidor não precisa
mais consumir, pois esse número pode chegar a zero significando que o produtor produziu
tudo mas não significa que o consumidor fez todo o processamento que precisava. Para isso
temos também a variável count que é responsável por saber se aquele bloco ainda precisa ser
consumido. Se for mais que um, então significa que tem coisas a serem consumidas. Essa
mesma variável (count) é usada no controle de concorrência do produtor/consumidor. Se
count for igual a zero, não tem como o consumidor trabalhar pois não tem nada consumido.
Por outro lado se count for igual ao número total do buffer, significa que o buffer está cheio e
isso impede que o produtor produza. Esse mecanismo está descrito no código.
O consumidor realiza a ordenação daquele bloco específico e depois faz a escrita no arquivo
de saída com uma variável de lock no padrão leitor/escritor. Só existe um escritor e nenhum
leitor, dessa forma basta fazer um lock de que apenas um escritor pode acessar e escrever no
arquivo de saída;
Depois disso se o total_numeros for igual a zero, indica que escrevemos tudo no arquivo de
saída, e com isso podemos liberar as threads.

2. Teste de Corretude
Os testes foram realizados com números diferentes de consumidores e tamanhos de blocos e
arquivos de entrada com dimensões diferentes.
Para verificar se o arquivo estava correto ou não foi criado um arquivo de verificação
(corretude.py) que verificava se cada linha do arquivo de saída está em ordem crescente. Nos
testes que realizamos, não encontramos erros.

3. Avaliação de Desempenho

Configuração da máquina testada:

Sistema operacional: Ubuntu

P = Produtor
C = Consumidor
N = tamanho do bloco

1P + 1C + 50N 1P + 2C + 50N 1P + 3C + 50N 1P + 5C + 50N

Tempo 0.002255 0.002255 0.002255 0.002255


Sequencial

Tempo 0.000216 0.000270 0.000245 0.000203


sequencial fixo

Tempo 0.001267 0.001624 0.002494 0.004243


concorrente

Aceleração 1.52056 1.19 0.823 0.5071

Para o exemplo acima usamos um arquivo com 500 números com 3,6KB.
Para o exemplo abaixo temos um arquivo com 4000 números com 28,5KB.

1P + 1C + 50N 1P + 2C + 50N 1P + 3C + 50N 1P + 5C + 50N

Tempo 0.011269 0.011269 0.011269 0.011269


Sequencial
Tempo 0.000130 0.000151 0.000195 0.000175
sequencial fixo

Tempo 0.004341 0.006791 0.007114 0.010364


concorrente

Aceleração 2.52046 1.62330 1.54179 1.069266

A aceleração é igual a (Tempo Sequencial)/(tempo sequencial fixo + tempo paralelo)


Cada teste foi realizado pelo menos 5 vezes e buscamos o menor valor entre eles.
Nota-se que nem sempre aumentar a quantidade de threads é uma tarefa que melhora o
desempenho, isso pois existe a sobrecarga de tempo já que é necessário um processamento a
mais para criar o mecanismo de lock e compartilhamento entre as threads e a criação das
threads em si.

4. Discussão
Com a tabela acima podemos ver que criar as threads e seu mecanismo podem ser
onerosos e dependendo do caso vale a pena realizar o uso da computação
concorrente. Olhando a tabela que tem 500 caracteres, pode-se ver que quanto mais
threads consumidoras se tem, maior o tempo de sincronização delas e por isso
acaba não valendo muito a pena. Mas em arquivos maiores, ainda se vê uma
melhora no desempenho, principalmente com quantidade de threads menores.
Acreditamos que além do tempo de criação das threads (que é de certa forma,
comum a todas a todos os casos da tabela), existe o tempo de sincronização entre
elas. Ter mais consumidores e um único produtor pode ter uma espera prolongada
pois a thread consumidoras disputam enquanto apenas uma produz.

5. Referências Bibliográficas
http://www.cmaismais.com.br/referencia/cstdio/fgets/ - Funções de escrita de arquivo

Você também pode gostar